/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./src/main.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./gen/astnodes.js":
/*!*************************!*\
  !*** ./gen/astnodes.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* File automatically generated by asdl_js.py. */

/* Object that holds all nodes */
Sk.astnodes = {};

/* ----- expr_context ----- */
/** @constructor */
Sk.astnodes.Load = function Load() {}
/** @constructor */
Sk.astnodes.Store = function Store() {}
/** @constructor */
Sk.astnodes.Del = function Del() {}
/** @constructor */
Sk.astnodes.AugLoad = function AugLoad() {}
/** @constructor */
Sk.astnodes.AugStore = function AugStore() {}
/** @constructor */
Sk.astnodes.Param = function Param() {}

/* ----- boolop ----- */
/** @constructor */
Sk.astnodes.And = function And() {}
/** @constructor */
Sk.astnodes.Or = function Or() {}

/* ----- operator ----- */
/** @constructor */
Sk.astnodes.Add = function Add() {}
/** @constructor */
Sk.astnodes.Sub = function Sub() {}
/** @constructor */
Sk.astnodes.Mult = function Mult() {}
/** @constructor */
Sk.astnodes.MatMult = function MatMult() {}
/** @constructor */
Sk.astnodes.Div = function Div() {}
/** @constructor */
Sk.astnodes.Mod = function Mod() {}
/** @constructor */
Sk.astnodes.Pow = function Pow() {}
/** @constructor */
Sk.astnodes.LShift = function LShift() {}
/** @constructor */
Sk.astnodes.RShift = function RShift() {}
/** @constructor */
Sk.astnodes.BitOr = function BitOr() {}
/** @constructor */
Sk.astnodes.BitXor = function BitXor() {}
/** @constructor */
Sk.astnodes.BitAnd = function BitAnd() {}
/** @constructor */
Sk.astnodes.FloorDiv = function FloorDiv() {}

/* ----- unaryop ----- */
/** @constructor */
Sk.astnodes.Invert = function Invert() {}
/** @constructor */
Sk.astnodes.Not = function Not() {}
/** @constructor */
Sk.astnodes.UAdd = function UAdd() {}
/** @constructor */
Sk.astnodes.USub = function USub() {}

/* ----- cmpop ----- */
/** @constructor */
Sk.astnodes.Eq = function Eq() {}
/** @constructor */
Sk.astnodes.NotEq = function NotEq() {}
/** @constructor */
Sk.astnodes.Lt = function Lt() {}
/** @constructor */
Sk.astnodes.LtE = function LtE() {}
/** @constructor */
Sk.astnodes.Gt = function Gt() {}
/** @constructor */
Sk.astnodes.GtE = function GtE() {}
/** @constructor */
Sk.astnodes.Is = function Is() {}
/** @constructor */
Sk.astnodes.IsNot = function IsNot() {}
/** @constructor */
Sk.astnodes.In = function In() {}
/** @constructor */
Sk.astnodes.NotIn = function NotIn() {}







/* ---------------------- */
/* constructors for nodes */
/* ---------------------- */





/** @constructor */
Sk.astnodes.Module = function Module(/* {asdl_seq *} */ body, /* {string} */
                                          docstring)
{
    this.body = body;
    this.docstring = docstring;
    return this;
}

/** @constructor */
Sk.astnodes.Interactive = function Interactive(/* {asdl_seq *} */ body)
{
    this.body = body;
    return this;
}

/** @constructor */
Sk.astnodes.Expression = function Expression(/* {expr_ty} */ body)
{
    this.body = body;
    return this;
}

/** @constructor */
Sk.astnodes.Suite = function Suite(/* {asdl_seq *} */ body)
{
    this.body = body;
    return this;
}

/** @constructor */
Sk.astnodes.FunctionDef = function FunctionDef(/* {identifier} */ name, /*
                                                    {arguments__ty} */ args, /*
                                                    {asdl_seq *} */ body, /*
                                                    {asdl_seq *} */
                                                    decorator_list, /*
                                                    {expr_ty} */ returns, /*
                                                    {string} */ docstring, /*
                                                    {int} */ lineno, /* {int}
                                                    */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.name = name;
    this.args = args;
    this.body = body;
    this.decorator_list = decorator_list;
    this.returns = returns;
    this.docstring = docstring;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.AsyncFunctionDef = function AsyncFunctionDef(/* {identifier} */
                                                              name, /*
                                                              {arguments__ty}
                                                              */ args, /*
                                                              {asdl_seq *} */
                                                              body, /*
                                                              {asdl_seq *} */
                                                              decorator_list,
                                                              /* {expr_ty} */
                                                              returns, /*
                                                              {string} */
                                                              docstring, /*
                                                              {int} */ lineno,
                                                              /* {int} */
                                                              col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.name = name;
    this.args = args;
    this.body = body;
    this.decorator_list = decorator_list;
    this.returns = returns;
    this.docstring = docstring;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.ClassDef = function ClassDef(/* {identifier} */ name, /* {asdl_seq
                                              *} */ bases, /* {asdl_seq *} */
                                              keywords, /* {asdl_seq *} */
                                              body, /* {asdl_seq *} */
                                              decorator_list, /* {string} */
                                              docstring, /* {int} */ lineno, /*
                                              {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.name = name;
    this.bases = bases;
    this.keywords = keywords;
    this.body = body;
    this.decorator_list = decorator_list;
    this.docstring = docstring;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Return = function Return(/* {expr_ty} */ value, /* {int} */ lineno,
                                          /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Delete = function Delete(/* {asdl_seq *} */ targets, /* {int} */
                                          lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.targets = targets;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Assign = function Assign(/* {asdl_seq *} */ targets, /* {expr_ty}
                                          */ value, /* {int} */ lineno, /*
                                          {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.targets = targets;
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.AugAssign = function AugAssign(/* {expr_ty} */ target, /*
                                                {operator_ty} */ op, /*
                                                {expr_ty} */ value, /* {int} */
                                                lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.target = target;
    this.op = op;
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.AnnAssign = function AnnAssign(/* {expr_ty} */ target, /* {expr_ty}
                                                */ annotation, /* {expr_ty} */
                                                value, /* {int} */ simple, /*
                                                {int} */ lineno, /* {int} */
                                                col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.target = target;
    this.annotation = annotation;
    this.value = value;
    this.simple = simple;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.For = function For(/* {expr_ty} */ target, /* {expr_ty} */ iter, /*
                                    {asdl_seq *} */ body, /* {asdl_seq *} */
                                    orelse, /* {int} */ lineno, /* {int} */
                                    col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.target = target;
    this.iter = iter;
    this.body = body;
    this.orelse = orelse;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.AsyncFor = function AsyncFor(/* {expr_ty} */ target, /* {expr_ty}
                                              */ iter, /* {asdl_seq *} */ body,
                                              /* {asdl_seq *} */ orelse, /*
                                              {int} */ lineno, /* {int} */
                                              col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.target = target;
    this.iter = iter;
    this.body = body;
    this.orelse = orelse;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.While = function While(/* {expr_ty} */ test, /* {asdl_seq *} */
                                        body, /* {asdl_seq *} */ orelse, /*
                                        {int} */ lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.test = test;
    this.body = body;
    this.orelse = orelse;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.If = function If(/* {expr_ty} */ test, /* {asdl_seq *} */ body, /*
                                  {asdl_seq *} */ orelse, /* {int} */ lineno,
                                  /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.test = test;
    this.body = body;
    this.orelse = orelse;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.With = function With(/* {asdl_seq *} */ items, /* {asdl_seq *} */
                                      body, /* {int} */ lineno, /* {int} */
                                      col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.items = items;
    this.body = body;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.AsyncWith = function AsyncWith(/* {asdl_seq *} */ items, /*
                                                {asdl_seq *} */ body, /* {int}
                                                */ lineno, /* {int} */
                                                col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.items = items;
    this.body = body;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Raise = function Raise(/* {expr_ty} */ exc, /* {expr_ty} */ cause,
                                        /* {expr_ty} */ inst, /* {expr_ty} */
                                        tback, /* {int} */ lineno, /* {int} */
                                        col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.exc = exc;
    this.cause = cause;
    this.inst = inst;
    this.tback = tback;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Try = function Try(/* {asdl_seq *} */ body, /* {asdl_seq *} */
                                    handlers, /* {asdl_seq *} */ orelse, /*
                                    {asdl_seq *} */ finalbody, /* {int} */
                                    lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.body = body;
    this.handlers = handlers;
    this.orelse = orelse;
    this.finalbody = finalbody;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Assert = function Assert(/* {expr_ty} */ test, /* {expr_ty} */ msg,
                                          /* {int} */ lineno, /* {int} */
                                          col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.test = test;
    this.msg = msg;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Import = function Import(/* {asdl_seq *} */ names, /* {int} */
                                          lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.names = names;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.ImportFrom = function ImportFrom(/* {identifier} */ module, /*
                                                  {asdl_seq *} */ names, /*
                                                  {int} */ level, /* {int} */
                                                  lineno, /* {int} */
                                                  col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.module = module;
    this.names = names;
    this.level = level;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Global = function Global(/* {asdl_seq *} */ names, /* {int} */
                                          lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.names = names;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Nonlocal = function Nonlocal(/* {asdl_seq *} */ names, /* {int} */
                                              lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.names = names;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Expr = function Expr(/* {expr_ty} */ value, /* {int} */ lineno, /*
                                      {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Pass = function Pass(/* {int} */ lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Break = function Break(/* {int} */ lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Continue = function Continue(/* {int} */ lineno, /* {int} */
                                              col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Print = function Print(/* {expr_ty} */ dest, /* {asdl_seq *} */
                                        values, /* {int} */ nl, /* {int} */
                                        lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.dest = dest;
    this.values = values;
    this.nl = nl;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Debugger = function Debugger(/* {int} */ lineno, /* {int} */
                                              col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.BoolOp = function BoolOp(/* {boolop_ty} */ op, /* {asdl_seq *} */
                                          values, /* {int} */ lineno, /* {int}
                                          */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.op = op;
    this.values = values;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.BinOp = function BinOp(/* {expr_ty} */ left, /* {operator_ty} */
                                        op, /* {expr_ty} */ right, /* {int} */
                                        lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.left = left;
    this.op = op;
    this.right = right;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.UnaryOp = function UnaryOp(/* {unaryop_ty} */ op, /* {expr_ty} */
                                            operand, /* {int} */ lineno, /*
                                            {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.op = op;
    this.operand = operand;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Lambda = function Lambda(/* {arguments__ty} */ args, /* {expr_ty}
                                          */ body, /* {int} */ lineno, /* {int}
                                          */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.args = args;
    this.body = body;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.IfExp = function IfExp(/* {expr_ty} */ test, /* {expr_ty} */ body,
                                        /* {expr_ty} */ orelse, /* {int} */
                                        lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.test = test;
    this.body = body;
    this.orelse = orelse;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Dict = function Dict(/* {asdl_seq *} */ keys, /* {asdl_seq *} */
                                      values, /* {int} */ lineno, /* {int} */
                                      col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.keys = keys;
    this.values = values;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Set = function Set(/* {asdl_seq *} */ elts, /* {int} */ lineno, /*
                                    {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.elts = elts;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.ListComp = function ListComp(/* {expr_ty} */ elt, /* {asdl_seq *}
                                              */ generators, /* {int} */
                                              lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.elt = elt;
    this.generators = generators;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.SetComp = function SetComp(/* {expr_ty} */ elt, /* {asdl_seq *} */
                                            generators, /* {int} */ lineno, /*
                                            {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.elt = elt;
    this.generators = generators;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.DictComp = function DictComp(/* {expr_ty} */ key, /* {expr_ty} */
                                              value, /* {asdl_seq *} */
                                              generators, /* {int} */ lineno,
                                              /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.key = key;
    this.value = value;
    this.generators = generators;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.GeneratorExp = function GeneratorExp(/* {expr_ty} */ elt, /*
                                                      {asdl_seq *} */
                                                      generators, /* {int} */
                                                      lineno, /* {int} */
                                                      col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.elt = elt;
    this.generators = generators;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Await = function Await(/* {expr_ty} */ value, /* {int} */ lineno,
                                        /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Yield = function Yield(/* {expr_ty} */ value, /* {int} */ lineno,
                                        /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.YieldFrom = function YieldFrom(/* {expr_ty} */ value, /* {int} */
                                                lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Compare = function Compare(/* {expr_ty} */ left, /* {asdl_int_seq
                                            *} */ ops, /* {asdl_seq *} */
                                            comparators, /* {int} */ lineno, /*
                                            {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.left = left;
    this.ops = ops;
    this.comparators = comparators;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Call = function Call(/* {expr_ty} */ func, /* {asdl_seq *} */ args,
                                      /* {asdl_seq *} */ keywords, /* {int} */
                                      lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.func = func;
    this.args = args;
    this.keywords = keywords;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Num = function Num(/* {object} */ n, /* {int} */ lineno, /* {int}
                                    */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.n = n;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Str = function Str(/* {string} */ s, /* {int} */ lineno, /* {int}
                                    */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.s = s;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.FormattedValue = function FormattedValue(/* {expr_ty} */ value, /*
                                                          {int} */ conversion,
                                                          /* {expr_ty} */
                                                          format_spec, /* {int}
                                                          */ lineno, /* {int}
                                                          */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.conversion = conversion;
    this.format_spec = format_spec;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.JoinedStr = function JoinedStr(/* {asdl_seq *} */ values, /* {int}
                                                */ lineno, /* {int} */
                                                col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.values = values;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Bytes = function Bytes(/* {bytes} */ s, /* {int} */ lineno, /*
                                        {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.s = s;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.NameConstant = function NameConstant(/* {singleton} */ value, /*
                                                      {int} */ lineno, /* {int}
                                                      */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Ellipsis = function Ellipsis(/* {int} */ lineno, /* {int} */
                                              col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Constant = function Constant(/* {constant} */ value, /* {int} */
                                              lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Attribute = function Attribute(/* {expr_ty} */ value, /*
                                                {identifier} */ attr, /*
                                                {expr_context_ty} */ ctx, /*
                                                {int} */ lineno, /* {int} */
                                                col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.attr = attr;
    this.ctx = ctx;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Subscript = function Subscript(/* {expr_ty} */ value, /* {slice_ty}
                                                */ slice, /* {expr_context_ty}
                                                */ ctx, /* {int} */ lineno, /*
                                                {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.slice = slice;
    this.ctx = ctx;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Starred = function Starred(/* {expr_ty} */ value, /*
                                            {expr_context_ty} */ ctx, /* {int}
                                            */ lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.value = value;
    this.ctx = ctx;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Name = function Name(/* {identifier} */ id, /* {expr_context_ty} */
                                      ctx, /* {int} */ lineno, /* {int} */
                                      col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.id = id;
    this.ctx = ctx;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.List = function List(/* {asdl_seq *} */ elts, /* {expr_context_ty}
                                      */ ctx, /* {int} */ lineno, /* {int} */
                                      col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.elts = elts;
    this.ctx = ctx;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Tuple = function Tuple(/* {asdl_seq *} */ elts, /*
                                        {expr_context_ty} */ ctx, /* {int} */
                                        lineno, /* {int} */ col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.elts = elts;
    this.ctx = ctx;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.Slice = function Slice(/* {expr_ty} */ lower, /* {expr_ty} */
                                        upper, /* {expr_ty} */ step)
{
    this.lower = lower;
    this.upper = upper;
    this.step = step;
    return this;
}

/** @constructor */
Sk.astnodes.ExtSlice = function ExtSlice(/* {asdl_seq *} */ dims)
{
    this.dims = dims;
    return this;
}

/** @constructor */
Sk.astnodes.Index = function Index(/* {expr_ty} */ value)
{
    this.value = value;
    return this;
}

/** @constructor */
Sk.astnodes.comprehension = function comprehension(/* {expr_ty} */ target, /*
                                                        {expr_ty} */ iter, /*
                                                        {asdl_seq *} */ ifs, /*
                                                        {int} */ is_async)
{
    this.target = target;
    this.iter = iter;
    this.ifs = ifs;
    this.is_async = is_async;
    return this;
}

/** @constructor */
Sk.astnodes.ExceptHandler = function ExceptHandler(/* {expr_ty} */ type, /*
                                                        {identifier} */ name,
                                                        /* {asdl_seq *} */
                                                        body, /* {int} */
                                                        lineno, /* {int} */
                                                        col_offset)
{
    Sk.asserts.assert(lineno !== null && lineno !== undefined);
    Sk.asserts.assert(col_offset !== null && col_offset !== undefined);
    this.type = type;
    this.name = name;
    this.body = body;
    this.lineno = lineno;
    this.col_offset = col_offset;
    return this;
}

/** @constructor */
Sk.astnodes.arguments_ = function arguments_(/* {asdl_seq *} */ args, /*
                                                  {arg_ty} */ vararg, /*
                                                  {asdl_seq *} */ kwonlyargs,
                                                  /* {asdl_seq *} */
                                                  kw_defaults, /* {arg_ty} */
                                                  kwarg, /* {asdl_seq *} */
                                                  defaults)
{
    this.args = args;
    this.vararg = vararg;
    this.kwonlyargs = kwonlyargs;
    this.kw_defaults = kw_defaults;
    this.kwarg = kwarg;
    this.defaults = defaults;
    return this;
}

/** @constructor */
Sk.astnodes.arg = function arg(/* {identifier} */ arg, /* {expr_ty} */
                                    annotation)
{
    this.arg = arg;
    this.annotation = annotation;
    return this;
}

/** @constructor */
Sk.astnodes.keyword = function keyword(/* {identifier} */ arg, /* {expr_ty} */
                                            value)
{
    this.arg = arg;
    this.value = value;
    return this;
}

/** @constructor */
Sk.astnodes.alias = function alias(/* {identifier} */ name, /* {identifier} */
                                        asname)
{
    this.name = name;
    this.asname = asname;
    return this;
}

/** @constructor */
Sk.astnodes.withitem = function withitem(/* {expr_ty} */ context_expr, /*
                                              {expr_ty} */ optional_vars)
{
    this.context_expr = context_expr;
    this.optional_vars = optional_vars;
    return this;
}


Sk.astnodes.Module.prototype._astname = "Module";
Sk.astnodes.Module.prototype._fields = [
    "body", function(n) { return n.body; },
    "docstring", function(n) { return n.docstring; }
];
Sk.astnodes.Interactive.prototype._astname = "Interactive";
Sk.astnodes.Interactive.prototype._fields = [
    "body", function(n) { return n.body; }
];
Sk.astnodes.Expression.prototype._astname = "Expression";
Sk.astnodes.Expression.prototype._fields = [
    "body", function(n) { return n.body; }
];
Sk.astnodes.Suite.prototype._astname = "Suite";
Sk.astnodes.Suite.prototype._fields = [
    "body", function(n) { return n.body; }
];
Sk.astnodes.FunctionDef.prototype._astname = "FunctionDef";
Sk.astnodes.FunctionDef.prototype._fields = [
    "name", function(n) { return n.name; },
    "args", function(n) { return n.args; },
    "body", function(n) { return n.body; },
    "decorator_list", function(n) { return n.decorator_list; },
    "returns", function(n) { return n.returns; },
    "docstring", function(n) { return n.docstring; }
];
Sk.astnodes.AsyncFunctionDef.prototype._astname = "AsyncFunctionDef";
Sk.astnodes.AsyncFunctionDef.prototype._fields = [
    "name", function(n) { return n.name; },
    "args", function(n) { return n.args; },
    "body", function(n) { return n.body; },
    "decorator_list", function(n) { return n.decorator_list; },
    "returns", function(n) { return n.returns; },
    "docstring", function(n) { return n.docstring; }
];
Sk.astnodes.ClassDef.prototype._astname = "ClassDef";
Sk.astnodes.ClassDef.prototype._fields = [
    "name", function(n) { return n.name; },
    "bases", function(n) { return n.bases; },
    "keywords", function(n) { return n.keywords; },
    "body", function(n) { return n.body; },
    "decorator_list", function(n) { return n.decorator_list; },
    "docstring", function(n) { return n.docstring; }
];
Sk.astnodes.Return.prototype._astname = "Return";
Sk.astnodes.Return.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.Delete.prototype._astname = "Delete";
Sk.astnodes.Delete.prototype._fields = [
    "targets", function(n) { return n.targets; }
];
Sk.astnodes.Assign.prototype._astname = "Assign";
Sk.astnodes.Assign.prototype._fields = [
    "targets", function(n) { return n.targets; },
    "value", function(n) { return n.value; }
];
Sk.astnodes.AugAssign.prototype._astname = "AugAssign";
Sk.astnodes.AugAssign.prototype._fields = [
    "target", function(n) { return n.target; },
    "op", function(n) { return n.op; },
    "value", function(n) { return n.value; }
];
Sk.astnodes.AnnAssign.prototype._astname = "AnnAssign";
Sk.astnodes.AnnAssign.prototype._fields = [
    "target", function(n) { return n.target; },
    "annotation", function(n) { return n.annotation; },
    "value", function(n) { return n.value; },
    "simple", function(n) { return n.simple; }
];
Sk.astnodes.For.prototype._astname = "For";
Sk.astnodes.For.prototype._fields = [
    "target", function(n) { return n.target; },
    "iter", function(n) { return n.iter; },
    "body", function(n) { return n.body; },
    "orelse", function(n) { return n.orelse; }
];
Sk.astnodes.AsyncFor.prototype._astname = "AsyncFor";
Sk.astnodes.AsyncFor.prototype._fields = [
    "target", function(n) { return n.target; },
    "iter", function(n) { return n.iter; },
    "body", function(n) { return n.body; },
    "orelse", function(n) { return n.orelse; }
];
Sk.astnodes.While.prototype._astname = "While";
Sk.astnodes.While.prototype._fields = [
    "test", function(n) { return n.test; },
    "body", function(n) { return n.body; },
    "orelse", function(n) { return n.orelse; }
];
Sk.astnodes.If.prototype._astname = "If";
Sk.astnodes.If.prototype._fields = [
    "test", function(n) { return n.test; },
    "body", function(n) { return n.body; },
    "orelse", function(n) { return n.orelse; }
];
Sk.astnodes.With.prototype._astname = "With";
Sk.astnodes.With.prototype._fields = [
    "items", function(n) { return n.items; },
    "body", function(n) { return n.body; }
];
Sk.astnodes.AsyncWith.prototype._astname = "AsyncWith";
Sk.astnodes.AsyncWith.prototype._fields = [
    "items", function(n) { return n.items; },
    "body", function(n) { return n.body; }
];
Sk.astnodes.Raise.prototype._astname = "Raise";
Sk.astnodes.Raise.prototype._fields = [
    "exc", function(n) { return n.exc; },
    "cause", function(n) { return n.cause; },
    "inst", function(n) { return n.inst; },
    "tback", function(n) { return n.tback; }
];
Sk.astnodes.Try.prototype._astname = "Try";
Sk.astnodes.Try.prototype._fields = [
    "body", function(n) { return n.body; },
    "handlers", function(n) { return n.handlers; },
    "orelse", function(n) { return n.orelse; },
    "finalbody", function(n) { return n.finalbody; }
];
Sk.astnodes.Assert.prototype._astname = "Assert";
Sk.astnodes.Assert.prototype._fields = [
    "test", function(n) { return n.test; },
    "msg", function(n) { return n.msg; }
];
Sk.astnodes.Import.prototype._astname = "Import";
Sk.astnodes.Import.prototype._fields = [
    "names", function(n) { return n.names; }
];
Sk.astnodes.ImportFrom.prototype._astname = "ImportFrom";
Sk.astnodes.ImportFrom.prototype._fields = [
    "module", function(n) { return n.module; },
    "names", function(n) { return n.names; },
    "level", function(n) { return n.level; }
];
Sk.astnodes.Global.prototype._astname = "Global";
Sk.astnodes.Global.prototype._fields = [
    "names", function(n) { return n.names; }
];
Sk.astnodes.Nonlocal.prototype._astname = "Nonlocal";
Sk.astnodes.Nonlocal.prototype._fields = [
    "names", function(n) { return n.names; }
];
Sk.astnodes.Expr.prototype._astname = "Expr";
Sk.astnodes.Expr.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.Pass.prototype._astname = "Pass";
Sk.astnodes.Pass.prototype._fields = [
];
Sk.astnodes.Break.prototype._astname = "Break";
Sk.astnodes.Break.prototype._fields = [
];
Sk.astnodes.Continue.prototype._astname = "Continue";
Sk.astnodes.Continue.prototype._fields = [
];
Sk.astnodes.Print.prototype._astname = "Print";
Sk.astnodes.Print.prototype._fields = [
    "dest", function(n) { return n.dest; },
    "values", function(n) { return n.values; },
    "nl", function(n) { return n.nl; }
];
Sk.astnodes.Debugger.prototype._astname = "Debugger";
Sk.astnodes.Debugger.prototype._fields = [
];
Sk.astnodes.BoolOp.prototype._astname = "BoolOp";
Sk.astnodes.BoolOp.prototype._fields = [
    "op", function(n) { return n.op; },
    "values", function(n) { return n.values; }
];
Sk.astnodes.BinOp.prototype._astname = "BinOp";
Sk.astnodes.BinOp.prototype._fields = [
    "left", function(n) { return n.left; },
    "op", function(n) { return n.op; },
    "right", function(n) { return n.right; }
];
Sk.astnodes.UnaryOp.prototype._astname = "UnaryOp";
Sk.astnodes.UnaryOp.prototype._fields = [
    "op", function(n) { return n.op; },
    "operand", function(n) { return n.operand; }
];
Sk.astnodes.Lambda.prototype._astname = "Lambda";
Sk.astnodes.Lambda.prototype._fields = [
    "args", function(n) { return n.args; },
    "body", function(n) { return n.body; }
];
Sk.astnodes.IfExp.prototype._astname = "IfExp";
Sk.astnodes.IfExp.prototype._fields = [
    "test", function(n) { return n.test; },
    "body", function(n) { return n.body; },
    "orelse", function(n) { return n.orelse; }
];
Sk.astnodes.Dict.prototype._astname = "Dict";
Sk.astnodes.Dict.prototype._fields = [
    "keys", function(n) { return n.keys; },
    "values", function(n) { return n.values; }
];
Sk.astnodes.Set.prototype._astname = "Set";
Sk.astnodes.Set.prototype._fields = [
    "elts", function(n) { return n.elts; }
];
Sk.astnodes.ListComp.prototype._astname = "ListComp";
Sk.astnodes.ListComp.prototype._fields = [
    "elt", function(n) { return n.elt; },
    "generators", function(n) { return n.generators; }
];
Sk.astnodes.SetComp.prototype._astname = "SetComp";
Sk.astnodes.SetComp.prototype._fields = [
    "elt", function(n) { return n.elt; },
    "generators", function(n) { return n.generators; }
];
Sk.astnodes.DictComp.prototype._astname = "DictComp";
Sk.astnodes.DictComp.prototype._fields = [
    "key", function(n) { return n.key; },
    "value", function(n) { return n.value; },
    "generators", function(n) { return n.generators; }
];
Sk.astnodes.GeneratorExp.prototype._astname = "GeneratorExp";
Sk.astnodes.GeneratorExp.prototype._fields = [
    "elt", function(n) { return n.elt; },
    "generators", function(n) { return n.generators; }
];
Sk.astnodes.Await.prototype._astname = "Await";
Sk.astnodes.Await.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.Yield.prototype._astname = "Yield";
Sk.astnodes.Yield.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.YieldFrom.prototype._astname = "YieldFrom";
Sk.astnodes.YieldFrom.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.Compare.prototype._astname = "Compare";
Sk.astnodes.Compare.prototype._fields = [
    "left", function(n) { return n.left; },
    "ops", function(n) { return n.ops; },
    "comparators", function(n) { return n.comparators; }
];
Sk.astnodes.Call.prototype._astname = "Call";
Sk.astnodes.Call.prototype._fields = [
    "func", function(n) { return n.func; },
    "args", function(n) { return n.args; },
    "keywords", function(n) { return n.keywords; }
];
Sk.astnodes.Num.prototype._astname = "Num";
Sk.astnodes.Num.prototype._fields = [
    "n", function(n) { return n.n; }
];
Sk.astnodes.Str.prototype._astname = "Str";
Sk.astnodes.Str.prototype._fields = [
    "s", function(n) { return n.s; }
];
Sk.astnodes.FormattedValue.prototype._astname = "FormattedValue";
Sk.astnodes.FormattedValue.prototype._fields = [
    "value", function(n) { return n.value; },
    "conversion", function(n) { return n.conversion; },
    "format_spec", function(n) { return n.format_spec; }
];
Sk.astnodes.JoinedStr.prototype._astname = "JoinedStr";
Sk.astnodes.JoinedStr.prototype._fields = [
    "values", function(n) { return n.values; }
];
Sk.astnodes.Bytes.prototype._astname = "Bytes";
Sk.astnodes.Bytes.prototype._fields = [
    "s", function(n) { return n.s; }
];
Sk.astnodes.NameConstant.prototype._astname = "NameConstant";
Sk.astnodes.NameConstant.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.Ellipsis.prototype._astname = "Ellipsis";
Sk.astnodes.Ellipsis.prototype._fields = [
];
Sk.astnodes.Constant.prototype._astname = "Constant";
Sk.astnodes.Constant.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.Attribute.prototype._astname = "Attribute";
Sk.astnodes.Attribute.prototype._fields = [
    "value", function(n) { return n.value; },
    "attr", function(n) { return n.attr; },
    "ctx", function(n) { return n.ctx; }
];
Sk.astnodes.Subscript.prototype._astname = "Subscript";
Sk.astnodes.Subscript.prototype._fields = [
    "value", function(n) { return n.value; },
    "slice", function(n) { return n.slice; },
    "ctx", function(n) { return n.ctx; }
];
Sk.astnodes.Starred.prototype._astname = "Starred";
Sk.astnodes.Starred.prototype._fields = [
    "value", function(n) { return n.value; },
    "ctx", function(n) { return n.ctx; }
];
Sk.astnodes.Name.prototype._astname = "Name";
Sk.astnodes.Name.prototype._fields = [
    "id", function(n) { return n.id; },
    "ctx", function(n) { return n.ctx; }
];
Sk.astnodes.List.prototype._astname = "List";
Sk.astnodes.List.prototype._fields = [
    "elts", function(n) { return n.elts; },
    "ctx", function(n) { return n.ctx; }
];
Sk.astnodes.Tuple.prototype._astname = "Tuple";
Sk.astnodes.Tuple.prototype._fields = [
    "elts", function(n) { return n.elts; },
    "ctx", function(n) { return n.ctx; }
];
Sk.astnodes.Load.prototype._astname = "Load";
Sk.astnodes.Load.prototype._isenum = true;
Sk.astnodes.Store.prototype._astname = "Store";
Sk.astnodes.Store.prototype._isenum = true;
Sk.astnodes.Del.prototype._astname = "Del";
Sk.astnodes.Del.prototype._isenum = true;
Sk.astnodes.AugLoad.prototype._astname = "AugLoad";
Sk.astnodes.AugLoad.prototype._isenum = true;
Sk.astnodes.AugStore.prototype._astname = "AugStore";
Sk.astnodes.AugStore.prototype._isenum = true;
Sk.astnodes.Param.prototype._astname = "Param";
Sk.astnodes.Param.prototype._isenum = true;
Sk.astnodes.Slice.prototype._astname = "Slice";
Sk.astnodes.Slice.prototype._fields = [
    "lower", function(n) { return n.lower; },
    "upper", function(n) { return n.upper; },
    "step", function(n) { return n.step; }
];
Sk.astnodes.ExtSlice.prototype._astname = "ExtSlice";
Sk.astnodes.ExtSlice.prototype._fields = [
    "dims", function(n) { return n.dims; }
];
Sk.astnodes.Index.prototype._astname = "Index";
Sk.astnodes.Index.prototype._fields = [
    "value", function(n) { return n.value; }
];
Sk.astnodes.And.prototype._astname = "And";
Sk.astnodes.And.prototype._isenum = true;
Sk.astnodes.Or.prototype._astname = "Or";
Sk.astnodes.Or.prototype._isenum = true;
Sk.astnodes.Add.prototype._astname = "Add";
Sk.astnodes.Add.prototype._isenum = true;
Sk.astnodes.Sub.prototype._astname = "Sub";
Sk.astnodes.Sub.prototype._isenum = true;
Sk.astnodes.Mult.prototype._astname = "Mult";
Sk.astnodes.Mult.prototype._isenum = true;
Sk.astnodes.MatMult.prototype._astname = "MatMult";
Sk.astnodes.MatMult.prototype._isenum = true;
Sk.astnodes.Div.prototype._astname = "Div";
Sk.astnodes.Div.prototype._isenum = true;
Sk.astnodes.Mod.prototype._astname = "Mod";
Sk.astnodes.Mod.prototype._isenum = true;
Sk.astnodes.Pow.prototype._astname = "Pow";
Sk.astnodes.Pow.prototype._isenum = true;
Sk.astnodes.LShift.prototype._astname = "LShift";
Sk.astnodes.LShift.prototype._isenum = true;
Sk.astnodes.RShift.prototype._astname = "RShift";
Sk.astnodes.RShift.prototype._isenum = true;
Sk.astnodes.BitOr.prototype._astname = "BitOr";
Sk.astnodes.BitOr.prototype._isenum = true;
Sk.astnodes.BitXor.prototype._astname = "BitXor";
Sk.astnodes.BitXor.prototype._isenum = true;
Sk.astnodes.BitAnd.prototype._astname = "BitAnd";
Sk.astnodes.BitAnd.prototype._isenum = true;
Sk.astnodes.FloorDiv.prototype._astname = "FloorDiv";
Sk.astnodes.FloorDiv.prototype._isenum = true;
Sk.astnodes.Invert.prototype._astname = "Invert";
Sk.astnodes.Invert.prototype._isenum = true;
Sk.astnodes.Not.prototype._astname = "Not";
Sk.astnodes.Not.prototype._isenum = true;
Sk.astnodes.UAdd.prototype._astname = "UAdd";
Sk.astnodes.UAdd.prototype._isenum = true;
Sk.astnodes.USub.prototype._astname = "USub";
Sk.astnodes.USub.prototype._isenum = true;
Sk.astnodes.Eq.prototype._astname = "Eq";
Sk.astnodes.Eq.prototype._isenum = true;
Sk.astnodes.NotEq.prototype._astname = "NotEq";
Sk.astnodes.NotEq.prototype._isenum = true;
Sk.astnodes.Lt.prototype._astname = "Lt";
Sk.astnodes.Lt.prototype._isenum = true;
Sk.astnodes.LtE.prototype._astname = "LtE";
Sk.astnodes.LtE.prototype._isenum = true;
Sk.astnodes.Gt.prototype._astname = "Gt";
Sk.astnodes.Gt.prototype._isenum = true;
Sk.astnodes.GtE.prototype._astname = "GtE";
Sk.astnodes.GtE.prototype._isenum = true;
Sk.astnodes.Is.prototype._astname = "Is";
Sk.astnodes.Is.prototype._isenum = true;
Sk.astnodes.IsNot.prototype._astname = "IsNot";
Sk.astnodes.IsNot.prototype._isenum = true;
Sk.astnodes.In.prototype._astname = "In";
Sk.astnodes.In.prototype._isenum = true;
Sk.astnodes.NotIn.prototype._astname = "NotIn";
Sk.astnodes.NotIn.prototype._isenum = true;
Sk.astnodes.comprehension.prototype._astname = "comprehension";
Sk.astnodes.comprehension.prototype._fields = [
    "target", function(n) { return n.target; },
    "iter", function(n) { return n.iter; },
    "ifs", function(n) { return n.ifs; },
    "is_async", function(n) { return n.is_async; }
];
Sk.astnodes.ExceptHandler.prototype._astname = "ExceptHandler";
Sk.astnodes.ExceptHandler.prototype._fields = [
    "type", function(n) { return n.type; },
    "name", function(n) { return n.name; },
    "body", function(n) { return n.body; }
];
Sk.astnodes.arguments_.prototype._astname = "arguments";
Sk.astnodes.arguments_.prototype._fields = [
    "args", function(n) { return n.args; },
    "vararg", function(n) { return n.vararg; },
    "kwonlyargs", function(n) { return n.kwonlyargs; },
    "kw_defaults", function(n) { return n.kw_defaults; },
    "kwarg", function(n) { return n.kwarg; },
    "defaults", function(n) { return n.defaults; }
];
Sk.astnodes.arg.prototype._astname = "arg";
Sk.astnodes.arg.prototype._fields = [
    "arg", function(n) { return n.arg; },
    "annotation", function(n) { return n.annotation; }
];
Sk.astnodes.keyword.prototype._astname = "keyword";
Sk.astnodes.keyword.prototype._fields = [
    "arg", function(n) { return n.arg; },
    "value", function(n) { return n.value; }
];
Sk.astnodes.alias.prototype._astname = "alias";
Sk.astnodes.alias.prototype._fields = [
    "name", function(n) { return n.name; },
    "asname", function(n) { return n.asname; }
];
Sk.astnodes.withitem.prototype._astname = "withitem";
Sk.astnodes.withitem.prototype._fields = [
    "context_expr", function(n) { return n.context_expr; },
    "optional_vars", function(n) { return n.optional_vars; }
];

Sk.exportSymbol("Sk.astnodes", Sk.astnodes);


/***/ }),

/***/ "./gen/parse_tables.js":
/*!*****************************!*\
  !*** ./gen/parse_tables.js ***!
  \*****************************/
/*! no static exports found */
/***/ (function(module, exports) {

// generated by pgen/main.py
Sk.OpMap = {
"(": Sk.token.tokens.T_LPAR,
")": Sk.token.tokens.T_RPAR,
"[": Sk.token.tokens.T_LSQB,
"]": Sk.token.tokens.T_RSQB,
":": Sk.token.tokens.T_COLON,
",": Sk.token.tokens.T_COMMA,
";": Sk.token.tokens.T_SEMI,
"+": Sk.token.tokens.T_PLUS,
"-": Sk.token.tokens.T_MINUS,
"*": Sk.token.tokens.T_STAR,
"/": Sk.token.tokens.T_SLASH,
"|": Sk.token.tokens.T_VBAR,
"&": Sk.token.tokens.T_AMPER,
"<": Sk.token.tokens.T_LESS,
">": Sk.token.tokens.T_GREATER,
"=": Sk.token.tokens.T_EQUAL,
".": Sk.token.tokens.T_DOT,
"%": Sk.token.tokens.T_PERCENT,
"`": Sk.token.tokens.T_BACKQUOTE,
"{": Sk.token.tokens.T_LBRACE,
"}": Sk.token.tokens.T_RBRACE,
"@": Sk.token.tokens.T_AT,
"@=": Sk.token.tokens.T_ATEQUAL,
"==": Sk.token.tokens.T_EQEQUAL,
"!=": Sk.token.tokens.T_NOTEQUAL,
"<>": Sk.token.tokens.T_NOTEQUAL,
"<=": Sk.token.tokens.T_LESSEQUAL,
">=": Sk.token.tokens.T_GREATEREQUAL,
"~": Sk.token.tokens.T_TILDE,
"^": Sk.token.tokens.T_CIRCUMFLEX,
"<<": Sk.token.tokens.T_LEFTSHIFT,
">>": Sk.token.tokens.T_RIGHTSHIFT,
"**": Sk.token.tokens.T_DOUBLESTAR,
"+=": Sk.token.tokens.T_PLUSEQUAL,
"-=": Sk.token.tokens.T_MINEQUAL,
"*=": Sk.token.tokens.T_STAREQUAL,
"/=": Sk.token.tokens.T_SLASHEQUAL,
"%=": Sk.token.tokens.T_PERCENTEQUAL,
"&=": Sk.token.tokens.T_AMPEREQUAL,
"|=": Sk.token.tokens.T_VBAREQUAL,
"^=": Sk.token.tokens.T_CIRCUMFLEXEQUAL,
"<<=": Sk.token.tokens.T_LEFTSHIFTEQUAL,
">>=": Sk.token.tokens.T_RIGHTSHIFTEQUAL,
"**=": Sk.token.tokens.T_DOUBLESTAREQUAL,
"//": Sk.token.tokens.T_DOUBLESLASH,
"//=": Sk.token.tokens.T_DOUBLESLASHEQUAL,
"->": Sk.token.tokens.T_RARROW,
"...": Sk.token.tokens.T_ELLIPSIS
};
Sk.ParseTables = {
sym:
{and_expr: 257,
 and_test: 258,
 annassign: 259,
 arglist: 260,
 argument: 261,
 arith_expr: 262,
 assert_stmt: 263,
 async_funcdef: 264,
 async_stmt: 265,
 atom: 266,
 atom_expr: 267,
 augassign: 268,
 break_stmt: 269,
 classdef: 270,
 comp_for: 271,
 comp_if: 272,
 comp_iter: 273,
 comp_op: 274,
 comparison: 275,
 compound_stmt: 276,
 continue_stmt: 277,
 debugger_stmt: 278,
 decorated: 279,
 decorator: 280,
 decorators: 281,
 del_stmt: 282,
 dictorsetmaker: 283,
 dotted_as_name: 284,
 dotted_as_names: 285,
 dotted_name: 286,
 encoding_decl: 287,
 eval_input: 288,
 except_clause: 289,
 expr: 290,
 expr_stmt: 291,
 exprlist: 292,
 factor: 293,
 file_input: 294,
 flow_stmt: 295,
 for_stmt: 296,
 funcdef: 297,
 global_stmt: 298,
 if_stmt: 299,
 import_as_name: 300,
 import_as_names: 301,
 import_from: 302,
 import_name: 303,
 import_stmt: 304,
 lambdef: 305,
 lambdef_nocond: 306,
 nonlocal_stmt: 307,
 not_test: 308,
 or_test: 309,
 parameters: 310,
 pass_stmt: 311,
 power: 312,
 print_stmt: 313,
 raise_stmt: 314,
 return_stmt: 315,
 shift_expr: 316,
 simple_stmt: 317,
 single_input: 256,
 sliceop: 318,
 small_stmt: 319,
 star_expr: 320,
 stmt: 321,
 subscript: 322,
 subscriptlist: 323,
 suite: 324,
 term: 325,
 test: 326,
 test_nocond: 327,
 testlist: 328,
 testlist_comp: 329,
 testlist_star_expr: 330,
 tfpdef: 331,
 trailer: 332,
 try_stmt: 333,
 typedargslist: 334,
 varargslist: 335,
 vfpdef: 336,
 while_stmt: 337,
 with_item: 338,
 with_stmt: 339,
 xor_expr: 340,
 yield_arg: 341,
 yield_expr: 342,
 yield_stmt: 343},
number2symbol:
{256: 'single_input',
 257: 'and_expr',
 258: 'and_test',
 259: 'annassign',
 260: 'arglist',
 261: 'argument',
 262: 'arith_expr',
 263: 'assert_stmt',
 264: 'async_funcdef',
 265: 'async_stmt',
 266: 'atom',
 267: 'atom_expr',
 268: 'augassign',
 269: 'break_stmt',
 270: 'classdef',
 271: 'comp_for',
 272: 'comp_if',
 273: 'comp_iter',
 274: 'comp_op',
 275: 'comparison',
 276: 'compound_stmt',
 277: 'continue_stmt',
 278: 'debugger_stmt',
 279: 'decorated',
 280: 'decorator',
 281: 'decorators',
 282: 'del_stmt',
 283: 'dictorsetmaker',
 284: 'dotted_as_name',
 285: 'dotted_as_names',
 286: 'dotted_name',
 287: 'encoding_decl',
 288: 'eval_input',
 289: 'except_clause',
 290: 'expr',
 291: 'expr_stmt',
 292: 'exprlist',
 293: 'factor',
 294: 'file_input',
 295: 'flow_stmt',
 296: 'for_stmt',
 297: 'funcdef',
 298: 'global_stmt',
 299: 'if_stmt',
 300: 'import_as_name',
 301: 'import_as_names',
 302: 'import_from',
 303: 'import_name',
 304: 'import_stmt',
 305: 'lambdef',
 306: 'lambdef_nocond',
 307: 'nonlocal_stmt',
 308: 'not_test',
 309: 'or_test',
 310: 'parameters',
 311: 'pass_stmt',
 312: 'power',
 313: 'print_stmt',
 314: 'raise_stmt',
 315: 'return_stmt',
 316: 'shift_expr',
 317: 'simple_stmt',
 318: 'sliceop',
 319: 'small_stmt',
 320: 'star_expr',
 321: 'stmt',
 322: 'subscript',
 323: 'subscriptlist',
 324: 'suite',
 325: 'term',
 326: 'test',
 327: 'test_nocond',
 328: 'testlist',
 329: 'testlist_comp',
 330: 'testlist_star_expr',
 331: 'tfpdef',
 332: 'trailer',
 333: 'try_stmt',
 334: 'typedargslist',
 335: 'varargslist',
 336: 'vfpdef',
 337: 'while_stmt',
 338: 'with_item',
 339: 'with_stmt',
 340: 'xor_expr',
 341: 'yield_arg',
 342: 'yield_expr',
 343: 'yield_stmt'},
dfas:
{256: [[[[1, 1], [2, 1], [3, 2]], [[0, 1]], [[2, 1]]],
       {2: 1,
        4: 1,
        5: 1,
        6: 1,
        7: 1,
        8: 1,
        9: 1,
        10: 1,
        11: 1,
        12: 1,
        13: 1,
        14: 1,
        15: 1,
        16: 1,
        17: 1,
        18: 1,
        19: 1,
        20: 1,
        21: 1,
        22: 1,
        23: 1,
        24: 1,
        25: 1,
        26: 1,
        27: 1,
        28: 1,
        29: 1,
        30: 1,
        31: 1,
        32: 1,
        33: 1,
        34: 1,
        35: 1,
        36: 1,
        37: 1,
        38: 1,
        39: 1,
        40: 1,
        41: 1,
        42: 1,
        43: 1}],
 257: [[[[44, 1]], [[45, 0], [0, 1]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 258: [[[[46, 1]], [[47, 0], [0, 1]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 259: [[[[48, 1]], [[49, 2]], [[50, 3], [0, 2]], [[49, 4]], [[0, 4]]],
       {48: 1}],
 260: [[[[51, 1]], [[52, 2], [0, 1]], [[51, 1], [0, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1,
        53: 1}],
 261: [[[[49, 1], [15, 2], [53, 2]],
        [[50, 2], [54, 3], [0, 1]],
        [[49, 3]],
        [[0, 3]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1,
        53: 1}],
 262: [[[[55, 1]], [[30, 0], [43, 0], [0, 1]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 263: [[[[24, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]],
       {24: 1}],
 264: [[[[10, 1]], [[56, 2]], [[0, 2]]], {10: 1}],
 265: [[[[10, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], {10: 1}],
 266: [[[[6, 1],
         [25, 1],
         [33, 1],
         [9, 1],
         [11, 1],
         [12, 2],
         [35, 3],
         [38, 4],
         [19, 1],
         [7, 5]],
        [[0, 1]],
        [[59, 1], [60, 6]],
        [[61, 1], [62, 7], [63, 7]],
        [[64, 1], [63, 8]],
        [[7, 5], [0, 5]],
        [[59, 1]],
        [[61, 1]],
        [[64, 1]]],
       {6: 1, 7: 1, 9: 1, 11: 1, 12: 1, 19: 1, 25: 1, 33: 1, 35: 1, 38: 1}],
 267: [[[[29, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        25: 1,
        29: 1,
        33: 1,
        35: 1,
        38: 1}],
 268: [[[[67, 1],
         [68, 1],
         [69, 1],
         [70, 1],
         [71, 1],
         [72, 1],
         [73, 1],
         [74, 1],
         [75, 1],
         [76, 1],
         [77, 1],
         [78, 1],
         [79, 1]],
        [[0, 1]]],
       {67: 1,
        68: 1,
        69: 1,
        70: 1,
        71: 1,
        72: 1,
        73: 1,
        74: 1,
        75: 1,
        76: 1,
        77: 1,
        78: 1,
        79: 1}],
 269: [[[[39, 1]], [[0, 1]]], {39: 1}],
 270: [[[[13, 1]],
        [[25, 2]],
        [[48, 3], [35, 4]],
        [[80, 5]],
        [[61, 6], [81, 7]],
        [[0, 5]],
        [[48, 3]],
        [[61, 6]]],
       {13: 1}],
 271: [[[[10, 1], [34, 2]],
        [[34, 2]],
        [[82, 3]],
        [[83, 4]],
        [[84, 5]],
        [[85, 6], [0, 5]],
        [[0, 6]]],
       {10: 1, 34: 1}],
 272: [[[[37, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], {37: 1}],
 273: [[[[87, 1], [54, 1]], [[0, 1]]], {10: 1, 34: 1, 37: 1}],
 274: [[[[88, 1],
         [89, 1],
         [8, 2],
         [90, 1],
         [88, 1],
         [83, 1],
         [91, 1],
         [92, 3],
         [93, 1],
         [94, 1]],
        [[0, 1]],
        [[83, 1]],
        [[8, 1], [0, 3]]],
       {8: 1, 83: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1, 93: 1, 94: 1}],
 275: [[[[95, 1]], [[96, 0], [0, 1]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 276: [[[[97, 1],
         [98, 1],
         [58, 1],
         [99, 1],
         [57, 1],
         [100, 1],
         [56, 1],
         [101, 1],
         [102, 1]],
        [[0, 1]]],
       {4: 1, 10: 1, 13: 1, 20: 1, 21: 1, 34: 1, 37: 1, 41: 1, 42: 1}],
 277: [[[[40, 1]], [[0, 1]]], {40: 1}],
 278: [[[[17, 1]], [[0, 1]]], {17: 1}],
 279: [[[[103, 1]], [[56, 2], [104, 2], [99, 2]], [[0, 2]]], {41: 1}],
 280: [[[[41, 1]],
        [[105, 2]],
        [[2, 4], [35, 3]],
        [[61, 5], [81, 6]],
        [[0, 4]],
        [[2, 4]],
        [[61, 5]]],
       {41: 1}],
 281: [[[[106, 1]], [[106, 1], [0, 1]]], {41: 1}],
 282: [[[[27, 1]], [[82, 2]], [[0, 2]]], {27: 1}],
 283: [[[[49, 1], [107, 2], [53, 3]],
        [[48, 4], [54, 5], [52, 6], [0, 1]],
        [[54, 5], [52, 6], [0, 2]],
        [[95, 7]],
        [[49, 7]],
        [[0, 5]],
        [[49, 8], [107, 8], [0, 6]],
        [[54, 5], [52, 9], [0, 7]],
        [[52, 6], [0, 8]],
        [[49, 10], [53, 11], [0, 9]],
        [[48, 12]],
        [[95, 13]],
        [[49, 13]],
        [[52, 9], [0, 13]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1,
        53: 1}],
 284: [[[[105, 1]], [[108, 2], [0, 1]], [[25, 3]], [[0, 3]]], {25: 1}],
 285: [[[[109, 1]], [[52, 0], [0, 1]]], {25: 1}],
 286: [[[[25, 1]], [[110, 0], [0, 1]]], {25: 1}],
 287: [[[[25, 1]], [[0, 1]]], {25: 1}],
 288: [[[[111, 1]], [[2, 1], [112, 2]], [[0, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 289: [[[[113, 1]],
        [[49, 2], [0, 1]],
        [[108, 3], [52, 3], [0, 2]],
        [[49, 4]],
        [[0, 4]]],
       {113: 1}],
 290: [[[[114, 1]], [[115, 0], [0, 1]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 291: [[[[116, 1]],
        [[117, 2], [50, 3], [118, 4], [0, 1]],
        [[111, 4], [62, 4]],
        [[116, 5], [62, 5]],
        [[0, 4]],
        [[50, 3], [0, 5]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 292: [[[[95, 1], [107, 1]], [[52, 2], [0, 1]], [[95, 1], [107, 1], [0, 2]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        15: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 293: [[[[119, 2], [30, 1], [22, 1], [43, 1]], [[120, 2]], [[0, 2]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 294: [[[[2, 0], [112, 1], [121, 0]], [[0, 1]]],
       {2: 1,
        4: 1,
        5: 1,
        6: 1,
        7: 1,
        8: 1,
        9: 1,
        10: 1,
        11: 1,
        12: 1,
        13: 1,
        14: 1,
        15: 1,
        16: 1,
        17: 1,
        18: 1,
        19: 1,
        20: 1,
        21: 1,
        22: 1,
        23: 1,
        24: 1,
        25: 1,
        26: 1,
        27: 1,
        28: 1,
        29: 1,
        30: 1,
        31: 1,
        32: 1,
        33: 1,
        34: 1,
        35: 1,
        36: 1,
        37: 1,
        38: 1,
        39: 1,
        40: 1,
        41: 1,
        42: 1,
        43: 1,
        112: 1}],
 295: [[[[122, 1], [123, 1], [124, 1], [125, 1], [126, 1]], [[0, 1]]],
       {5: 1, 23: 1, 31: 1, 39: 1, 40: 1}],
 296: [[[[34, 1]],
        [[82, 2]],
        [[83, 3]],
        [[111, 4]],
        [[48, 5]],
        [[80, 6]],
        [[127, 7], [0, 6]],
        [[48, 8]],
        [[80, 9]],
        [[0, 9]]],
       {34: 1}],
 297: [[[[4, 1]],
        [[25, 2]],
        [[128, 3]],
        [[48, 4], [129, 5]],
        [[80, 6]],
        [[49, 7]],
        [[0, 6]],
        [[48, 4]]],
       {4: 1}],
 298: [[[[26, 1]], [[25, 2]], [[52, 1], [0, 2]]], {26: 1}],
 299: [[[[37, 1]],
        [[49, 2]],
        [[48, 3]],
        [[80, 4]],
        [[127, 5], [130, 1], [0, 4]],
        [[48, 6]],
        [[80, 7]],
        [[0, 7]]],
       {37: 1}],
 300: [[[[25, 1]], [[108, 2], [0, 1]], [[25, 3]], [[0, 3]]], {25: 1}],
 301: [[[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]], {25: 1}],
 302: [[[[36, 1]],
        [[105, 2], [19, 3], [110, 3]],
        [[32, 4]],
        [[105, 2], [19, 3], [32, 4], [110, 3]],
        [[132, 5], [15, 5], [35, 6]],
        [[0, 5]],
        [[132, 7]],
        [[61, 5]]],
       {36: 1}],
 303: [[[[32, 1]], [[133, 2]], [[0, 2]]], {32: 1}],
 304: [[[[134, 1], [135, 1]], [[0, 1]]], {32: 1, 36: 1}],
 305: [[[[14, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]],
       {14: 1}],
 306: [[[[14, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]],
       {14: 1}],
 307: [[[[18, 1]], [[25, 2]], [[52, 1], [0, 2]]], {18: 1}],
 308: [[[[8, 1], [137, 2]], [[46, 2]], [[0, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 309: [[[[138, 1]], [[139, 0], [0, 1]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 310: [[[[35, 1]], [[61, 2], [140, 3]], [[0, 2]], [[61, 2]]], {35: 1}],
 311: [[[[28, 1]], [[0, 1]]], {28: 1}],
 312: [[[[141, 1]], [[53, 2], [0, 1]], [[120, 3]], [[0, 3]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        25: 1,
        29: 1,
        33: 1,
        35: 1,
        38: 1}],
 313: [[[[16, 1]],
        [[49, 2], [142, 3], [0, 1]],
        [[52, 4], [0, 2]],
        [[49, 5]],
        [[49, 2], [0, 4]],
        [[52, 6], [0, 5]],
        [[49, 7]],
        [[52, 8], [0, 7]],
        [[49, 7], [0, 8]]],
       {16: 1}],
 314: [[[[5, 1]],
        [[49, 2], [0, 1]],
        [[36, 3], [52, 3], [0, 2]],
        [[49, 4]],
        [[52, 5], [0, 4]],
        [[49, 6]],
        [[0, 6]]],
       {5: 1}],
 315: [[[[23, 1]], [[111, 2], [0, 1]], [[0, 2]]], {23: 1}],
 316: [[[[143, 1]], [[144, 0], [142, 0], [0, 1]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 317: [[[[145, 1]], [[2, 2], [146, 3]], [[0, 2]], [[145, 1], [2, 2]]],
       {5: 1,
        6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        16: 1,
        17: 1,
        18: 1,
        19: 1,
        22: 1,
        23: 1,
        24: 1,
        25: 1,
        26: 1,
        27: 1,
        28: 1,
        29: 1,
        30: 1,
        31: 1,
        32: 1,
        33: 1,
        35: 1,
        36: 1,
        38: 1,
        39: 1,
        40: 1,
        43: 1}],
 318: [[[[48, 1]], [[49, 2], [0, 1]], [[0, 2]]], {48: 1}],
 319: [[[[147, 1],
         [148, 1],
         [149, 1],
         [150, 1],
         [151, 1],
         [152, 1],
         [153, 1],
         [154, 1],
         [155, 1],
         [156, 1]],
        [[0, 1]]],
       {5: 1,
        6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        16: 1,
        17: 1,
        18: 1,
        19: 1,
        22: 1,
        23: 1,
        24: 1,
        25: 1,
        26: 1,
        27: 1,
        28: 1,
        29: 1,
        30: 1,
        31: 1,
        32: 1,
        33: 1,
        35: 1,
        36: 1,
        38: 1,
        39: 1,
        40: 1,
        43: 1}],
 320: [[[[15, 1]], [[95, 2]], [[0, 2]]], {15: 1}],
 321: [[[[1, 1], [3, 1]], [[0, 1]]],
       {4: 1,
        5: 1,
        6: 1,
        7: 1,
        8: 1,
        9: 1,
        10: 1,
        11: 1,
        12: 1,
        13: 1,
        14: 1,
        15: 1,
        16: 1,
        17: 1,
        18: 1,
        19: 1,
        20: 1,
        21: 1,
        22: 1,
        23: 1,
        24: 1,
        25: 1,
        26: 1,
        27: 1,
        28: 1,
        29: 1,
        30: 1,
        31: 1,
        32: 1,
        33: 1,
        34: 1,
        35: 1,
        36: 1,
        37: 1,
        38: 1,
        39: 1,
        40: 1,
        41: 1,
        42: 1,
        43: 1}],
 322: [[[[49, 1], [48, 2]],
        [[48, 2], [0, 1]],
        [[49, 3], [157, 4], [0, 2]],
        [[157, 4], [0, 3]],
        [[0, 4]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1,
        48: 1}],
 323: [[[[158, 1]], [[52, 2], [0, 1]], [[158, 1], [0, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1,
        48: 1}],
 324: [[[[1, 1], [2, 2]],
        [[0, 1]],
        [[159, 3]],
        [[121, 4]],
        [[160, 1], [121, 4]]],
       {2: 1,
        5: 1,
        6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        16: 1,
        17: 1,
        18: 1,
        19: 1,
        22: 1,
        23: 1,
        24: 1,
        25: 1,
        26: 1,
        27: 1,
        28: 1,
        29: 1,
        30: 1,
        31: 1,
        32: 1,
        33: 1,
        35: 1,
        36: 1,
        38: 1,
        39: 1,
        40: 1,
        43: 1}],
 325: [[[[120, 1]], [[161, 0], [15, 0], [162, 0], [41, 0], [163, 0], [0, 1]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 326: [[[[84, 1], [164, 2]],
        [[37, 3], [0, 1]],
        [[0, 2]],
        [[84, 4]],
        [[127, 5]],
        [[49, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 327: [[[[165, 1], [84, 1]], [[0, 1]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 328: [[[[49, 1]], [[52, 2], [0, 1]], [[49, 1], [0, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 329: [[[[49, 1], [107, 1]],
        [[54, 2], [52, 3], [0, 1]],
        [[0, 2]],
        [[49, 4], [107, 4], [0, 3]],
        [[52, 3], [0, 4]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 330: [[[[49, 1], [107, 1]], [[52, 2], [0, 1]], [[49, 1], [107, 1], [0, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        15: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 331: [[[[25, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], {25: 1}],
 332: [[[[35, 1], [110, 2], [38, 3]],
        [[61, 4], [81, 5]],
        [[25, 4]],
        [[166, 6]],
        [[0, 4]],
        [[61, 4]],
        [[64, 4]]],
       {35: 1, 38: 1, 110: 1}],
 333: [[[[20, 1]],
        [[48, 2]],
        [[80, 3]],
        [[167, 4], [168, 5]],
        [[48, 6]],
        [[48, 7]],
        [[80, 8]],
        [[80, 9]],
        [[167, 4], [127, 10], [168, 5], [0, 8]],
        [[0, 9]],
        [[48, 11]],
        [[80, 12]],
        [[168, 5], [0, 12]]],
       {20: 1}],
 334: [[[[15, 1], [169, 2], [53, 3]],
        [[169, 4], [52, 5], [0, 1]],
        [[50, 6], [52, 7], [0, 2]],
        [[169, 8]],
        [[52, 5], [0, 4]],
        [[169, 9], [53, 3], [0, 5]],
        [[49, 10]],
        [[15, 11], [169, 2], [53, 3], [0, 7]],
        [[52, 12], [0, 8]],
        [[50, 13], [52, 5], [0, 9]],
        [[52, 7], [0, 10]],
        [[169, 14], [52, 15], [0, 11]],
        [[0, 12]],
        [[49, 4]],
        [[52, 15], [0, 14]],
        [[169, 16], [53, 3], [0, 15]],
        [[50, 17], [52, 15], [0, 16]],
        [[49, 14]]],
       {15: 1, 25: 1, 53: 1}],
 335: [[[[15, 1], [53, 2], [170, 3]],
        [[170, 5], [52, 4], [0, 1]],
        [[170, 6]],
        [[50, 7], [52, 8], [0, 3]],
        [[53, 2], [170, 9], [0, 4]],
        [[52, 4], [0, 5]],
        [[52, 10], [0, 6]],
        [[49, 11]],
        [[15, 12], [53, 2], [170, 3], [0, 8]],
        [[50, 13], [52, 4], [0, 9]],
        [[0, 10]],
        [[52, 8], [0, 11]],
        [[52, 15], [170, 14], [0, 12]],
        [[49, 5]],
        [[52, 15], [0, 14]],
        [[53, 2], [170, 16], [0, 15]],
        [[50, 17], [52, 15], [0, 16]],
        [[49, 14]]],
       {15: 1, 25: 1, 53: 1}],
 336: [[[[25, 1]], [[0, 1]]], {25: 1}],
 337: [[[[21, 1]],
        [[49, 2]],
        [[48, 3]],
        [[80, 4]],
        [[127, 5], [0, 4]],
        [[48, 6]],
        [[80, 7]],
        [[0, 7]]],
       {21: 1}],
 338: [[[[49, 1]], [[108, 2], [0, 1]], [[95, 3]], [[0, 3]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 339: [[[[42, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]],
       {42: 1}],
 340: [[[[172, 1]], [[173, 0], [0, 1]]],
       {6: 1,
        7: 1,
        9: 1,
        11: 1,
        12: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        38: 1,
        43: 1}],
 341: [[[[111, 2], [36, 1]], [[49, 2]], [[0, 2]]],
       {6: 1,
        7: 1,
        8: 1,
        9: 1,
        11: 1,
        12: 1,
        14: 1,
        19: 1,
        22: 1,
        25: 1,
        29: 1,
        30: 1,
        33: 1,
        35: 1,
        36: 1,
        38: 1,
        43: 1}],
 342: [[[[31, 1]], [[174, 2], [0, 1]], [[0, 2]]], {31: 1}],
 343: [[[[62, 1]], [[0, 1]]], {31: 1}]},
states:
[[[[1, 1], [2, 1], [3, 2]], [[0, 1]], [[2, 1]]],
 [[[44, 1]], [[45, 0], [0, 1]]],
 [[[46, 1]], [[47, 0], [0, 1]]],
 [[[48, 1]], [[49, 2]], [[50, 3], [0, 2]], [[49, 4]], [[0, 4]]],
 [[[51, 1]], [[52, 2], [0, 1]], [[51, 1], [0, 2]]],
 [[[49, 1], [15, 2], [53, 2]],
  [[50, 2], [54, 3], [0, 1]],
  [[49, 3]],
  [[0, 3]]],
 [[[55, 1]], [[30, 0], [43, 0], [0, 1]]],
 [[[24, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]],
 [[[10, 1]], [[56, 2]], [[0, 2]]],
 [[[10, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]],
 [[[6, 1],
   [25, 1],
   [33, 1],
   [9, 1],
   [11, 1],
   [12, 2],
   [35, 3],
   [38, 4],
   [19, 1],
   [7, 5]],
  [[0, 1]],
  [[59, 1], [60, 6]],
  [[61, 1], [62, 7], [63, 7]],
  [[64, 1], [63, 8]],
  [[7, 5], [0, 5]],
  [[59, 1]],
  [[61, 1]],
  [[64, 1]]],
 [[[29, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]],
 [[[67, 1],
   [68, 1],
   [69, 1],
   [70, 1],
   [71, 1],
   [72, 1],
   [73, 1],
   [74, 1],
   [75, 1],
   [76, 1],
   [77, 1],
   [78, 1],
   [79, 1]],
  [[0, 1]]],
 [[[39, 1]], [[0, 1]]],
 [[[13, 1]],
  [[25, 2]],
  [[48, 3], [35, 4]],
  [[80, 5]],
  [[61, 6], [81, 7]],
  [[0, 5]],
  [[48, 3]],
  [[61, 6]]],
 [[[10, 1], [34, 2]],
  [[34, 2]],
  [[82, 3]],
  [[83, 4]],
  [[84, 5]],
  [[85, 6], [0, 5]],
  [[0, 6]]],
 [[[37, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]],
 [[[87, 1], [54, 1]], [[0, 1]]],
 [[[88, 1],
   [89, 1],
   [8, 2],
   [90, 1],
   [88, 1],
   [83, 1],
   [91, 1],
   [92, 3],
   [93, 1],
   [94, 1]],
  [[0, 1]],
  [[83, 1]],
  [[8, 1], [0, 3]]],
 [[[95, 1]], [[96, 0], [0, 1]]],
 [[[97, 1],
   [98, 1],
   [58, 1],
   [99, 1],
   [57, 1],
   [100, 1],
   [56, 1],
   [101, 1],
   [102, 1]],
  [[0, 1]]],
 [[[40, 1]], [[0, 1]]],
 [[[17, 1]], [[0, 1]]],
 [[[103, 1]], [[56, 2], [104, 2], [99, 2]], [[0, 2]]],
 [[[41, 1]],
  [[105, 2]],
  [[2, 4], [35, 3]],
  [[61, 5], [81, 6]],
  [[0, 4]],
  [[2, 4]],
  [[61, 5]]],
 [[[106, 1]], [[106, 1], [0, 1]]],
 [[[27, 1]], [[82, 2]], [[0, 2]]],
 [[[49, 1], [107, 2], [53, 3]],
  [[48, 4], [54, 5], [52, 6], [0, 1]],
  [[54, 5], [52, 6], [0, 2]],
  [[95, 7]],
  [[49, 7]],
  [[0, 5]],
  [[49, 8], [107, 8], [0, 6]],
  [[54, 5], [52, 9], [0, 7]],
  [[52, 6], [0, 8]],
  [[49, 10], [53, 11], [0, 9]],
  [[48, 12]],
  [[95, 13]],
  [[49, 13]],
  [[52, 9], [0, 13]]],
 [[[105, 1]], [[108, 2], [0, 1]], [[25, 3]], [[0, 3]]],
 [[[109, 1]], [[52, 0], [0, 1]]],
 [[[25, 1]], [[110, 0], [0, 1]]],
 [[[25, 1]], [[0, 1]]],
 [[[111, 1]], [[2, 1], [112, 2]], [[0, 2]]],
 [[[113, 1]],
  [[49, 2], [0, 1]],
  [[108, 3], [52, 3], [0, 2]],
  [[49, 4]],
  [[0, 4]]],
 [[[114, 1]], [[115, 0], [0, 1]]],
 [[[116, 1]],
  [[117, 2], [50, 3], [118, 4], [0, 1]],
  [[111, 4], [62, 4]],
  [[116, 5], [62, 5]],
  [[0, 4]],
  [[50, 3], [0, 5]]],
 [[[95, 1], [107, 1]], [[52, 2], [0, 1]], [[95, 1], [107, 1], [0, 2]]],
 [[[119, 2], [30, 1], [22, 1], [43, 1]], [[120, 2]], [[0, 2]]],
 [[[2, 0], [112, 1], [121, 0]], [[0, 1]]],
 [[[122, 1], [123, 1], [124, 1], [125, 1], [126, 1]], [[0, 1]]],
 [[[34, 1]],
  [[82, 2]],
  [[83, 3]],
  [[111, 4]],
  [[48, 5]],
  [[80, 6]],
  [[127, 7], [0, 6]],
  [[48, 8]],
  [[80, 9]],
  [[0, 9]]],
 [[[4, 1]],
  [[25, 2]],
  [[128, 3]],
  [[48, 4], [129, 5]],
  [[80, 6]],
  [[49, 7]],
  [[0, 6]],
  [[48, 4]]],
 [[[26, 1]], [[25, 2]], [[52, 1], [0, 2]]],
 [[[37, 1]],
  [[49, 2]],
  [[48, 3]],
  [[80, 4]],
  [[127, 5], [130, 1], [0, 4]],
  [[48, 6]],
  [[80, 7]],
  [[0, 7]]],
 [[[25, 1]], [[108, 2], [0, 1]], [[25, 3]], [[0, 3]]],
 [[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]],
 [[[36, 1]],
  [[105, 2], [19, 3], [110, 3]],
  [[32, 4]],
  [[105, 2], [19, 3], [32, 4], [110, 3]],
  [[132, 5], [15, 5], [35, 6]],
  [[0, 5]],
  [[132, 7]],
  [[61, 5]]],
 [[[32, 1]], [[133, 2]], [[0, 2]]],
 [[[134, 1], [135, 1]], [[0, 1]]],
 [[[14, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]],
 [[[14, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]],
 [[[18, 1]], [[25, 2]], [[52, 1], [0, 2]]],
 [[[8, 1], [137, 2]], [[46, 2]], [[0, 2]]],
 [[[138, 1]], [[139, 0], [0, 1]]],
 [[[35, 1]], [[61, 2], [140, 3]], [[0, 2]], [[61, 2]]],
 [[[28, 1]], [[0, 1]]],
 [[[141, 1]], [[53, 2], [0, 1]], [[120, 3]], [[0, 3]]],
 [[[16, 1]],
  [[49, 2], [142, 3], [0, 1]],
  [[52, 4], [0, 2]],
  [[49, 5]],
  [[49, 2], [0, 4]],
  [[52, 6], [0, 5]],
  [[49, 7]],
  [[52, 8], [0, 7]],
  [[49, 7], [0, 8]]],
 [[[5, 1]],
  [[49, 2], [0, 1]],
  [[36, 3], [52, 3], [0, 2]],
  [[49, 4]],
  [[52, 5], [0, 4]],
  [[49, 6]],
  [[0, 6]]],
 [[[23, 1]], [[111, 2], [0, 1]], [[0, 2]]],
 [[[143, 1]], [[144, 0], [142, 0], [0, 1]]],
 [[[145, 1]], [[2, 2], [146, 3]], [[0, 2]], [[145, 1], [2, 2]]],
 [[[48, 1]], [[49, 2], [0, 1]], [[0, 2]]],
 [[[147, 1],
   [148, 1],
   [149, 1],
   [150, 1],
   [151, 1],
   [152, 1],
   [153, 1],
   [154, 1],
   [155, 1],
   [156, 1]],
  [[0, 1]]],
 [[[15, 1]], [[95, 2]], [[0, 2]]],
 [[[1, 1], [3, 1]], [[0, 1]]],
 [[[49, 1], [48, 2]],
  [[48, 2], [0, 1]],
  [[49, 3], [157, 4], [0, 2]],
  [[157, 4], [0, 3]],
  [[0, 4]]],
 [[[158, 1]], [[52, 2], [0, 1]], [[158, 1], [0, 2]]],
 [[[1, 1], [2, 2]], [[0, 1]], [[159, 3]], [[121, 4]], [[160, 1], [121, 4]]],
 [[[120, 1]], [[161, 0], [15, 0], [162, 0], [41, 0], [163, 0], [0, 1]]],
 [[[84, 1], [164, 2]],
  [[37, 3], [0, 1]],
  [[0, 2]],
  [[84, 4]],
  [[127, 5]],
  [[49, 2]]],
 [[[165, 1], [84, 1]], [[0, 1]]],
 [[[49, 1]], [[52, 2], [0, 1]], [[49, 1], [0, 2]]],
 [[[49, 1], [107, 1]],
  [[54, 2], [52, 3], [0, 1]],
  [[0, 2]],
  [[49, 4], [107, 4], [0, 3]],
  [[52, 3], [0, 4]]],
 [[[49, 1], [107, 1]], [[52, 2], [0, 1]], [[49, 1], [107, 1], [0, 2]]],
 [[[25, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]],
 [[[35, 1], [110, 2], [38, 3]],
  [[61, 4], [81, 5]],
  [[25, 4]],
  [[166, 6]],
  [[0, 4]],
  [[61, 4]],
  [[64, 4]]],
 [[[20, 1]],
  [[48, 2]],
  [[80, 3]],
  [[167, 4], [168, 5]],
  [[48, 6]],
  [[48, 7]],
  [[80, 8]],
  [[80, 9]],
  [[167, 4], [127, 10], [168, 5], [0, 8]],
  [[0, 9]],
  [[48, 11]],
  [[80, 12]],
  [[168, 5], [0, 12]]],
 [[[15, 1], [169, 2], [53, 3]],
  [[169, 4], [52, 5], [0, 1]],
  [[50, 6], [52, 7], [0, 2]],
  [[169, 8]],
  [[52, 5], [0, 4]],
  [[169, 9], [53, 3], [0, 5]],
  [[49, 10]],
  [[15, 11], [169, 2], [53, 3], [0, 7]],
  [[52, 12], [0, 8]],
  [[50, 13], [52, 5], [0, 9]],
  [[52, 7], [0, 10]],
  [[169, 14], [52, 15], [0, 11]],
  [[0, 12]],
  [[49, 4]],
  [[52, 15], [0, 14]],
  [[169, 16], [53, 3], [0, 15]],
  [[50, 17], [52, 15], [0, 16]],
  [[49, 14]]],
 [[[15, 1], [53, 2], [170, 3]],
  [[170, 5], [52, 4], [0, 1]],
  [[170, 6]],
  [[50, 7], [52, 8], [0, 3]],
  [[53, 2], [170, 9], [0, 4]],
  [[52, 4], [0, 5]],
  [[52, 10], [0, 6]],
  [[49, 11]],
  [[15, 12], [53, 2], [170, 3], [0, 8]],
  [[50, 13], [52, 4], [0, 9]],
  [[0, 10]],
  [[52, 8], [0, 11]],
  [[52, 15], [170, 14], [0, 12]],
  [[49, 5]],
  [[52, 15], [0, 14]],
  [[53, 2], [170, 16], [0, 15]],
  [[50, 17], [52, 15], [0, 16]],
  [[49, 14]]],
 [[[25, 1]], [[0, 1]]],
 [[[21, 1]],
  [[49, 2]],
  [[48, 3]],
  [[80, 4]],
  [[127, 5], [0, 4]],
  [[48, 6]],
  [[80, 7]],
  [[0, 7]]],
 [[[49, 1]], [[108, 2], [0, 1]], [[95, 3]], [[0, 3]]],
 [[[42, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]],
 [[[172, 1]], [[173, 0], [0, 1]]],
 [[[111, 2], [36, 1]], [[49, 2]], [[0, 2]]],
 [[[31, 1]], [[174, 2], [0, 1]], [[0, 2]]],
 [[[62, 1]], [[0, 1]]]],
labels:
[[0, 'EMPTY'],
 [317, null],
 [4, null],
 [276, null],
 [1, 'def'],
 [1, 'raise'],
 [1, 'True'],
 [3, null],
 [1, 'not'],
 [1, 'null'],
 [55, null],
 [2, null],
 [25, null],
 [1, 'class'],
 [1, 'lambda'],
 [16, null],
 [1, 'print'],
 [1, 'debugger'],
 [1, 'nonlocal'],
 [52, null],
 [1, 'try'],
 [1, 'while'],
 [31, null],
 [1, 'return'],
 [1, 'assert'],
 [1, null],
 [1, 'global'],
 [1, 'del'],
 [1, 'pass'],
 [54, null],
 [15, null],
 [1, 'yield'],
 [1, 'import'],
 [1, 'False'],
 [1, 'for'],
 [7, null],
 [1, 'from'],
 [1, 'if'],
 [9, null],
 [1, 'break'],
 [1, 'continue'],
 [49, null],
 [1, 'with'],
 [14, null],
 [316, null],
 [19, null],
 [308, null],
 [1, 'and'],
 [11, null],
 [326, null],
 [22, null],
 [261, null],
 [12, null],
 [35, null],
 [271, null],
 [325, null],
 [297, null],
 [339, null],
 [296, null],
 [26, null],
 [283, null],
 [8, null],
 [342, null],
 [329, null],
 [10, null],
 [266, null],
 [332, null],
 [45, null],
 [38, null],
 [40, null],
 [50, null],
 [46, null],
 [41, null],
 [42, null],
 [36, null],
 [43, null],
 [48, null],
 [44, null],
 [37, null],
 [39, null],
 [324, null],
 [260, null],
 [292, null],
 [1, 'in'],
 [309, null],
 [273, null],
 [327, null],
 [272, null],
 [28, null],
 [21, null],
 [27, null],
 [29, null],
 [1, 'is'],
 [30, null],
 [20, null],
 [290, null],
 [274, null],
 [333, null],
 [299, null],
 [270, null],
 [337, null],
 [279, null],
 [265, null],
 [281, null],
 [264, null],
 [286, null],
 [280, null],
 [320, null],
 [1, 'as'],
 [284, null],
 [23, null],
 [328, null],
 [0, null],
 [1, 'except'],
 [340, null],
 [18, null],
 [330, null],
 [268, null],
 [259, null],
 [312, null],
 [293, null],
 [321, null],
 [269, null],
 [277, null],
 [314, null],
 [315, null],
 [343, null],
 [1, 'else'],
 [310, null],
 [51, null],
 [1, 'elif'],
 [300, null],
 [301, null],
 [285, null],
 [303, null],
 [302, null],
 [335, null],
 [275, null],
 [258, null],
 [1, 'or'],
 [334, null],
 [267, null],
 [34, null],
 [262, null],
 [33, null],
 [319, null],
 [13, null],
 [295, null],
 [263, null],
 [291, null],
 [311, null],
 [307, null],
 [313, null],
 [282, null],
 [298, null],
 [304, null],
 [278, null],
 [318, null],
 [322, null],
 [5, null],
 [6, null],
 [47, null],
 [17, null],
 [24, null],
 [305, null],
 [306, null],
 [323, null],
 [289, null],
 [1, 'finally'],
 [331, null],
 [336, null],
 [338, null],
 [257, null],
 [32, null],
 [341, null]],
keywords:
{'False': 33,
 'null': 9,
 'True': 6,
 'and': 47,
 'as': 108,
 'assert': 24,
 'break': 39,
 'class': 13,
 'continue': 40,
 'debugger': 17,
 'def': 4,
 'del': 27,
 'elif': 130,
 'else': 127,
 'except': 113,
 'finally': 168,
 'for': 34,
 'from': 36,
 'global': 26,
 'if': 37,
 'import': 32,
 'in': 83,
 'is': 92,
 'lambda': 14,
 'nonlocal': 18,
 'not': 8,
 'or': 139,
 'pass': 28,
 'print': 16,
 'raise': 5,
 'return': 23,
 'try': 20,
 'while': 21,
 'with': 42,
 'yield': 31},
tokens:
{0: 112,
 1: 25,
 2: 11,
 3: 7,
 4: 2,
 5: 159,
 6: 160,
 7: 35,
 8: 61,
 9: 38,
 10: 64,
 11: 48,
 12: 52,
 13: 146,
 14: 43,
 15: 30,
 16: 15,
 17: 162,
 18: 115,
 19: 45,
 20: 94,
 21: 89,
 22: 50,
 23: 110,
 24: 163,
 25: 12,
 26: 59,
 27: 90,
 28: 88,
 29: 91,
 30: 93,
 31: 22,
 32: 173,
 33: 144,
 34: 142,
 35: 53,
 36: 74,
 37: 78,
 38: 68,
 39: 79,
 40: 69,
 41: 72,
 42: 73,
 43: 75,
 44: 77,
 45: 67,
 46: 71,
 47: 161,
 48: 76,
 49: 41,
 50: 70,
 51: 129,
 52: 19,
 54: 29,
 55: 10},
start: 256
};


/***/ }),

/***/ "./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js":
/*!**************************************************************************************!*\
  !*** ./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js ***!
  \**************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(global) {(function(r){function x(){}function y(){}var z=String.fromCharCode,v={}.toString,A=v.call(r.SharedArrayBuffer),B=v(),q=r.Uint8Array,t=q||Array,w=q?ArrayBuffer:t,C=w.isView||function(g){return g&&"length"in g},D=v.call(w.prototype);w=y.prototype;var E=r.TextEncoder,a=new (q?Uint16Array:t)(32);x.prototype.decode=function(g){if(!C(g)){var l=v.call(g);if(l!==D&&l!==A&&l!==B)throw TypeError("Failed to execute 'decode' on 'TextDecoder': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'");
g=q?new t(g):g||[]}for(var f=l="",b=0,c=g.length|0,u=c-32|0,e,d,h=0,p=0,m,k=0,n=-1;b<c;){for(e=b<=u?32:c-b|0;k<e;b=b+1|0,k=k+1|0){d=g[b]&255;switch(d>>4){case 15:m=g[b=b+1|0]&255;if(2!==m>>6||247<d){b=b-1|0;break}h=(d&7)<<6|m&63;p=5;d=256;case 14:m=g[b=b+1|0]&255,h<<=6,h|=(d&15)<<6|m&63,p=2===m>>6?p+4|0:24,d=d+256&768;case 13:case 12:m=g[b=b+1|0]&255,h<<=6,h|=(d&31)<<6|m&63,p=p+7|0,b<c&&2===m>>6&&h>>p&&1114112>h?(d=h,h=h-65536|0,0<=h&&(n=(h>>10)+55296|0,d=(h&1023)+56320|0,31>k?(a[k]=n,k=k+1|0,n=-1):
(m=n,n=d,d=m))):(d>>=8,b=b-d-1|0,d=65533),h=p=0,e=b<=u?32:c-b|0;default:a[k]=d;continue;case 11:case 10:case 9:case 8:}a[k]=65533}f+=z(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15],a[16],a[17],a[18],a[19],a[20],a[21],a[22],a[23],a[24],a[25],a[26],a[27],a[28],a[29],a[30],a[31]);32>k&&(f=f.slice(0,k-32|0));if(b<c){if(a[0]=n,k=~n>>>31,n=-1,f.length<l.length)continue}else-1!==n&&(f+=z(n));l+=f;f=""}return l};w.encode=function(g){g=void 0===g?"":""+g;var l=g.length|
0,f=new t((l<<1)+8|0),b,c=0,u=!q;for(b=0;b<l;b=b+1|0,c=c+1|0){var e=g.charCodeAt(b)|0;if(127>=e)f[c]=e;else{if(2047>=e)f[c]=192|e>>6;else{a:{if(55296<=e)if(56319>=e){var d=g.charCodeAt(b=b+1|0)|0;if(56320<=d&&57343>=d){e=(e<<10)+d-56613888|0;if(65535<e){f[c]=240|e>>18;f[c=c+1|0]=128|e>>12&63;f[c=c+1|0]=128|e>>6&63;f[c=c+1|0]=128|e&63;continue}break a}e=65533}else 57343>=e&&(e=65533);!u&&b<<1<c&&b<<1<(c-7|0)&&(u=!0,d=new t(3*l),d.set(f),f=d)}f[c]=224|e>>12;f[c=c+1|0]=128|e>>6&63}f[c=c+1|0]=128|e&63}}return q?
f.subarray(0,c):f.slice(0,c)};E||(r.TextDecoder=x,r.TextEncoder=y)})(""+void 0==typeof global?""+void 0==typeof self?this:self:global);//AnonyCo
//# sourceMappingURL=https://cdn.jsdelivr.net/gh/AnonyCo/FastestSmallestTextEncoderDecoder/EncoderDecoderTogether.min.js.map

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/jsbi/dist/jsbi-umd.js":
/*!********************************************!*\
  !*** ./node_modules/jsbi/dist/jsbi-umd.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

(function(e,t){ true?module.exports=t():undefined})(this,function(){'use strict';var v=Math.imul,f=Math.clz32;function e(t){"@babel/helpers - typeof";return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var _,n=0;n<t.length;n++)_=t[n],_.enumerable=_.enumerable||!1,_.configurable=!0,"value"in _&&(_.writable=!0),Object.defineProperty(e,_.key,_)}function _(e,t,_){return t&&i(e.prototype,t),_&&i(e,_),e}function n(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&g(e,t)}function l(e){return l=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},l(e)}function g(e,t){return g=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},g(e,t)}function o(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}function a(){return a=o()?Reflect.construct:function(e,t,i){var _=[null];_.push.apply(_,t);var n=Function.bind.apply(e,_),l=new n;return i&&g(l,i.prototype),l},a.apply(null,arguments)}function s(e){return-1!==Function.toString.call(e).indexOf("[native code]")}function u(e){var t="function"==typeof Map?new Map:void 0;return u=function(e){function i(){return a(e,arguments,l(this).constructor)}if(null===e||!s(e))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if("undefined"!=typeof t){if(t.has(e))return t.get(e);t.set(e,i)}return i.prototype=Object.create(e.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),g(i,e)},u(e)}function r(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function d(e,t){return t&&("object"==typeof t||"function"==typeof t)?t:r(e)}function h(e){var t=o();return function(){var i,_=l(e);if(t){var n=l(this).constructor;i=Reflect.construct(_,arguments,n)}else i=_.apply(this,arguments);return d(this,i)}}function b(e,t){if(e){if("string"==typeof e)return m(e,t);var i=Object.prototype.toString.call(e).slice(8,-1);return"Object"===i&&e.constructor&&(i=e.constructor.name),"Map"===i||"Set"===i?Array.from(e):"Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?m(e,t):void 0}}function m(e,t){(null==t||t>e.length)&&(t=e.length);for(var _=0,n=Array(t);_<t;_++)n[_]=e[_];return n}function c(e,t){var _;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(_=b(e))||t&&e&&"number"==typeof e.length){_&&(e=_);var n=0,l=function(){};return{s:l,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(t){throw t},f:l}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var g,a=!0,s=!1;return{s:function(){_=e[Symbol.iterator]()},n:function(){var e=_.next();return a=e.done,e},e:function(t){s=!0,g=t},f:function(){try{a||null==_.return||_.return()}finally{if(s)throw g}}}}var y=function(i){var g=Math.abs,o=Math.max;function l(e,i){var _;if(t(this,l),e>l.__kMaxLength)throw new RangeError("Maximum BigInt size exceeded");return _=a.call(this,e),_.sign=i,_}n(l,i);var a=h(l);return _(l,[{key:"toDebugString",value:function(){var e,t=["BigInt["],i=c(this);try{for(i.s();!(e=i.n()).done;){var _=e.value;t.push((_?(_>>>0).toString(16):_)+", ")}}catch(e){i.e(e)}finally{i.f()}return t.push("]"),t.join("")}},{key:"toString",value:function(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:10;if(2>e||36<e)throw new RangeError("toString() radix argument must be between 2 and 36");return 0===this.length?"0":0==(e&e-1)?l.__toStringBasePowerOfTwo(this,e):l.__toStringGeneric(this,e,!1)}},{key:"__copy",value:function(){for(var e=new l(this.length,this.sign),t=0;t<this.length;t++)e[t]=this[t];return e}},{key:"__trim",value:function(){for(var e=this.length,t=this[e-1];0===t;)e--,t=this[e-1],this.pop();return 0===e&&(this.sign=!1),this}},{key:"__initializeDigits",value:function(){for(var e=0;e<this.length;e++)this[e]=0}},{key:"__clzmsd",value:function(){return l.__clz32(this[this.length-1])}},{key:"__inplaceMultiplyAdd",value:function(e,t,_){_>this.length&&(_=this.length);for(var n=65535&e,g=e>>>16,o=0,a=65535&t,s=t>>>16,u=0;u<_;u++){var r=this.__digit(u),d=65535&r,h=r>>>16,b=l.__imul(d,n),m=l.__imul(d,g),c=l.__imul(h,n),v=l.__imul(h,g),f=a+(65535&b),y=s+o+(f>>>16)+(b>>>16)+(65535&m)+(65535&c);a=(m>>>16)+(c>>>16)+(65535&v)+(y>>>16),o=a>>>16,a&=65535,s=v>>>16;this.__setDigit(u,65535&f|y<<16)}if(0!==o||0!==a||0!==s)throw new Error("implementation bug")}},{key:"__inplaceAdd",value:function(e,t,_){for(var n,l=0,g=0;g<_;g++)n=this.__halfDigit(t+g)+e.__halfDigit(g)+l,l=n>>>16,this.__setHalfDigit(t+g,n);return l}},{key:"__inplaceSub",value:function(e,t,_){var n=0;if(1&t){t>>=1;for(var l=this.__digit(t),g=65535&l,o=0;o<_-1>>>1;o++){var a=e.__digit(o),s=(l>>>16)-(65535&a)-n;n=1&s>>>16,this.__setDigit(t+o,s<<16|65535&g),l=this.__digit(t+o+1),g=(65535&l)-(a>>>16)-n,n=1&g>>>16}var u=e.__digit(o),r=(l>>>16)-(65535&u)-n;n=1&r>>>16,this.__setDigit(t+o,r<<16|65535&g);if(t+o+1>=this.length)throw new RangeError("out of bounds");0==(1&_)&&(l=this.__digit(t+o+1),g=(65535&l)-(u>>>16)-n,n=1&g>>>16,this.__setDigit(t+e.length,4294901760&l|65535&g))}else{t>>=1;for(var d=0;d<e.length-1;d++){var h=this.__digit(t+d),b=e.__digit(d),m=(65535&h)-(65535&b)-n;n=1&m>>>16;var c=(h>>>16)-(b>>>16)-n;n=1&c>>>16,this.__setDigit(t+d,c<<16|65535&m)}var v=this.__digit(t+d),f=e.__digit(d),y=(65535&v)-(65535&f)-n;n=1&y>>>16;var k=0;0==(1&_)&&(k=(v>>>16)-(f>>>16)-n,n=1&k>>>16),this.__setDigit(t+d,k<<16|65535&y)}return n}},{key:"__inplaceRightShift",value:function(e){if(0!==e){for(var t,_=this.__digit(0)>>>e,n=this.length-1,l=0;l<n;l++)t=this.__digit(l+1),this.__setDigit(l,t<<32-e|_),_=t>>>e;this.__setDigit(n,_)}}},{key:"__digit",value:function(e){return this[e]}},{key:"__unsignedDigit",value:function(e){return this[e]>>>0}},{key:"__setDigit",value:function(e,t){this[e]=0|t}},{key:"__setDigitGrow",value:function(e,t){this[e]=0|t}},{key:"__halfDigitLength",value:function(){var e=this.length;return 65535>=this.__unsignedDigit(e-1)?2*e-1:2*e}},{key:"__halfDigit",value:function(e){return 65535&this[e>>>1]>>>((1&e)<<4)}},{key:"__setHalfDigit",value:function(e,t){var i=e>>>1,_=this.__digit(i),n=1&e?65535&_|t<<16:4294901760&_|65535&t;this.__setDigit(i,n)}}],[{key:"BigInt",value:function(t){var i=Math.floor,_=Number.isFinite;if("number"==typeof t){if(0===t)return l.__zero();if((0|t)===t)return 0>t?l.__oneDigit(-t,!0):l.__oneDigit(t,!1);if(!_(t)||i(t)!==t)throw new RangeError("The number "+t+" cannot be converted to BigInt because it is not an integer");return l.__fromDouble(t)}if("string"==typeof t){var n=l.__fromString(t);if(null===n)throw new SyntaxError("Cannot convert "+t+" to a BigInt");return n}if("boolean"==typeof t)return!0===t?l.__oneDigit(1,!1):l.__zero();if("object"===e(t)){if(t.constructor===l)return t;var g=l.__toPrimitive(t);return l.BigInt(g)}throw new TypeError("Cannot convert "+t+" to a BigInt")}},{key:"toNumber",value:function(e){var t=e.length;if(0===t)return 0;if(1===t){var i=e.__unsignedDigit(0);return e.sign?-i:i}var _=e.__digit(t-1),n=l.__clz32(_),g=32*t-n;if(1024<g)return e.sign?-Infinity:1/0;var o=g-1,a=_,s=t-1,u=n+1,r=32===u?0:a<<u;r>>>=12;var d=u-12,h=12<=u?0:a<<20+u,b=20+u;0<d&&0<s&&(s--,a=e.__digit(s),r|=a>>>32-d,h=a<<d,b=d),0<b&&0<s&&(s--,a=e.__digit(s),h|=a>>>32-b,b-=32);var m=l.__decideRounding(e,b,s,a);if((1===m||0===m&&1==(1&h))&&(h=h+1>>>0,0===h&&(r++,0!=r>>>20&&(r=0,o++,1023<o))))return e.sign?-Infinity:1/0;var c=e.sign?-2147483648:0;return o=o+1023<<20,l.__kBitConversionInts[1]=c|o|r,l.__kBitConversionInts[0]=h,l.__kBitConversionDouble[0]}},{key:"unaryMinus",value:function(e){if(0===e.length)return e;var t=e.__copy();return t.sign=!e.sign,t}},{key:"bitwiseNot",value:function(e){return e.sign?l.__absoluteSubOne(e).__trim():l.__absoluteAddOne(e,!0)}},{key:"exponentiate",value:function(e,t){if(t.sign)throw new RangeError("Exponent must be positive");if(0===t.length)return l.__oneDigit(1,!1);if(0===e.length)return e;if(1===e.length&&1===e.__digit(0))return e.sign&&0==(1&t.__digit(0))?l.unaryMinus(e):e;if(1<t.length)throw new RangeError("BigInt too big");var i=t.__unsignedDigit(0);if(1===i)return e;if(i>=l.__kMaxLengthBits)throw new RangeError("BigInt too big");if(1===e.length&&2===e.__digit(0)){var _=1+(i>>>5),n=e.sign&&0!=(1&i),g=new l(_,n);g.__initializeDigits();var o=1<<(31&i);return g.__setDigit(_-1,o),g}var a=null,s=e;for(0!=(1&i)&&(a=e),i>>=1;0!==i;i>>=1)s=l.multiply(s,s),0!=(1&i)&&(null===a?a=s:a=l.multiply(a,s));return a}},{key:"multiply",value:function(e,t){if(0===e.length)return e;if(0===t.length)return t;var _=e.length+t.length;32<=e.__clzmsd()+t.__clzmsd()&&_--;var n=new l(_,e.sign!==t.sign);n.__initializeDigits();for(var g=0;g<e.length;g++)l.__multiplyAccumulate(t,e.__digit(g),n,g);return n.__trim()}},{key:"divide",value:function(e,t){if(0===t.length)throw new RangeError("Division by zero");if(0>l.__absoluteCompare(e,t))return l.__zero();var i,_=e.sign!==t.sign,n=t.__unsignedDigit(0);if(1===t.length&&65535>=n){if(1===n)return _===e.sign?e:l.unaryMinus(e);i=l.__absoluteDivSmall(e,n,null)}else i=l.__absoluteDivLarge(e,t,!0,!1);return i.sign=_,i.__trim()}},{key:"remainder",value:function e(t,i){if(0===i.length)throw new RangeError("Division by zero");if(0>l.__absoluteCompare(t,i))return t;var _=i.__unsignedDigit(0);if(1===i.length&&65535>=_){if(1===_)return l.__zero();var n=l.__absoluteModSmall(t,_);return 0===n?l.__zero():l.__oneDigit(n,t.sign)}var e=l.__absoluteDivLarge(t,i,!1,!0);return e.sign=t.sign,e.__trim()}},{key:"add",value:function(e,t){var i=e.sign;return i===t.sign?l.__absoluteAdd(e,t,i):0<=l.__absoluteCompare(e,t)?l.__absoluteSub(e,t,i):l.__absoluteSub(t,e,!i)}},{key:"subtract",value:function(e,t){var i=e.sign;return i===t.sign?0<=l.__absoluteCompare(e,t)?l.__absoluteSub(e,t,i):l.__absoluteSub(t,e,!i):l.__absoluteAdd(e,t,i)}},{key:"leftShift",value:function(e,t){return 0===t.length||0===e.length?e:t.sign?l.__rightShiftByAbsolute(e,t):l.__leftShiftByAbsolute(e,t)}},{key:"signedRightShift",value:function(e,t){return 0===t.length||0===e.length?e:t.sign?l.__leftShiftByAbsolute(e,t):l.__rightShiftByAbsolute(e,t)}},{key:"unsignedRightShift",value:function(){throw new TypeError("BigInts have no unsigned right shift; use >> instead")}},{key:"lessThan",value:function(e,t){return 0>l.__compareToBigInt(e,t)}},{key:"lessThanOrEqual",value:function(e,t){return 0>=l.__compareToBigInt(e,t)}},{key:"greaterThan",value:function(e,t){return 0<l.__compareToBigInt(e,t)}},{key:"greaterThanOrEqual",value:function(e,t){return 0<=l.__compareToBigInt(e,t)}},{key:"equal",value:function(e,t){if(e.sign!==t.sign)return!1;if(e.length!==t.length)return!1;for(var _=0;_<e.length;_++)if(e.__digit(_)!==t.__digit(_))return!1;return!0}},{key:"notEqual",value:function(e,t){return!l.equal(e,t)}},{key:"bitwiseAnd",value:function(e,t){if(!e.sign&&!t.sign)return l.__absoluteAnd(e,t).__trim();if(e.sign&&t.sign){var i=o(e.length,t.length)+1,_=l.__absoluteSubOne(e,i),n=l.__absoluteSubOne(t);return _=l.__absoluteOr(_,n,_),l.__absoluteAddOne(_,!0,_).__trim()}if(e.sign){var g=[t,e];e=g[0],t=g[1]}return l.__absoluteAndNot(e,l.__absoluteSubOne(t)).__trim()}},{key:"bitwiseXor",value:function(e,t){if(!e.sign&&!t.sign)return l.__absoluteXor(e,t).__trim();if(e.sign&&t.sign){var i=o(e.length,t.length),_=l.__absoluteSubOne(e,i),n=l.__absoluteSubOne(t);return l.__absoluteXor(_,n,_).__trim()}var g=o(e.length,t.length)+1;if(e.sign){var a=[t,e];e=a[0],t=a[1]}var s=l.__absoluteSubOne(t,g);return s=l.__absoluteXor(s,e,s),l.__absoluteAddOne(s,!0,s).__trim()}},{key:"bitwiseOr",value:function(e,t){var i=o(e.length,t.length);if(!e.sign&&!t.sign)return l.__absoluteOr(e,t).__trim();if(e.sign&&t.sign){var _=l.__absoluteSubOne(e,i),n=l.__absoluteSubOne(t);return _=l.__absoluteAnd(_,n,_),l.__absoluteAddOne(_,!0,_).__trim()}if(e.sign){var g=[t,e];e=g[0],t=g[1]}var a=l.__absoluteSubOne(t,i);return a=l.__absoluteAndNot(a,e,a),l.__absoluteAddOne(a,!0,a).__trim()}},{key:"asIntN",value:function(e,t){if(0===t.length)return t;if(0===e)return l.__zero();if(e>=l.__kMaxLengthBits)return t;var _=e+31>>>5;if(t.length<_)return t;var n=t.__unsignedDigit(_-1),g=1<<(31&e-1);if(t.length===_&&n<g)return t;if(!((n&g)===g))return l.__truncateToNBits(e,t);if(!t.sign)return l.__truncateAndSubFromPowerOfTwo(e,t,!0);if(0==(n&g-1)){for(var o=_-2;0<=o;o--)if(0!==t.__digit(o))return l.__truncateAndSubFromPowerOfTwo(e,t,!1);return t.length===_&&n===g?t:l.__truncateToNBits(e,t)}return l.__truncateAndSubFromPowerOfTwo(e,t,!1)}},{key:"asUintN",value:function(e,t){if(0===t.length)return t;if(0===e)return l.__zero();if(t.sign){if(e>l.__kMaxLengthBits)throw new RangeError("BigInt too big");return l.__truncateAndSubFromPowerOfTwo(e,t,!1)}if(e>=l.__kMaxLengthBits)return t;var i=e+31>>>5;if(t.length<i)return t;var _=31&e;if(t.length==i){if(0===_)return t;var n=t.__digit(i-1);if(0==n>>>_)return t}return l.__truncateToNBits(e,t)}},{key:"ADD",value:function(e,t){if(e=l.__toPrimitive(e),t=l.__toPrimitive(t),"string"==typeof e)return"string"!=typeof t&&(t=t.toString()),e+t;if("string"==typeof t)return e.toString()+t;if(e=l.__toNumeric(e),t=l.__toNumeric(t),l.__isBigInt(e)&&l.__isBigInt(t))return l.add(e,t);if("number"==typeof e&&"number"==typeof t)return e+t;throw new TypeError("Cannot mix BigInt and other types, use explicit conversions")}},{key:"LT",value:function(e,t){return l.__compare(e,t,0)}},{key:"LE",value:function(e,t){return l.__compare(e,t,1)}},{key:"GT",value:function(e,t){return l.__compare(e,t,2)}},{key:"GE",value:function(e,t){return l.__compare(e,t,3)}},{key:"EQ",value:function(t,i){for(;;){if(l.__isBigInt(t))return l.__isBigInt(i)?l.equal(t,i):l.EQ(i,t);if("number"==typeof t){if(l.__isBigInt(i))return l.__equalToNumber(i,t);if("object"!==e(i))return t==i;i=l.__toPrimitive(i)}else if("string"==typeof t){if(l.__isBigInt(i))return t=l.__fromString(t),null!==t&&l.equal(t,i);if("object"!==e(i))return t==i;i=l.__toPrimitive(i)}else if("boolean"==typeof t){if(l.__isBigInt(i))return l.__equalToNumber(i,+t);if("object"!==e(i))return t==i;i=l.__toPrimitive(i)}else if("symbol"===e(t)){if(l.__isBigInt(i))return!1;if("object"!==e(i))return t==i;i=l.__toPrimitive(i)}else if("object"===e(t)){if("object"===e(i)&&i.constructor!==l)return t==i;t=l.__toPrimitive(t)}else return t==i}}},{key:"NE",value:function(e,t){return!l.EQ(e,t)}},{key:"__zero",value:function(){return new l(0,!1)}},{key:"__oneDigit",value:function(e,t){var i=new l(1,t);return i.__setDigit(0,e),i}},{key:"__decideRounding",value:function(e,t,i,_){if(0<t)return-1;var n;if(0>t)n=-t-1;else{if(0===i)return-1;i--,_=e.__digit(i),n=31}var l=1<<n;if(0==(_&l))return-1;if(l-=1,0!=(_&l))return 1;for(;0<i;)if(i--,0!==e.__digit(i))return 1;return 0}},{key:"__fromDouble",value:function(e){l.__kBitConversionDouble[0]=e;var t,i=2047&l.__kBitConversionInts[1]>>>20,_=i-1023,n=(_>>>5)+1,g=new l(n,0>e),o=1048575&l.__kBitConversionInts[1]|1048576,a=l.__kBitConversionInts[0],s=20,u=31&_,r=0;if(u<s){var d=s-u;r=d+32,t=o>>>d,o=o<<32-d|a>>>d,a<<=32-d}else if(u===s)r=32,t=o,o=a;else{var h=u-s;r=32-h,t=o<<h|a>>>32-h,o=a<<h}g.__setDigit(n-1,t);for(var b=n-2;0<=b;b--)0<r?(r-=32,t=o,o=a):t=0,g.__setDigit(b,t);return g.__trim()}},{key:"__isWhitespace",value:function(e){return!!(13>=e&&9<=e)||(159>=e?32==e:131071>=e?160==e||5760==e:196607>=e?(e&=131071,10>=e||40==e||41==e||47==e||95==e||4096==e):65279==e)}},{key:"__fromString",value:function(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:0,i=0,_=e.length,n=0;if(n===_)return l.__zero();for(var g=e.charCodeAt(n);l.__isWhitespace(g);){if(++n===_)return l.__zero();g=e.charCodeAt(n)}if(43===g){if(++n===_)return null;g=e.charCodeAt(n),i=1}else if(45===g){if(++n===_)return null;g=e.charCodeAt(n),i=-1}if(0===t){if(t=10,48===g){if(++n===_)return l.__zero();if(g=e.charCodeAt(n),88===g||120===g){if(t=16,++n===_)return null;g=e.charCodeAt(n)}else if(79===g||111===g){if(t=8,++n===_)return null;g=e.charCodeAt(n)}else if(66===g||98===g){if(t=2,++n===_)return null;g=e.charCodeAt(n)}}}else if(16===t&&48===g){if(++n===_)return l.__zero();if(g=e.charCodeAt(n),88===g||120===g){if(++n===_)return null;g=e.charCodeAt(n)}}for(;48===g;){if(++n===_)return l.__zero();g=e.charCodeAt(n)}var o=_-n,a=l.__kMaxBitsPerChar[t],s=l.__kBitsPerCharTableMultiplier-1;if(o>1073741824/a)return null;var u=a*o+s>>>l.__kBitsPerCharTableShift,r=new l(u+31>>>5,!1),h=10>t?t:10,b=10<t?t-10:0;if(0==(t&t-1)){a>>=l.__kBitsPerCharTableShift;var c=[],v=[],f=!1;do{for(var y,k=0,D=0;;){if(y=void 0,g-48>>>0<h)y=g-48;else if((32|g)-97>>>0<b)y=(32|g)-87;else{f=!0;break}if(D+=a,k=k<<a|y,++n===_){f=!0;break}if(g=e.charCodeAt(n),32<D+a)break}c.push(k),v.push(D)}while(!f);l.__fillFromParts(r,c,v)}else{r.__initializeDigits();var p=!1,B=0;do{for(var S,C=0,A=1;;){if(S=void 0,g-48>>>0<h)S=g-48;else if((32|g)-97>>>0<b)S=(32|g)-87;else{p=!0;break}var T=A*t;if(4294967295<T)break;if(A=T,C=C*t+S,B++,++n===_){p=!0;break}g=e.charCodeAt(n)}s=32*l.__kBitsPerCharTableMultiplier-1;var m=a*B+s>>>l.__kBitsPerCharTableShift+5;r.__inplaceMultiplyAdd(A,C,m)}while(!p)}if(n!==_){if(!l.__isWhitespace(g))return null;for(n++;n<_;n++)if(g=e.charCodeAt(n),!l.__isWhitespace(g))return null}return 0!==i&&10!==t?null:(r.sign=-1===i,r.__trim())}},{key:"__fillFromParts",value:function(e,t,_){for(var n=0,l=0,g=0,o=t.length-1;0<=o;o--){var a=t[o],s=_[o];l|=a<<g,g+=s,32===g?(e.__setDigit(n++,l),g=0,l=0):32<g&&(e.__setDigit(n++,l),g-=32,l=a>>>s-g)}if(0!==l){if(n>=e.length)throw new Error("implementation bug");e.__setDigit(n++,l)}for(;n<e.length;n++)e.__setDigit(n,0)}},{key:"__toStringBasePowerOfTwo",value:function(e,t){var _=e.length,n=t-1;n=(85&n>>>1)+(85&n),n=(51&n>>>2)+(51&n),n=(15&n>>>4)+(15&n);var g=n,o=t-1,a=e.__digit(_-1),s=l.__clz32(a),u=0|(32*_-s+g-1)/g;if(e.sign&&u++,268435456<u)throw new Error("string too long");for(var r=Array(u),d=u-1,h=0,b=0,m=0;m<_-1;m++){var c=e.__digit(m),v=(h|c<<b)&o;r[d--]=l.__kConversionChars[v];var f=g-b;for(h=c>>>f,b=32-f;b>=g;)r[d--]=l.__kConversionChars[h&o],h>>>=g,b-=g}var y=(h|a<<b)&o;for(r[d--]=l.__kConversionChars[y],h=a>>>g-b;0!==h;)r[d--]=l.__kConversionChars[h&o],h>>>=g;if(e.sign&&(r[d--]="-"),-1!==d)throw new Error("implementation bug");return r.join("")}},{key:"__toStringGeneric",value:function(e,t,_){var n=e.length;if(0===n)return"";if(1===n){var g=e.__unsignedDigit(0).toString(t);return!1===_&&e.sign&&(g="-"+g),g}var o=32*n-l.__clz32(e.__digit(n-1)),a=l.__kMaxBitsPerChar[t],s=a-1,u=o*l.__kBitsPerCharTableMultiplier;u+=s-1,u=0|u/s;var r,d,h=u+1>>1,b=l.exponentiate(l.__oneDigit(t,!1),l.__oneDigit(h,!1)),m=b.__unsignedDigit(0);if(1===b.length&&65535>=m){r=new l(e.length,!1),r.__initializeDigits();for(var c,v=0,f=2*e.length-1;0<=f;f--)c=v<<16|e.__halfDigit(f),r.__setHalfDigit(f,0|c/m),v=0|c%m;d=v.toString(t)}else{var y=l.__absoluteDivLarge(e,b,!0,!0);r=y.quotient;var k=y.remainder.__trim();d=l.__toStringGeneric(k,t,!0)}r.__trim();for(var D=l.__toStringGeneric(r,t,!0);d.length<h;)d="0"+d;return!1===_&&e.sign&&(D="-"+D),D+d}},{key:"__unequalSign",value:function(e){return e?-1:1}},{key:"__absoluteGreater",value:function(e){return e?-1:1}},{key:"__absoluteLess",value:function(e){return e?1:-1}},{key:"__compareToBigInt",value:function(e,t){var i=e.sign;if(i!==t.sign)return l.__unequalSign(i);var _=l.__absoluteCompare(e,t);return 0<_?l.__absoluteGreater(i):0>_?l.__absoluteLess(i):0}},{key:"__compareToNumber",value:function(e,t){if(!0|t){var i=e.sign,_=0>t;if(i!==_)return l.__unequalSign(i);if(0===e.length){if(_)throw new Error("implementation bug");return 0===t?0:-1}if(1<e.length)return l.__absoluteGreater(i);var n=g(t),o=e.__unsignedDigit(0);return o>n?l.__absoluteGreater(i):o<n?l.__absoluteLess(i):0}return l.__compareToDouble(e,t)}},{key:"__compareToDouble",value:function(e,t){if(t!==t)return t;if(t===1/0)return-1;if(t===-Infinity)return 1;var i=e.sign;if(i!==0>t)return l.__unequalSign(i);if(0===t)throw new Error("implementation bug: should be handled elsewhere");if(0===e.length)return-1;l.__kBitConversionDouble[0]=t;var _=2047&l.__kBitConversionInts[1]>>>20;if(2047==_)throw new Error("implementation bug: handled elsewhere");var n=_-1023;if(0>n)return l.__absoluteGreater(i);var g=e.length,o=e.__digit(g-1),a=l.__clz32(o),s=32*g-a,u=n+1;if(s<u)return l.__absoluteLess(i);if(s>u)return l.__absoluteGreater(i);var r=1048576|1048575&l.__kBitConversionInts[1],d=l.__kBitConversionInts[0],h=20,b=31-a;if(b!==(s-1)%31)throw new Error("implementation bug");var m,c=0;if(b<h){var v=h-b;c=v+32,m=r>>>v,r=r<<32-v|d>>>v,d<<=32-v}else if(b===h)c=32,m=r,r=d;else{var f=b-h;c=32-f,m=r<<f|d>>>32-f,r=d<<f}if(o>>>=0,m>>>=0,o>m)return l.__absoluteGreater(i);if(o<m)return l.__absoluteLess(i);for(var y=g-2;0<=y;y--){0<c?(c-=32,m=r>>>0,r=d,d=0):m=0;var k=e.__unsignedDigit(y);if(k>m)return l.__absoluteGreater(i);if(k<m)return l.__absoluteLess(i)}if(0!==r||0!==d){if(0===c)throw new Error("implementation bug");return l.__absoluteLess(i)}return 0}},{key:"__equalToNumber",value:function(e,t){return t|0===t?0===t?0===e.length:1===e.length&&e.sign===0>t&&e.__unsignedDigit(0)===g(t):0===l.__compareToDouble(e,t)}},{key:"__comparisonResultToBool",value:function(e,t){switch(t){case 0:return 0>e;case 1:return 0>=e;case 2:return 0<e;case 3:return 0<=e;}throw new Error("unreachable")}},{key:"__compare",value:function(e,t,i){if(e=l.__toPrimitive(e),t=l.__toPrimitive(t),"string"==typeof e&&"string"==typeof t)switch(i){case 0:return e<t;case 1:return e<=t;case 2:return e>t;case 3:return e>=t;}if(l.__isBigInt(e)&&"string"==typeof t)return t=l.__fromString(t),null!==t&&l.__comparisonResultToBool(l.__compareToBigInt(e,t),i);if("string"==typeof e&&l.__isBigInt(t))return e=l.__fromString(e),null!==e&&l.__comparisonResultToBool(l.__compareToBigInt(e,t),i);if(e=l.__toNumeric(e),t=l.__toNumeric(t),l.__isBigInt(e)){if(l.__isBigInt(t))return l.__comparisonResultToBool(l.__compareToBigInt(e,t),i);if("number"!=typeof t)throw new Error("implementation bug");return l.__comparisonResultToBool(l.__compareToNumber(e,t),i)}if("number"!=typeof e)throw new Error("implementation bug");if(l.__isBigInt(t))return l.__comparisonResultToBool(l.__compareToNumber(t,e),2^i);if("number"!=typeof t)throw new Error("implementation bug");return 0===i?e<t:1===i?e<=t:2===i?e>t:3===i?e>=t:void 0}},{key:"__absoluteAdd",value:function(e,t,_){if(e.length<t.length)return l.__absoluteAdd(t,e,_);if(0===e.length)return e;if(0===t.length)return e.sign===_?e:l.unaryMinus(e);var n=e.length;(0===e.__clzmsd()||t.length===e.length&&0===t.__clzmsd())&&n++;for(var g=new l(n,_),o=0,a=0;a<t.length;a++){var s=t.__digit(a),u=e.__digit(a),r=(65535&u)+(65535&s)+o,d=(u>>>16)+(s>>>16)+(r>>>16);o=d>>>16,g.__setDigit(a,65535&r|d<<16)}for(;a<e.length;a++){var h=e.__digit(a),b=(65535&h)+o,m=(h>>>16)+(b>>>16);o=m>>>16,g.__setDigit(a,65535&b|m<<16)}return a<g.length&&g.__setDigit(a,o),g.__trim()}},{key:"__absoluteSub",value:function(e,t,_){if(0===e.length)return e;if(0===t.length)return e.sign===_?e:l.unaryMinus(e);for(var n=new l(e.length,_),g=0,o=0;o<t.length;o++){var a=e.__digit(o),s=t.__digit(o),u=(65535&a)-(65535&s)-g;g=1&u>>>16;var r=(a>>>16)-(s>>>16)-g;g=1&r>>>16,n.__setDigit(o,65535&u|r<<16)}for(;o<e.length;o++){var d=e.__digit(o),h=(65535&d)-g;g=1&h>>>16;var b=(d>>>16)-g;g=1&b>>>16,n.__setDigit(o,65535&h|b<<16)}return n.__trim()}},{key:"__absoluteAddOne",value:function(e,t){var _=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,n=e.length;null===_?_=new l(n,t):_.sign=t;for(var g,o=!0,a=0;a<n;a++){if(g=e.__digit(a),o){var s=-1===g;g=0|g+1,o=s}_.__setDigit(a,g)}return o&&_.__setDigitGrow(n,1),_}},{key:"__absoluteSubOne",value:function(e,t){var _=e.length;t=t||_;for(var n,g=new l(t,!1),o=!0,a=0;a<_;a++){if(n=e.__digit(a),o){var s=0===n;n=0|n-1,o=s}g.__setDigit(a,n)}if(o)throw new Error("implementation bug");for(var u=_;u<t;u++)g.__setDigit(u,0);return g}},{key:"__absoluteAnd",value:function(e,t){var _=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,n=e.length,g=t.length,o=g;if(n<g){o=n;var a=e,s=n;e=t,n=g,t=a,g=s}var u=o;null===_?_=new l(u,!1):u=_.length;for(var r=0;r<o;r++)_.__setDigit(r,e.__digit(r)&t.__digit(r));for(;r<u;r++)_.__setDigit(r,0);return _}},{key:"__absoluteAndNot",value:function(e,t){var _=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,n=e.length,g=t.length,o=g;n<g&&(o=n);var a=n;null===_?_=new l(a,!1):a=_.length;for(var s=0;s<o;s++)_.__setDigit(s,e.__digit(s)&~t.__digit(s));for(;s<n;s++)_.__setDigit(s,e.__digit(s));for(;s<a;s++)_.__setDigit(s,0);return _}},{key:"__absoluteOr",value:function(e,t){var _=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,n=e.length,g=t.length,o=g;if(n<g){o=n;var a=e,s=n;e=t,n=g,t=a,g=s}var u=n;null===_?_=new l(u,!1):u=_.length;for(var r=0;r<o;r++)_.__setDigit(r,e.__digit(r)|t.__digit(r));for(;r<n;r++)_.__setDigit(r,e.__digit(r));for(;r<u;r++)_.__setDigit(r,0);return _}},{key:"__absoluteXor",value:function(e,t){var _=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,n=e.length,g=t.length,o=g;if(n<g){o=n;var a=e,s=n;e=t,n=g,t=a,g=s}var u=n;null===_?_=new l(u,!1):u=_.length;for(var r=0;r<o;r++)_.__setDigit(r,e.__digit(r)^t.__digit(r));for(;r<n;r++)_.__setDigit(r,e.__digit(r));for(;r<u;r++)_.__setDigit(r,0);return _}},{key:"__absoluteCompare",value:function(e,t){var _=e.length-t.length;if(0!=_)return _;for(var n=e.length-1;0<=n&&e.__digit(n)===t.__digit(n);)n--;return 0>n?0:e.__unsignedDigit(n)>t.__unsignedDigit(n)?1:-1}},{key:"__multiplyAccumulate",value:function(e,t,_,n){if(0!==t){for(var g=65535&t,o=t>>>16,a=0,s=0,u=0,r=0;r<e.length;r++,n++){var d=_.__digit(n),h=65535&d,b=d>>>16,m=e.__digit(r),c=65535&m,v=m>>>16,f=l.__imul(c,g),y=l.__imul(c,o),k=l.__imul(v,g),D=l.__imul(v,o);h+=s+(65535&f),b+=u+a+(h>>>16)+(f>>>16)+(65535&y)+(65535&k),a=b>>>16,s=(y>>>16)+(k>>>16)+(65535&D)+a,a=s>>>16,s&=65535,u=D>>>16,d=65535&h|b<<16,_.__setDigit(n,d)}for(;0!==a||0!==s||0!==u;n++){var p=_.__digit(n),B=(65535&p)+s,S=(p>>>16)+(B>>>16)+u+a;s=0,u=0,a=S>>>16,p=65535&B|S<<16,_.__setDigit(n,p)}}}},{key:"__internalMultiplyAdd",value:function(e,t,_,g,o){for(var a=_,s=0,u=0;u<g;u++){var r=e.__digit(u),d=l.__imul(65535&r,t),h=(65535&d)+s+a;a=h>>>16;var b=l.__imul(r>>>16,t),m=(65535&b)+(d>>>16)+a;a=m>>>16,s=b>>>16,o.__setDigit(u,m<<16|65535&h)}if(o.length>g)for(o.__setDigit(g++,a+s);g<o.length;)o.__setDigit(g++,0);else if(0!==a+s)throw new Error("implementation bug")}},{key:"__absoluteDivSmall",value:function(e,t,_){null===_&&(_=new l(e.length,!1));for(var n=0,g=2*e.length-1;0<=g;g-=2){var o=(n<<16|e.__halfDigit(g))>>>0,a=0|o/t;n=0|o%t,o=(n<<16|e.__halfDigit(g-1))>>>0;var s=0|o/t;n=0|o%t,_.__setDigit(g>>>1,a<<16|s)}return _}},{key:"__absoluteModSmall",value:function(e,t){for(var _,n=0,l=2*e.length-1;0<=l;l--)_=(n<<16|e.__halfDigit(l))>>>0,n=0|_%t;return n}},{key:"__absoluteDivLarge",value:function(e,t,i,_){var g=t.__halfDigitLength(),n=t.length,o=e.__halfDigitLength()-g,a=null;i&&(a=new l(o+2>>>1,!1),a.__initializeDigits());var s=new l(g+2>>>1,!1);s.__initializeDigits();var r=l.__clz16(t.__halfDigit(g-1));0<r&&(t=l.__specialLeftShift(t,r,0));for(var d=l.__specialLeftShift(e,r,1),u=t.__halfDigit(g-1),h=0,b=o;0<=b;b--){var m=65535,v=d.__halfDigit(b+g);if(v!==u){var f=(v<<16|d.__halfDigit(b+g-1))>>>0;m=0|f/u;for(var y=0|f%u,k=t.__halfDigit(g-2),D=d.__halfDigit(b+g-2);l.__imul(m,k)>>>0>(y<<16|D)>>>0&&(m--,y+=u,!(65535<y)););}l.__internalMultiplyAdd(t,m,0,n,s);var p=d.__inplaceSub(s,b,g+1);0!==p&&(p=d.__inplaceAdd(t,b,g),d.__setHalfDigit(b+g,d.__halfDigit(b+g)+p),m--),i&&(1&b?h=m<<16:a.__setDigit(b>>>1,h|m))}return _?(d.__inplaceRightShift(r),i?{quotient:a,remainder:d}:d):i?a:void 0}},{key:"__clz16",value:function(e){return l.__clz32(e)-16}},{key:"__specialLeftShift",value:function(e,t,_){var g=e.length,n=new l(g+_,!1);if(0===t){for(var o=0;o<g;o++)n.__setDigit(o,e.__digit(o));return 0<_&&n.__setDigit(g,0),n}for(var a,s=0,u=0;u<g;u++)a=e.__digit(u),n.__setDigit(u,a<<t|s),s=a>>>32-t;return 0<_&&n.__setDigit(g,s),n}},{key:"__leftShiftByAbsolute",value:function(e,t){var _=l.__toShiftAmount(t);if(0>_)throw new RangeError("BigInt too big");var n=_>>>5,g=31&_,o=e.length,a=0!==g&&0!=e.__digit(o-1)>>>32-g,s=o+n+(a?1:0),u=new l(s,e.sign);if(0===g){for(var r=0;r<n;r++)u.__setDigit(r,0);for(;r<s;r++)u.__setDigit(r,e.__digit(r-n))}else{for(var h=0,b=0;b<n;b++)u.__setDigit(b,0);for(var m,c=0;c<o;c++)m=e.__digit(c),u.__setDigit(c+n,m<<g|h),h=m>>>32-g;if(a)u.__setDigit(o+n,h);else if(0!==h)throw new Error("implementation bug")}return u.__trim()}},{key:"__rightShiftByAbsolute",value:function(e,t){var _=e.length,n=e.sign,g=l.__toShiftAmount(t);if(0>g)return l.__rightShiftByMaximum(n);var o=g>>>5,a=31&g,s=_-o;if(0>=s)return l.__rightShiftByMaximum(n);var u=!1;if(n){if(0!=(e.__digit(o)&(1<<a)-1))u=!0;else for(var r=0;r<o;r++)if(0!==e.__digit(r)){u=!0;break}}if(u&&0===a){var h=e.__digit(_-1);0==~h&&s++}var b=new l(s,n);if(0===a)for(var m=o;m<_;m++)b.__setDigit(m-o,e.__digit(m));else{for(var c,v=e.__digit(o)>>>a,f=_-o-1,y=0;y<f;y++)c=e.__digit(y+o+1),b.__setDigit(y,c<<32-a|v),v=c>>>a;b.__setDigit(f,v)}return u&&(b=l.__absoluteAddOne(b,!0,b)),b.__trim()}},{key:"__rightShiftByMaximum",value:function(e){return e?l.__oneDigit(1,!0):l.__zero()}},{key:"__toShiftAmount",value:function(e){if(1<e.length)return-1;var t=e.__unsignedDigit(0);return t>l.__kMaxLengthBits?-1:t}},{key:"__toPrimitive",value:function(t){var i=1<arguments.length&&void 0!==arguments[1]?arguments[1]:"default";if("object"!==e(t))return t;if(t.constructor===l)return t;var _=t[Symbol.toPrimitive];if(_){var n=_(i);if("object"!==e(n))return n;throw new TypeError("Cannot convert object to primitive value")}var g=t.valueOf;if(g){var o=g.call(t);if("object"!==e(o))return o}var a=t.toString;if(a){var s=a.call(t);if("object"!==e(s))return s}throw new TypeError("Cannot convert object to primitive value")}},{key:"__toNumeric",value:function(e){return l.__isBigInt(e)?e:+e}},{key:"__isBigInt",value:function(t){return"object"===e(t)&&t.constructor===l}},{key:"__truncateToNBits",value:function(e,t){for(var _=e+31>>>5,n=new l(_,t.sign),g=_-1,o=0;o<g;o++)n.__setDigit(o,t.__digit(o));var a=t.__digit(g);if(0!=(31&e)){var s=32-(31&e);a=a<<s>>>s}return n.__setDigit(g,a),n.__trim()}},{key:"__truncateAndSubFromPowerOfTwo",value:function(e,t,_){for(var n=Math.min,g=e+31>>>5,o=new l(g,_),a=0,s=g-1,u=0,r=n(s,t.length);a<r;a++){var d=t.__digit(a),h=0-(65535&d)-u;u=1&h>>>16;var b=0-(d>>>16)-u;u=1&b>>>16,o.__setDigit(a,65535&h|b<<16)}for(;a<s;a++)o.__setDigit(a,0|-u);var m,c=s<t.length?t.__digit(s):0,v=31&e;if(0===v){var f=0-(65535&c)-u;u=1&f>>>16;var y=0-(c>>>16)-u;m=65535&f|y<<16}else{var k=32-v;c=c<<k>>>k;var D=1<<32-k,p=(65535&D)-(65535&c)-u;u=1&p>>>16;var B=(D>>>16)-(c>>>16)-u;m=65535&p|B<<16,m&=D-1}return o.__setDigit(s,m),o.__trim()}},{key:"__digitPow",value:function(e,t){for(var i=1;0<t;)1&t&&(i*=e),t>>>=1,e*=e;return i}}]),l}(u(Array));return y.__kMaxLength=33554432,y.__kMaxLengthBits=y.__kMaxLength<<5,y.__kMaxBitsPerChar=[0,0,32,51,64,75,83,90,96,102,107,111,115,119,122,126,128,131,134,136,139,141,143,145,147,149,151,153,154,156,158,159,160,162,163,165,166],y.__kBitsPerCharTableShift=5,y.__kBitsPerCharTableMultiplier=1<<y.__kBitsPerCharTableShift,y.__kConversionChars=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"],y.__kBitConversionBuffer=new ArrayBuffer(8),y.__kBitConversionDouble=new Float64Array(y.__kBitConversionBuffer),y.__kBitConversionInts=new Int32Array(y.__kBitConversionBuffer),y.__clz32=f||function(e){var t=Math.LN2,i=Math.log;return 0===e?32:0|31-(0|i(e>>>0)/t)},y.__imul=v||function(e,t){return 0|e*t},y});


/***/ }),

/***/ "./node_modules/process/browser.js":
/*!*****************************************!*\
  !*** ./node_modules/process/browser.js ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// shim for using process in browser
var process = module.exports = {};

// cached from whatever global is present so that test runners that stub it
// don't break things.  But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals.  It's inside a
// function because try/catches deoptimize in certain engines.

var cachedSetTimeout;
var cachedClearTimeout;

function defaultSetTimout() {
    throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
    throw new Error('clearTimeout has not been defined');
}
(function () {
    try {
        if (typeof setTimeout === 'function') {
            cachedSetTimeout = setTimeout;
        } else {
            cachedSetTimeout = defaultSetTimout;
        }
    } catch (e) {
        cachedSetTimeout = defaultSetTimout;
    }
    try {
        if (typeof clearTimeout === 'function') {
            cachedClearTimeout = clearTimeout;
        } else {
            cachedClearTimeout = defaultClearTimeout;
        }
    } catch (e) {
        cachedClearTimeout = defaultClearTimeout;
    }
} ())
function runTimeout(fun) {
    if (cachedSetTimeout === setTimeout) {
        //normal enviroments in sane situations
        return setTimeout(fun, 0);
    }
    // if setTimeout wasn't available but was latter defined
    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
        cachedSetTimeout = setTimeout;
        return setTimeout(fun, 0);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedSetTimeout(fun, 0);
    } catch(e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
            return cachedSetTimeout.call(null, fun, 0);
        } catch(e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
            return cachedSetTimeout.call(this, fun, 0);
        }
    }


}
function runClearTimeout(marker) {
    if (cachedClearTimeout === clearTimeout) {
        //normal enviroments in sane situations
        return clearTimeout(marker);
    }
    // if clearTimeout wasn't available but was latter defined
    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
        cachedClearTimeout = clearTimeout;
        return clearTimeout(marker);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedClearTimeout(marker);
    } catch (e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
            return cachedClearTimeout.call(null, marker);
        } catch (e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
            return cachedClearTimeout.call(this, marker);
        }
    }



}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;

function cleanUpNextTick() {
    if (!draining || !currentQueue) {
        return;
    }
    draining = false;
    if (currentQueue.length) {
        queue = currentQueue.concat(queue);
    } else {
        queueIndex = -1;
    }
    if (queue.length) {
        drainQueue();
    }
}

function drainQueue() {
    if (draining) {
        return;
    }
    var timeout = runTimeout(cleanUpNextTick);
    draining = true;

    var len = queue.length;
    while(len) {
        currentQueue = queue;
        queue = [];
        while (++queueIndex < len) {
            if (currentQueue) {
                currentQueue[queueIndex].run();
            }
        }
        queueIndex = -1;
        len = queue.length;
    }
    currentQueue = null;
    draining = false;
    runClearTimeout(timeout);
}

process.nextTick = function (fun) {
    var args = new Array(arguments.length - 1);
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
    }
    queue.push(new Item(fun, args));
    if (queue.length === 1 && !draining) {
        runTimeout(drainQueue);
    }
};

// v8 likes predictible objects
function Item(fun, array) {
    this.fun = fun;
    this.array = array;
}
Item.prototype.run = function () {
    this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;

process.listeners = function (name) { return [] }

process.binding = function (name) {
    throw new Error('process.binding is not supported');
};

process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };


/***/ }),

/***/ "./node_modules/setimmediate/setImmediate.js":
/*!***************************************************!*\
  !*** ./node_modules/setimmediate/setImmediate.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) {
    "use strict";

    if (global.setImmediate) {
        return;
    }

    var nextHandle = 1; // Spec says greater than zero
    var tasksByHandle = {};
    var currentlyRunningATask = false;
    var doc = global.document;
    var registerImmediate;

    function setImmediate(callback) {
      // Callback can either be a function or a string
      if (typeof callback !== "function") {
        callback = new Function("" + callback);
      }
      // Copy function arguments
      var args = new Array(arguments.length - 1);
      for (var i = 0; i < args.length; i++) {
          args[i] = arguments[i + 1];
      }
      // Store and register the task
      var task = { callback: callback, args: args };
      tasksByHandle[nextHandle] = task;
      registerImmediate(nextHandle);
      return nextHandle++;
    }

    function clearImmediate(handle) {
        delete tasksByHandle[handle];
    }

    function run(task) {
        var callback = task.callback;
        var args = task.args;
        switch (args.length) {
        case 0:
            callback();
            break;
        case 1:
            callback(args[0]);
            break;
        case 2:
            callback(args[0], args[1]);
            break;
        case 3:
            callback(args[0], args[1], args[2]);
            break;
        default:
            callback.apply(undefined, args);
            break;
        }
    }

    function runIfPresent(handle) {
        // From the spec: "Wait until any invocations of this algorithm started before this one have completed."
        // So if we're currently running a task, we'll need to delay this invocation.
        if (currentlyRunningATask) {
            // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
            // "too much recursion" error.
            setTimeout(runIfPresent, 0, handle);
        } else {
            var task = tasksByHandle[handle];
            if (task) {
                currentlyRunningATask = true;
                try {
                    run(task);
                } finally {
                    clearImmediate(handle);
                    currentlyRunningATask = false;
                }
            }
        }
    }

    function installNextTickImplementation() {
        registerImmediate = function(handle) {
            process.nextTick(function () { runIfPresent(handle); });
        };
    }

    function canUsePostMessage() {
        // The test against `importScripts` prevents this implementation from being installed inside a web worker,
        // where `global.postMessage` means something completely different and can't be used for this purpose.
        if (global.postMessage && !global.importScripts) {
            var postMessageIsAsynchronous = true;
            var oldOnMessage = global.onmessage;
            global.onmessage = function() {
                postMessageIsAsynchronous = false;
            };
            global.postMessage("", "*");
            global.onmessage = oldOnMessage;
            return postMessageIsAsynchronous;
        }
    }

    function installPostMessageImplementation() {
        // Installs an event handler on `global` for the `message` event: see
        // * https://developer.mozilla.org/en/DOM/window.postMessage
        // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages

        var messagePrefix = "setImmediate$" + Math.random() + "$";
        var onGlobalMessage = function(event) {
            if (event.source === global &&
                typeof event.data === "string" &&
                event.data.indexOf(messagePrefix) === 0) {
                runIfPresent(+event.data.slice(messagePrefix.length));
            }
        };

        if (global.addEventListener) {
            global.addEventListener("message", onGlobalMessage, false);
        } else {
            global.attachEvent("onmessage", onGlobalMessage);
        }

        registerImmediate = function(handle) {
            global.postMessage(messagePrefix + handle, "*");
        };
    }

    function installMessageChannelImplementation() {
        var channel = new MessageChannel();
        channel.port1.onmessage = function(event) {
            var handle = event.data;
            runIfPresent(handle);
        };

        registerImmediate = function(handle) {
            channel.port2.postMessage(handle);
        };
    }

    function installReadyStateChangeImplementation() {
        var html = doc.documentElement;
        registerImmediate = function(handle) {
            // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
            // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
            var script = doc.createElement("script");
            script.onreadystatechange = function () {
                runIfPresent(handle);
                script.onreadystatechange = null;
                html.removeChild(script);
                script = null;
            };
            html.appendChild(script);
        };
    }

    function installSetTimeoutImplementation() {
        registerImmediate = function(handle) {
            setTimeout(runIfPresent, 0, handle);
        };
    }

    // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
    var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
    attachTo = attachTo && attachTo.setTimeout ? attachTo : global;

    // Don't get fooled by e.g. browserify environments.
    if ({}.toString.call(global.process) === "[object process]") {
        // For Node.js before 0.9
        installNextTickImplementation();

    } else if (canUsePostMessage()) {
        // For non-IE10 modern browsers
        installPostMessageImplementation();

    } else if (global.MessageChannel) {
        // For web workers, where supported
        installMessageChannelImplementation();

    } else if (doc && "onreadystatechange" in doc.createElement("script")) {
        // For IE 6–8
        installReadyStateChangeImplementation();

    } else {
        // For older browsers
        installSetTimeoutImplementation();
    }

    attachTo.setImmediate = setImmediate;
    attachTo.clearImmediate = clearImmediate;
}(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self));

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"), __webpack_require__(/*! ./../process/browser.js */ "./node_modules/process/browser.js")))

/***/ }),

/***/ "./node_modules/strftime/strftime.js":
/*!*******************************************!*\
  !*** ./node_modules/strftime/strftime.js ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports) {

//
// strftime
// github.com/samsonjs/strftime
// @_sjs
//
// Copyright 2010 - 2016 Sami Samhuri <sami@samhuri.net>
//
// MIT License
// http://sjs.mit-license.org
//

;(function() {

    var Locales = {
        de_DE: {
            days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
            shortDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
            months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
            shortMonths: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d.%m.%Y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        en_CA: {
            days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
            shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
            months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            ordinalSuffixes: [
                'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
                'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
                'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
                'st'
            ],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d/%m/%y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%r',
                x: '%D'
            }
        },

        en_US: {
            days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
            shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
            months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            ordinalSuffixes: [
                'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
                'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
                'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
                'st'
            ],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%m/%d/%y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%r',
                x: '%D'
            }
        },

        es_MX: {
            days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
            shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
            months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre',' diciembre'],
            shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d/%m/%Y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        fr_FR: {
            days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
            shortDays: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
            months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
            shortMonths: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d/%m/%Y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        it_IT: {
            days: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato'],
            shortDays: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
            months: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
            shortMonths: ['pr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d/%m/%Y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        nl_NL: {
            days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
            shortDays: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
            months: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
            shortMonths: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d-%m-%y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        pt_BR: {
            days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'],
            shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
            months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
            shortMonths: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d-%m-%Y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        ru_RU: {
            days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
            shortDays: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
            months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
            shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
            AM: 'AM',
            PM: 'PM',
            am: 'am',
            pm: 'pm',
            formats: {
                c: '%a %d %b %Y %X',
                D: '%d.%m.%y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        tr_TR: {
            days: ['Pazar', 'Pazartesi', 'Salı','Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'],
            shortDays: ['Paz', 'Pzt', 'Sal', 'Çrş', 'Prş', 'Cum', 'Cts'],
            months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
            shortMonths: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
            AM: 'ÖÖ',
            PM: 'ÖS',
            am: 'ÖÖ',
            pm: 'ÖS',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d-%m-%Y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%T',
                x: '%D'
            }
        },

        // By michaeljayt<michaeljayt@gmail.com>
        // https://github.com/michaeljayt/strftime/commit/bcb4c12743811d51e568175aa7bff3fd2a77cef3
        zh_CN: {
            days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
            shortDays: ['日', '一', '二', '三', '四', '五', '六'],
            months: ['一月份', '二月份', '三月份', '四月份', '五月份', '六月份', '七月份', '八月份', '九月份', '十月份', '十一月份', '十二月份'],
            shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
            AM: '上午',
            PM: '下午',
            am: '上午',
            pm: '下午',
            formats: {
                c: '%a %d %b %Y %X %Z',
                D: '%d/%m/%y',
                F: '%Y-%m-%d',
                R: '%H:%M',
                r: '%I:%M:%S %p',
                T: '%H:%M:%S',
                v: '%e-%b-%Y',
                X: '%r',
                x: '%D'
            }
        }
    };

    var DefaultLocale = Locales['en_US'],
        defaultStrftime = new Strftime(DefaultLocale, 0, false),
        isCommonJS = typeof module !== 'undefined',
        namespace;

    // CommonJS / Node module
    if (isCommonJS) {
        namespace = module.exports = defaultStrftime;
    }
    // Browsers and other environments
    else {
        // Get the global object. Works in ES3, ES5, and ES5 strict mode.
        namespace = (function() { return this || (1,eval)('this'); }());
        namespace.strftime = defaultStrftime;
    }

    // Polyfill Date.now for old browsers.
    if (typeof Date.now !== 'function') {
        Date.now = function() {
          return +new Date();
        };
    }

    function Strftime(locale, customTimezoneOffset, useUtcTimezone) {
        var _locale = locale || DefaultLocale,
            _customTimezoneOffset = customTimezoneOffset || 0,
            _useUtcBasedDate = useUtcTimezone || false,

            // we store unix timestamp value here to not create new Date() each iteration (each millisecond)
            // Date.now() is 2 times faster than new Date()
            // while millisecond precise is enough here
            // this could be very helpful when strftime triggered a lot of times one by one
            _cachedDateTimestamp = 0,
            _cachedDate;

        function _strftime(format, date) {
            var timestamp;

            if (!date) {
                var currentTimestamp = Date.now();
                if (currentTimestamp > _cachedDateTimestamp) {
                    _cachedDateTimestamp = currentTimestamp;
                    _cachedDate = new Date(_cachedDateTimestamp);

                    timestamp = _cachedDateTimestamp;

                    if (_useUtcBasedDate) {
                        // how to avoid duplication of date instantiation for utc here?
                        // we tied to getTimezoneOffset of the current date
                        _cachedDate = new Date(_cachedDateTimestamp + getTimestampToUtcOffsetFor(_cachedDate) + _customTimezoneOffset);
                    }
                }
                else {
                  timestamp = _cachedDateTimestamp;
                }
                date = _cachedDate;
            }
            else {
                timestamp = date.getTime();

                if (_useUtcBasedDate) {
                    var utcOffset = getTimestampToUtcOffsetFor(date);
                    date = new Date(timestamp + utcOffset + _customTimezoneOffset);
                    // If we've crossed a DST boundary with this calculation we need to
                    // adjust the new date accordingly or it will be off by an hour in UTC.
                    if (getTimestampToUtcOffsetFor(date) !== utcOffset) {
                        var newUTCOffset = getTimestampToUtcOffsetFor(date);
                        date = new Date(timestamp + newUTCOffset + _customTimezoneOffset);
                    }
                }
            }

            return _processFormat(format, date, _locale, timestamp);
        }

        function _processFormat(format, date, locale, timestamp) {
            var resultString = '',
                padding = null,
                isInScope = false,
                length = format.length,
                extendedTZ = false;

            for (var i = 0; i < length; i++) {

                var currentCharCode = format.charCodeAt(i);

                if (isInScope === true) {
                    // '-'
                    if (currentCharCode === 45) {
                        padding = '';
                        continue;
                    }
                    // '_'
                    else if (currentCharCode === 95) {
                        padding = ' ';
                        continue;
                    }
                    // '0'
                    else if (currentCharCode === 48) {
                        padding = '0';
                        continue;
                    }
                    // ':'
                    else if (currentCharCode === 58) {
                      if (extendedTZ) {
                          warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime");
                      }
                      extendedTZ = true;
                      continue;
                    }

                    switch (currentCharCode) {

                        // Examples for new Date(0) in GMT

                        // '%'
                        // case '%':
                        case 37:
                            resultString += '%';
                            break;

                        // 'Thursday'
                        // case 'A':
                        case 65:
                            resultString += locale.days[date.getDay()];
                            break;

                        // 'January'
                        // case 'B':
                        case 66:
                            resultString += locale.months[date.getMonth()];
                            break;

                        // '19'
                        // case 'C':
                        case 67:
                            resultString += padTill2(Math.floor(date.getFullYear() / 100), padding);
                            break;

                        // '01/01/70'
                        // case 'D':
                        case 68:
                            resultString += _processFormat(locale.formats.D, date, locale, timestamp);
                            break;

                        // '1970-01-01'
                        // case 'F':
                        case 70:
                            resultString += _processFormat(locale.formats.F, date, locale, timestamp);
                            break;

                        // '00'
                        // case 'H':
                        case 72:
                            resultString += padTill2(date.getHours(), padding);
                            break;

                        // '12'
                        // case 'I':
                        case 73:
                            resultString += padTill2(hours12(date.getHours()), padding);
                            break;

                        // '000'
                        // case 'L':
                        case 76:
                            resultString += padTill3(Math.floor(timestamp % 1000));
                            break;

                        // '00'
                        // case 'M':
                        case 77:
                            resultString += padTill2(date.getMinutes(), padding);
                            break;

                        // 'am'
                        // case 'P':
                        case 80:
                            resultString += date.getHours() < 12 ? locale.am : locale.pm;
                            break;

                        // '00:00'
                        // case 'R':
                        case 82:
                            resultString += _processFormat(locale.formats.R, date, locale, timestamp);
                            break;

                        // '00'
                        // case 'S':
                        case 83:
                            resultString += padTill2(date.getSeconds(), padding);
                            break;

                        // '00:00:00'
                        // case 'T':
                        case 84:
                            resultString += _processFormat(locale.formats.T, date, locale, timestamp);
                            break;

                        // '00'
                        // case 'U':
                        case 85:
                            resultString += padTill2(weekNumber(date, 'sunday'), padding);
                            break;

                        // '00'
                        // case 'W':
                        case 87:
                            resultString += padTill2(weekNumber(date, 'monday'), padding);
                            break;

                        // '16:00:00'
                        // case 'X':
                        case 88:
                            resultString += _processFormat(locale.formats.X, date, locale, timestamp);
                            break;

                        // '1970'
                        // case 'Y':
                        case 89:
                            resultString += date.getFullYear();
                            break;

                        // 'GMT'
                        // case 'Z':
                        case 90:
                            if (_useUtcBasedDate && _customTimezoneOffset === 0) {
                                resultString += "GMT";
                            }
                            else {
                                // fixme optimize
                                var tzString = date.toString().match(/\(([\w\s]+)\)/);
                                resultString += tzString && tzString[1] || '';
                            }
                            break;

                        // 'Thu'
                        // case 'a':
                        case 97:
                            resultString += locale.shortDays[date.getDay()];
                            break;

                        // 'Jan'
                        // case 'b':
                        case 98:
                            resultString += locale.shortMonths[date.getMonth()];
                            break;

                        // ''
                        // case 'c':
                        case 99:
                            resultString += _processFormat(locale.formats.c, date, locale, timestamp);
                            break;

                        // '01'
                        // case 'd':
                        case 100:
                            resultString += padTill2(date.getDate(), padding);
                            break;

                        // ' 1'
                        // case 'e':
                        case 101:
                            resultString += padTill2(date.getDate(), padding == null ? ' ' : padding);
                            break;

                        // 'Jan'
                        // case 'h':
                        case 104:
                            resultString += locale.shortMonths[date.getMonth()];
                            break;

                        // '000'
                        // case 'j':
                        case 106:
                            var y = new Date(date.getFullYear(), 0, 1);
                            var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24));
                            resultString += padTill3(day);
                            break;

                        // ' 0'
                        // case 'k':
                        case 107:
                            resultString += padTill2(date.getHours(), padding == null ? ' ' : padding);
                            break;

                        // '12'
                        // case 'l':
                        case 108:
                            resultString += padTill2(hours12(date.getHours()), padding == null ? ' ' : padding);
                            break;

                        // '01'
                        // case 'm':
                        case 109:
                            resultString += padTill2(date.getMonth() + 1, padding);
                            break;

                        // '\n'
                        // case 'n':
                        case 110:
                            resultString += '\n';
                            break;

                        // '1st'
                        // case 'o':
                        case 111:
                            // Try to use an ordinal suffix from the locale, but fall back to using the old
                            // function for compatibility with old locales that lack them.
                            var day = date.getDate();
                            if (locale.ordinalSuffixes) {
                                resultString += String(day) + (locale.ordinalSuffixes[day - 1] || ordinal(day));
                            }
                            else {
                                resultString += String(day) + ordinal(day);
                            }
                            break;

                        // 'AM'
                        // case 'p':
                        case 112:
                            resultString += date.getHours() < 12 ? locale.AM : locale.PM;
                            break;

                        // '12:00:00 AM'
                        // case 'r':
                        case 114:
                            resultString += _processFormat(locale.formats.r, date, locale, timestamp);
                            break;

                        // '0'
                        // case 's':
                        case 115:
                            resultString += Math.floor(timestamp / 1000);
                            break;

                        // '\t'
                        // case 't':
                        case 116:
                            resultString += '\t';
                            break;

                        // '4'
                        // case 'u':
                        case 117:
                            var day = date.getDay();
                            resultString += day === 0 ? 7 : day;
                            break; // 1 - 7, Monday is first day of the week

                        // ' 1-Jan-1970'
                        // case 'v':
                        case 118:
                            resultString += _processFormat(locale.formats.v, date, locale, timestamp);
                            break;

                        // '4'
                        // case 'w':
                        case 119:
                            resultString += date.getDay();
                            break; // 0 - 6, Sunday is first day of the week

                        // '12/31/69'
                        // case 'x':
                        case 120:
                            resultString += _processFormat(locale.formats.x, date, locale, timestamp);
                            break;

                        // '70'
                        // case 'y':
                        case 121:
                            resultString += ('' + date.getFullYear()).slice(2);
                            break;

                        // '+0000'
                        // case 'z':
                        case 122:
                            if (_useUtcBasedDate && _customTimezoneOffset === 0) {
                                resultString += extendedTZ ? "+00:00" : "+0000";
                            }
                            else {
                                var off;
                                if (_customTimezoneOffset !== 0) {
                                    off = _customTimezoneOffset / (60 * 1000);
                                }
                                else {
                                    off = -date.getTimezoneOffset();
                                }
                                var sign = off < 0 ? '-' : '+';
                                var sep = extendedTZ ? ':' : '';
                                var hours = Math.floor(Math.abs(off / 60));
                                var mins = Math.abs(off % 60);
                                resultString += sign + padTill2(hours) + sep + padTill2(mins);
                            }
                            break;

                        default:
                            if (isInScope) {
                                resultString += '%';
                            }
                            resultString += format[i];
                            break;
                    }

                    padding = null;
                    isInScope = false;
                    continue;
                }

                // '%'
                if (currentCharCode === 37) {
                    isInScope = true;
                    continue;
                }

                resultString += format[i];
            }

            return resultString;
        }

        var strftime = _strftime;

        strftime.localize = function(locale) {
            return new Strftime(locale || _locale, _customTimezoneOffset, _useUtcBasedDate);
        };

        strftime.localizeByIdentifier = function(localeIdentifier) {
            var locale = Locales[localeIdentifier];
            if (!locale) {
                warn('[WARNING] No locale found with identifier "' + localeIdentifier + '".');
                return strftime;
            }
            return strftime.localize(locale);
        };

        strftime.timezone = function(timezone) {
            var customTimezoneOffset = _customTimezoneOffset;
            var useUtcBasedDate = _useUtcBasedDate;

            var timezoneType = typeof timezone;
            if (timezoneType === 'number' || timezoneType === 'string') {
                useUtcBasedDate = true;

                // ISO 8601 format timezone string, [-+]HHMM
                if (timezoneType === 'string') {
                    var sign = timezone[0] === '-' ? -1 : 1,
                        hours = parseInt(timezone.slice(1, 3), 10),
                        minutes = parseInt(timezone.slice(3, 5), 10);

                    customTimezoneOffset = sign * ((60 * hours) + minutes) * 60 * 1000;
                    // in minutes: 420
                }
                else if (timezoneType === 'number') {
                    customTimezoneOffset = timezone * 60 * 1000;
                }
            }

            return new Strftime(_locale, customTimezoneOffset, useUtcBasedDate);
        };

        strftime.utc = function() {
            return new Strftime(_locale, _customTimezoneOffset, true);
        };

        return strftime;
    }

    function padTill2(numberToPad, paddingChar) {
        if (paddingChar === '' || numberToPad > 9) {
            return numberToPad;
        }
        if (paddingChar == null) {
            paddingChar = '0';
        }
        return paddingChar + numberToPad;
    }

    function padTill3(numberToPad) {
        if (numberToPad > 99) {
            return numberToPad;
        }
        if (numberToPad > 9) {
            return '0' + numberToPad;
        }
        return '00' + numberToPad;
    }

    function hours12(hour) {
        if (hour === 0) {
            return 12;
        }
        else if (hour > 12) {
            return hour - 12;
        }
        return hour;
    }

    // firstWeekday: 'sunday' or 'monday', default is 'sunday'
    //
    // Pilfered & ported from Ruby's strftime implementation.
    function weekNumber(date, firstWeekday) {
        firstWeekday = firstWeekday || 'sunday';

        // This works by shifting the weekday back by one day if we
        // are treating Monday as the first day of the week.
        var weekday = date.getDay();
        if (firstWeekday === 'monday') {
            if (weekday === 0) // Sunday
                weekday = 6;
            else
                weekday--;
        }

        var firstDayOfYearUtc = Date.UTC(date.getFullYear(), 0, 1),
            dateUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
            yday = Math.floor((dateUtc - firstDayOfYearUtc) / 86400000),
            weekNum = (yday + 7 - weekday) / 7;

        return Math.floor(weekNum);
    }

    // Get the ordinal suffix for a number: st, nd, rd, or th
    function ordinal(number) {
        var i = number % 10;
        var ii = number % 100;

        if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) {
            return 'th';
        }
        switch (i) {
            case 1: return 'st';
            case 2: return 'nd';
            case 3: return 'rd';
        }
    }

    function getTimestampToUtcOffsetFor(date) {
        return (date.getTimezoneOffset() || 0) * 60000;
    }

    function warn(message) {
        if (typeof console !== 'undefined' && typeof console.warn == 'function') {
            console.warn(message)
        }
    }

}());


/***/ }),

/***/ "./node_modules/webpack/buildin/global.js":
/*!***********************************!*\
  !*** (webpack)/buildin/global.js ***!
  \***********************************/
/*! no static exports found */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || new Function("return this")();
} catch (e) {
	// This works if the window reference is available
	if (typeof window === "object") g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),

/***/ "./src/abstract.js":
/*!*************************!*\
  !*** ./src/abstract.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @namespace Sk.abstr
 *
 * @description
 * A collection of functions that can be used to interact with Skulpt Python Objects
 */
Sk.abstr = {};

/**@typedef {Sk.builtin.object}*/var pyObject;
/** @typedef {Sk.builtin.type|FunctionConstructor}*/var typeObject;


/**
 * @function
 * @description
 * Typically used withing error messages
 *
 * @example
 * throw new Sk.builtin.TypeError("expected an 'int' (got '" + Sk.abstr.typeName(i) + "'");
 *
 * @param {*} obj
 * @returns {string} - returns the typeName of any pyObject or `<invaid type>` if a JS object was passed
 */
Sk.abstr.typeName = function (obj) {
    if (obj != null && obj.tp$name !== undefined) {
        return obj.tp$name;
    } else {
        return "<invalid type>";
    }
};

const binop_name_to_symbol = {
    Add: "+",
    Sub: "-",
    Mult: "*",
    MatMult: "@",
    Div: "/",
    FloorDiv: "//",
    Mod: "%",
    DivMod: "divmod()",
    Pow: "** or pow()",
    LShift: "<<",
    RShift: ">>",
    BitAnd: "&",
    BitXor: "^",
    BitOr: "|",
};

function binop_type_error(v, w, name) {
    const vtypename = Sk.abstr.typeName(v);
    const wtypename = Sk.abstr.typeName(w);
    throw new Sk.builtin.TypeError("unsupported operand type(s) for " + binop_name_to_symbol[name] + ": '" + vtypename + "' and '" + wtypename + "'");
};

function biniop_type_error(v, w, name) {
    const vtypename = Sk.abstr.typeName(v);
    const wtypename = Sk.abstr.typeName(w);
    throw new Sk.builtin.TypeError("unsupported operand type(s) for " + binop_name_to_symbol[name] + "=: '" + vtypename + "' and '" + wtypename + "'");
};

const uop_name_to_symbol = {
    UAdd: "+",
    USub: "-",
    Invert: "~",
};
function unop_type_error(v, name) {
    var vtypename = Sk.abstr.typeName(v);
    throw new Sk.builtin.TypeError("bad operand type for unary " + uop_name_to_symbol[name] + ": '" + vtypename + "'");
};

/**
 * lookup and return the LHS object slot function method.  This could be either a builtin slot function or a dunder method defined by the user.
 * 
 * @param obj
 * @param name
 * 
 * @returns {Function|undefined}
 * 
 * @private
 */
function boNameToSlotFuncLhs_(obj, name) {
    switch (name) {
        case "Add":
            return obj.nb$add;
        case "Sub":
            return obj.nb$subtract;
        case "Mult":
            return obj.nb$multiply;
        case "MatMult":
            if (Sk.__future__.python3) {
                return obj.nb$matrix_multiply;
            }
        case "Div":
            return obj.nb$divide;
        case "FloorDiv":
            return obj.nb$floor_divide;
        case "Mod":
            return obj.nb$remainder;
        case "DivMod":
            return obj.nb$divmod;
        case "Pow":
            return obj.nb$power;
        case "LShift":
            return obj.nb$lshift;
        case "RShift":
            return obj.nb$rshift;
        case "BitAnd":
            return obj.nb$and;
        case "BitXor":
            return obj.nb$xor;
        case "BitOr":
            return obj.nb$or;
    }
};

function boNameToSlotFuncRhs_(obj, name) {
    switch (name) {
        case "Add":
            return obj.nb$reflected_add;
        case "Sub":
            return obj.nb$reflected_subtract;
        case "Mult":
            return obj.nb$reflected_multiply;
        case "MatMult":
            if (Sk.__future__.python3) {
                return obj.nb$reflected_matrix_multiply;
            }
        case "Div":
            return obj.nb$reflected_divide;
        case "FloorDiv":
            return obj.nb$reflected_floor_divide;
        case "Mod":
            return obj.nb$reflected_remainder;
        case "DivMod":
            return obj.nb$reflected_divmod;
        case "Pow":
            return obj.nb$reflected_power;
        case "LShift":
            return obj.nb$reflected_lshift;
        case "RShift":
            return obj.nb$reflected_rshift;
        case "BitAnd":
            return obj.nb$reflected_and;
        case "BitXor":
            return obj.nb$reflected_xor;
        case "BitOr":
            return obj.nb$reflected_or;
    }
};

function iboNameToSlotFunc_(obj, name) {
    switch (name) {
        case "Add":
            return obj.nb$inplace_add;
        case "Sub":
            return obj.nb$inplace_subtract;
        case "Mult":
            return obj.nb$inplace_multiply;
        case "MatMult":
            if (Sk.__future__.python3) {
                return obj.nb$inplace_matrix_multiply;
            }
        case "Div":
            return obj.nb$inplace_divide;
        case "FloorDiv":
            return obj.nb$inplace_floor_divide;
        case "Mod":
            return obj.nb$inplace_remainder;
        case "Pow":
            return obj.nb$inplace_power;
        case "LShift":
            return obj.nb$inplace_lshift;
        case "RShift":
            return obj.nb$inplace_rshift;
        case "BitAnd":
            return obj.nb$inplace_and;
        case "BitOr":
            return obj.nb$inplace_or;
        case "BitXor":
            return obj.nb$inplace_xor;
    }
};

function uoNameToSlotFunc_(obj, name) {
    switch (name) {
        case "USub":
            return obj.nb$negative;
        case "UAdd":
            return obj.nb$positive;
        case "Invert":
            return obj.nb$invert;
    }
};

function binary_op_(v, w, opname) {
    // All Python inheritance is now enforced with Javascript inheritance
    // (see Sk.abstr.setUpInheritance). This checks if w's type is a strict
    // subclass of v's type
    const w_type = w.constructor;
    const v_type = v.constructor;
    const w_is_subclass = w_type !== v_type && w_type.sk$baseClass === undefined && w instanceof v_type;

    // From the Python 2.7 docs:
    //
    // "If the right operand’s type is a subclass of the left operand’s type and
    // that subclass provides the reflected method for the operation, this
    // method will be called before the left operand’s non-reflected method.
    // This behavior allows subclasses to override their ancestors’ operations."
    //
    // -- https://docs.python.org/2/reference/datamodel.html#index-92

    let wop;
    let ret;
    if (w_is_subclass) {
        wop = boNameToSlotFuncRhs_(w, opname);
        // only use the reflected slot if it has actually be overridden
        if (wop !== undefined && wop !== boNameToSlotFuncRhs_(v, opname)) {
            ret = wop.call(w, v);
            if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
                return ret;
            }
        }
    }

    const vop = boNameToSlotFuncLhs_(v, opname);
    if (vop !== undefined) {
        ret = vop.call(v, w);
        if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
            return ret;
        }
    }
    // Don't retry RHS if failed above
    if (!w_is_subclass) {
        wop = boNameToSlotFuncRhs_(w, opname);
        if (wop !== undefined) {
            ret = wop.call(w, v);
            if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
                return ret;
            }
        }
    }

};

function binary_iop_(v, w, opname) {
    const vop = iboNameToSlotFunc_(v, opname);
    if (vop !== undefined) {
        const ret = vop.call(v, w);
        if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
            return ret;
        }
    }
    // If there wasn't an in-place operation, fall back to the binop
    return binary_op_(v, w, opname);
};

function unary_op_(v, opname) {
    const vop = uoNameToSlotFunc_(v, opname);
    if (vop !== undefined) {
        return vop.call(v);
    }
};

/**
 * @function
 * @description
 * Perform a binary operation with any pyObjects that support the operation
 * @param {pyObject} v
 * @param {pyObject} w
 * @param {string} op - `Add`, `Sub`, `Mult`, `Divide`, ...
 *
 * @throws {Sk.builtin.TypeError}
 */
Sk.abstr.numberBinOp = function (v, w, op) {
    return binary_op_(v, w, op) || binop_type_error(v, w, op);
};
Sk.exportSymbol("Sk.abstr.numberBinOp", Sk.abstr.numberBinOp);

/**
 * @function
 * @description
 * Perform an inplace operation with any pyObjects that support the operation
 * @param {pyObject} v
 * @param {pyObject} w
 * @param {string} op - `Add`, `Sub`, `Mult`, `Divide`, ...
 *
 * @throws {Sk.builtin.TypeError}
 */
Sk.abstr.numberInplaceBinOp = function (v, w, op) {
    return binary_iop_(v, w, op) || biniop_type_error(v, w, op);
};
Sk.exportSymbol("Sk.abstr.numberInplaceBinOp", Sk.abstr.numberInplaceBinOp);

/**
 * @function
 * @description
 * Perform a unary operation with any pyObjects that support the operation
 * @param {pyObject} v
 * @param {string} op - `UAdd`, `USub`
 *
 * @throws {Sk.builtin.TypeError}
 */
Sk.abstr.numberUnaryOp = function (v, op) {
    if (op === "Not") {
        return Sk.misceval.isTrue(v) ? Sk.builtin.bool.false$ : Sk.builtin.bool.true$;
    }
    return unary_op_(v, op) || unop_type_error(v, op);
};
Sk.exportSymbol("Sk.abstr.numberUnaryOp", Sk.abstr.numberUnaryOp);

/**
 * @deprecated
 */
Sk.abstr.fixSeqIndex_ = function (seq, i) {
    i = Sk.builtin.asnum$(i);
    if (i < 0 && seq.sq$length) {
        i += seq.sq$length();
    }
    return i;
};

/**
 * @param {pyObject} seq
 * @param {pyObject} ob
 * @param {boolean=} canSuspend
 * 
 */
Sk.abstr.sequenceContains = function (seq, ob, canSuspend) {
    if (seq.sq$contains) {
        return seq.sq$contains(ob, canSuspend);
    }
    const r = Sk.misceval.iterFor(
        Sk.abstr.iter(seq),
        function (i) {
            if (i === ob || Sk.misceval.richCompareBool(i, ob, "Eq")) {
                return new Sk.misceval.Break(true);
            } else {
                return false;
            }
        },
        false
    );
    return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r);
};

Sk.abstr.sequenceConcat = function (seq1, seq2) {
    if (seq1.sq$concat) {
        return seq1.sq$concat(seq2);
    }
    throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq1) + "' object can't be concatenated");
};

/**
 * @param {pyObject} seq 
 * @param {pyObject} ob 
 */
Sk.abstr.sequenceGetIndexOf = function (seq, ob) {
    if (seq.index) {
        return Sk.misceval.callsimArray(seq.index, [seq, ob]);
    }
    let index = 0;
    for (let it = Sk.abstr.iter(seq), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
        if (Sk.misceval.richCompareBool(ob, i, "Eq")) {
            return new Sk.builtin.int_(index);
        }
        index += 1;
    }
    throw new Sk.builtin.ValueError("sequence.index(x): x not in sequence");
};

/**
 * @param {pyObject} seq 
 * @param {pyObject} ob 
 */
Sk.abstr.sequenceGetCountOf = function (seq, ob) {
    if (seq.count) {
        return Sk.misceval.callsimArray(seq.count, [seq, ob]);
    }
    let count = 0;
    for (let it = Sk.abstr.iter(seq), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
        if (Sk.misceval.richCompareBool(ob, i, "Eq")) {
            count += 1;
        }
    }
    return new Sk.builtin.int_(count);
};

Sk.abstr.sequenceGetItem = function (seq, i, canSuspend) {
    if (typeof i === "number") {
        i = new Sk.builtin.int_(i);
    }
    return Sk.abstr.objectGetItem(seq, i, canSuspend);
};

Sk.abstr.sequenceSetItem = function (seq, i, x, canSuspend) {
    if (typeof i === "number") {
        i = new Sk.builtin.int_(i);
    }
    return Sk.abstr.objectSetItem(seq, i, x, canSuspend);
};

Sk.abstr.sequenceDelItem = function (seq, i, canSuspend) {
    return Sk.abstr.objectDelItem(seq, i, canSuspend);
};

Sk.abstr.sequenceGetSlice = function (seq, i1, i2) {
    return Sk.abstr.objectGetItem(seq, new Sk.builtin.slice(i1, i2));
};

Sk.abstr.sequenceDelSlice = function (seq, i1, i2) {
    return Sk.abstr.objectDelItem(seq, new Sk.builtin.slice(i1, i2));
};

Sk.abstr.sequenceSetSlice = function (seq, i1, i2, x) {
    return Sk.abstr.objectSetItem(seq, new Sk.builtin.slice(i1, i2));
};

/**
 * 
 * @param {*} seq the iterable to unpack
 * @param {*} breakIdx either the starred index or the number of elements to unpack if no star
 * @param {*} numvals the total number of un-starred indices
 * @param {*} hasStar is there a starred index
 * 
 * this function is used in compile code to unpack a sequence to an assignment statement
 * e.g.
 * a, b, c = 1, 2, 3 # seq is the tuple 1,2,3
 * // Sk.abstr.sequenceUncpack(seq, 3, 3, false)
 * // return [int_(1), int_(2), int_(3)]
 * 
 * 
 * a, *b, c = 1,2,3,4 
 * // Sk.abstr.sequenceUncpack(seq, 1, 2, true)
 * // return [int_(1), list(int_(2), int_(3)), int_(4)]
 * 
 */
Sk.abstr.sequenceUnpack = function (seq, breakIdx, numvals, hasStar) {
    if (!Sk.builtin.checkIterable(seq)) {
        throw new Sk.builtin.TypeError("cannot unpack non-iterable " + Sk.abstr.typeName(seq) + " object");
    }
    const it = Sk.abstr.iter(seq);
    const res = [];
    let i = 0;
    let upToStar;
    if (breakIdx > 0) {
        // iterator up to but not including the breakIdx
        upToStar = Sk.misceval.iterFor(it, (nxt) => {
            res.push(nxt);
            if (++i === breakIdx) {
                return new Sk.misceval.Break();
            }
        });
    }

    return Sk.misceval.chain(upToStar, () => {
        if (res.length < breakIdx) {
            throw new Sk.builtin.ValueError("not enough values to unpack (expected at least " + numvals + ", got " + res.length + ")");
        }
        if (!hasStar) {
            // check we've consumed the iterator
            return Sk.misceval.chain(it.tp$iternext(true), (nxt) => {
                if (nxt !== undefined) {
                    throw new Sk.builtin.ValueError("too many values to unpack (expected " + breakIdx + ")");
                }
                return res;
            });
        }
        const starred = [];
        return Sk.misceval.chain(
            Sk.misceval.iterFor(it, (nxt) => {
                starred.push(nxt);
            }),
            () => {
                const starred_end = starred.length + breakIdx - numvals;
                if (starred_end < 0) {
                    throw new Sk.builtin.ValueError(
                        "not enough values to unpack (expected at least " + numvals + ", got " + (numvals + starred_end) + ")"
                    );
                }
                res.push(new Sk.builtin.list(starred.slice(0, starred_end)));
                res.push(...starred.slice(starred_end));
                // Return Javascript array of items
                return res;
            }
        );
    });
};

// Unpack mapping into a JS array of alternating keys/values, possibly suspending
// Skulpt uses a slightly grungy format for keyword args
// into misceval.apply() and friends (alternating JS strings and Python values).
// We should probably migrate that interface to using Python strings
// at some point, but in the meantime we have this function to
// unpack keyword dictionaries into our special format
Sk.abstr.mappingUnpackIntoKeywordArray = function (jsArray, pyMapping, pyCodeObject) {
    if (pyMapping instanceof Sk.builtin.dict) {
        pyMapping.$items().forEach(([key, val]) => {
            if (!Sk.builtin.checkString(key)) {
                throw new Sk.builtin.TypeError((pyCodeObject.$qualname ? pyCodeObject.$qualname + "() " : "") + "keywords must be strings");
            } 
            jsArray.push(key.v);
            jsArray.push(val);
        });
        return;
    }

    const keyf = Sk.abstr.lookupSpecial(pyMapping, Sk.builtin.str.$keys);
    if (keyf === undefined) {
        throw new Sk.builtin.TypeError("Object is not a mapping");
    }
    return Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(keyf), (keys) =>
        Sk.misceval.iterFor(Sk.abstr.iter(keys), (key) => {
            if (!Sk.builtin.checkString(key)) {
                throw new Sk.builtin.TypeError((pyCodeObject.$qualname ? pyCodeObject.$qualname + "() " : "") + "keywords must be strings");
            }
            return Sk.misceval.chain(pyMapping.mp$subscript(key, true), (val) => {
                jsArray.push(key.v);
                jsArray.push(val);
            });
        })
    );
};

/**
 *
 * @function
 * @description
 * A helper function used by native js functions whose call method is FastCall i.e. the args and kwargs are provided as Array objects.
 *
 * @param {string} func_name - used for error messages
 * @param {Array<null|string>} varnames - Argument names to map to. For position only arguments use null
 * @param {Array} args - typically provided by the `tp$call` method
 * @param {Array|undefined} kwargs - typically provided the `tp$call` method
 * @param {Array=} defaults
 * @throws {Sk.builtin.TypeError}
 *
 * @example
 * // base is a possible keyword argument for int_ and x is a position only argument
 * Sk.builtin.int_.prototype.tp$new = function(args, kwargs) {
 *     args = Sk.abstr.copyKeywordsToNamedArgs("int", [null, "base"], args, kwargs, [
 *         new Sk.builtin.int_(0),
 *         Sk.builtin.none.none$
 *     ]);
 * }
 */
Sk.abstr.copyKeywordsToNamedArgs = function (func_name, varnames, args, kwargs, defaults) {
    // args is an array, kwargs is an array or undefined
    kwargs = kwargs || [];

    const nargs = args.length + kwargs.length / 2;
    if (nargs > varnames.length) {
        throw new Sk.builtin.TypeError(func_name + "() expected at most " + varnames.length + " arguments (" + nargs + " given)");
    }
    if (!kwargs.length && defaults === undefined) {
        // no defaults supplied
        return args;
    } else if (nargs === varnames.length && !kwargs.length) {
        // position only arguments match
        return args;
    } else if (nargs === 0 && varnames.length === (defaults && defaults.length)) {
        // a fast case - no args so just return the defaults
        return defaults;
    }
    args = args.slice(0); // make a copy of args

    for (let i = 0; i < kwargs.length; i += 2) {
        const name = kwargs[i]; // JS string
        const value = kwargs[i + 1]; // Python value
        const idx = varnames.indexOf(name);

        if (idx >= 0) {
            if (args[idx] !== undefined) {
                throw new Sk.builtin.TypeError(func_name + "() got multiple values for argument '" + name + "'");
            }
            args[idx] = value;
        } else {
            throw new Sk.builtin.TypeError(func_name + "() got an unexpected keyword argument '" + name + "'");
        }
    }
    if (defaults) {
        const nargs = varnames.length;
        for (let i = nargs - 1; i >= 0; i--) {
            if (args[i] === undefined) {
                args[i] = defaults[defaults.length - 1 - (nargs - 1 - i)];
            }
        }
        const missing = varnames.filter((x, i) => args[i] === undefined);
        if (missing.length) {
            throw new Sk.builtin.TypeError(func_name + "() missing " + missing.length + " required positional arguments: " + missing.join(", "));
        }
    }

    return args;
};
Sk.exportSymbol("Sk.abstr.copyKeywordsToNamedArgs", Sk.abstr.copyKeywordsToNamedArgs);

/**
 * @function
 * @param {string} func_name
 * @param {Array|undefined} kwargs
 * @throws {Sk.builtin.TypeError}
 */
Sk.abstr.checkNoKwargs = function (func_name, kwargs) {
    if (kwargs && kwargs.length) {
        throw new Sk.builtin.TypeError(func_name + "() takes no keyword arguments");
    }
};
Sk.exportSymbol("Sk.abstr.checkNoKwargs", Sk.abstr.checkNoKwargs);

/**
 * @function
 * @param {string} func_name
 * @param {Array} args
 * @param {Array|undefined=} kwargs
 *
 * @throws {Sk.builtin.TypeError}
 */
Sk.abstr.checkNoArgs = function (func_name, args, kwargs) {
    const nargs = args.length + (kwargs ? kwargs.length : 0);
    if (nargs) {
        throw new Sk.builtin.TypeError(func_name + "() takes no arguments (" + nargs + " given)");
    }
};
Sk.exportSymbol("Sk.abstr.checkNoArgs", Sk.abstr.checkNoArgs);

/**
 * @function
 * @param {string} func_name
 * @param {Array} args
 * @param {Array|undefined=} kwargs
 * @throws {Sk.builtin.TypeError}
 */
Sk.abstr.checkOneArg = function (func_name, args, kwargs) {
    Sk.abstr.checkNoKwargs(func_name, kwargs);
    if (args.length !== 1) {
        throw new Sk.builtin.TypeError(func_name + "() takes exactly one argument (" + args.length + " given)");
    }
};
Sk.exportSymbol("Sk.abstr.checkOneArg", Sk.abstr.checkOneArg);

/**
 * @function
 * @param {string} func_name
 * @param {Array} args
 * @param {number} minargs
 * @param {number=} [maxargs=Infinity]
 * @throws {Sk.builtin.TypeError}
 *
 */
Sk.abstr.checkArgsLen = function (func_name, args, minargs, maxargs) {
    const nargs = args.length;
    let msg;
    if (maxargs === undefined) {
        maxargs = Infinity;
    }
    if (nargs < minargs || nargs > maxargs) {
        if (minargs === maxargs) {
            msg = func_name + "() takes exactly " + minargs + " arguments";
        } else if (nargs < minargs) {
            msg = func_name + "() takes at least " + minargs + " arguments";
        } else {
            msg = func_name + "() takes at most " + maxargs + " arguments";
        }
        msg += " (" + nargs + " given)";
        throw new Sk.builtin.TypeError(msg);
    }
};
Sk.exportSymbol("Sk.abstr.checkArgsLen", Sk.abstr.checkArgsLen);

Sk.abstr.objectFormat = function (obj, format_spec) {
    const meth = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$format); // inherited from object so guaranteed to exist
    const result = Sk.misceval.callsimArray(meth, [format_spec]);
    if (!Sk.builtin.checkString(result)) {
        throw new Sk.builtin.TypeError("__format__ must return a str, not " + Sk.abstr.typeName(result));
    }
    return result;
};

/**
 * 
 * @param {pyObject} obj 
 * 
 * @returns {Number} the hash value a number less than Number.MAX_SAFE_INTEGER 
 * @throws {Sk.buitin.TypeError} if the object is unhashable
 */
Sk.abstr.objectHash = function (obj) {
    const hash_func = obj.tp$hash;
    if (hash_func !== undefined) {
        if (Sk.builtin.checkNone(hash_func)) {
            throw new Sk.builtin.TypeError("unhashable type: '" + Sk.abstr.typeName(obj) + "'");
        }
        return obj.tp$hash();
    }
    throw new Sk.builtin.TypeError("unsupported Javascript type");
};

Sk.abstr.objectAdd = function (a, b) {
    if (a.nb$add) {
        return a.nb$add(b);
    }
    const atypename = Sk.abstr.typeName(a);
    const btypename = Sk.abstr.typeName(b);
    throw new Sk.builtin.TypeError("unsupported operand type(s) for +: '" + atypename + "' and '" + btypename + "'");
};

// in Python 2.6, this behaviour seems to be defined for numbers and bools (converts bool to int)
Sk.abstr.objectNegative = function (obj) {
    if (obj.nb$negative) {
        return obj.nb$negative();
    }
    throw new Sk.builtin.TypeError("bad operand type for unary -: '" + Sk.abstr.typeName(obj) + "'");
};

Sk.abstr.objectPositive = function (obj) {
    if (obj.nb$positive) {
        return obj.nb$positive();
    }
    throw new Sk.builtin.TypeError("bad operand type for unary +: '" + Sk.abstr.typeName(obj) + "'");
};

Sk.abstr.objectDelItem = function (o, key, canSuspend) {
    if (o.mp$ass_subscript) {
        return o.mp$ass_subscript(key, undefined, canSuspend);
    }
    throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(o) + "' object does not support item deletion");
};
Sk.exportSymbol("Sk.abstr.objectDelItem", Sk.abstr.objectDelItem);

/**
 * 
 * @param {pyObject} o 
 * @param {pyObject} key 
 * @param {boolean=} canSuspend 
 */
Sk.abstr.objectGetItem = function (o, key, canSuspend) {
    if (o.mp$subscript) {
        return o.mp$subscript(key, canSuspend);
    }
    throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(o) + "' does not support indexing");
};
Sk.exportSymbol("Sk.abstr.objectGetItem", Sk.abstr.objectGetItem);

/**
 * 
 * @param {pyObject} o 
 * @param {pyObject} key 
 * @param {pyObject=} v 
 * @param {boolean=} canSuspend 
 */
Sk.abstr.objectSetItem = function (o, key, v, canSuspend) {
    if (o.mp$ass_subscript) {
        return o.mp$ass_subscript(key, v, canSuspend);
    }
    throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(o) + "' does not support item assignment");
};
Sk.exportSymbol("Sk.abstr.objectSetItem", Sk.abstr.objectSetItem);

/**
 * 
 * @param {pyObject} obj 
 * @param {Sk.builtin.str} pyName 
 * @param {boolean=} canSuspend 
 */
Sk.abstr.gattr = function (obj, pyName, canSuspend) {
    // let the getattr and setattr's deal with reserved words - we don't want to pass a mangled pyName to tp$getattr!!
    const ret = obj.tp$getattr(pyName, canSuspend);
    if (ret === undefined) {
        throw new Sk.builtin.AttributeError(obj.sk$attrError() + " has no attribute '" + pyName.$jsstr() + "'");
    } else if (ret.$isSuspension) {
        return Sk.misceval.chain(ret, function (r) {
            if (r === undefined) {
                throw new Sk.builtin.AttributeError(obj.sk$attrError() + " has no attribute '" + pyName.$jsstr() + "'");
            }
            return r;
        });
    } else {
        return ret;
    }
};
Sk.exportSymbol("Sk.abstr.gattr", Sk.abstr.gattr);

Sk.abstr.sattr = function (obj, pyName, data, canSuspend) {
    return obj.tp$setattr(pyName, data, canSuspend);
};
Sk.exportSymbol("Sk.abstr.sattr", Sk.abstr.sattr);

Sk.abstr.iternext = function (it, canSuspend) {
    return it.tp$iternext(canSuspend);
};
Sk.exportSymbol("Sk.abstr.iternext", Sk.abstr.iternext);

/**
 * @function
 *
 * @description
 * Get the iterator for a Python object  This iterator could be one of the following.
 * This is the preferred mechanism for consistently getting the correct iterator.  You should
 * not just use tp$iter because that could lead to incorrect behavior of a user created class.
 *
 * - `tp$iter`
 * - A user defined `__iter__` method
 * - A user defined `__getitem__` method
 *
 * @param {pyObject} obj
 *
 * @throws {Sk.builtin.TypeError} If the object passed is not iterable
 * @returns {pyObject}
 */
Sk.abstr.iter = function (obj) {
    if (obj.tp$iter) {
        const iter = obj.tp$iter();
        if (iter.tp$iternext) {
            // only a valid iterator if there is a tp$iternext
            return iter;
        }
        throw new Sk.builtin.TypeError("iter() returned non-iterator of type '" + Sk.abstr.typeName(iter) + "'");
    }
    if (obj.mp$subscript) {
        return new Sk.builtin.seq_iter_(obj);
    }

    throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(obj) + "' object is not iterable");
};
Sk.exportSymbol("Sk.abstr.iter", Sk.abstr.iter);


/**
 * @description
 * Special method look up.
 * Checks whether the attribute is defined on object type's prototype
 *
 * @returns {undefined | Object} Return undefined if not found or the function
 *
 * @param {pyObject} obj
 * @param {Sk.builtin.str} pyName
 */
Sk.abstr.lookupSpecial = function (obj, pyName) {
    var obtype = obj.ob$type;
    if (obtype === undefined) {
        Sk.asserts.fail("javascript object sent to lookupSpecial");
        return;
    }
    var func = obtype.$typeLookup(pyName);
    if (func === undefined) {
        return;
    } else if (func.tp$descr_get !== undefined) {
        func = func.tp$descr_get(obj, obtype);
    }
    return func;
};
Sk.exportSymbol("Sk.abstr.lookupSpecial", Sk.abstr.lookupSpecial);


Sk.abstr.typeLookup = function (type_obj, pyName) {
    const res = type_obj.$typeLookup(pyName);
    if (res !== undefined && res.tp$descr_get) {
        return res.tp$descr_get(null, type_obj);
    }
    return res;
};

/**
 * Mark a class as unhashable and prevent its `__hash__` function from being called.
 * @param  {*} thisClass The class to mark as unhashable.
 * @return {undefined}
 */
Sk.abstr.markUnhashable = function (thisClass) {
    const proto = thisClass.prototype;
    proto.__hash__ = Sk.builtin.none.none$;
    proto.tp$hash = Sk.builtin.none.none$;
};

/**
 * @description
 * Set up inheritance between two Python classes. This allows only for single
 * inheritance -- multiple inheritance is not supported by Javascript.
 * multiple inheritance is dealt with by tp$getattr implementations
 *
 * Javascript's inheritance is prototypal. This means that properties must
 * be defined on the superclass' prototype in order for subclasses to inherit
 * them.
 *
 * ```
 * Sk.superclass.myProperty                 # will NOT be inherited
 * Sk.superclass.prototype.myProperty       # will be inherited
 * ```
 *
 * In order for a class to be subclassable, it must (directly or indirectly)
 * inherit from Sk.builtin.object so that it will be properly initialized in
 * {@link Sk.doOneTimeInitialization} (in src/import.js). Further, all Python
 * builtins should inherit from Sk.builtin.object.
 *
 * @param {string} childName The Python name of the child (subclass).
 * @param {FunctionConstructor} child     The subclass.
 * @param {FunctionConstructor=} [parent=Sk.builtin.object]    The base of child.
 * @param {FunctionConstructor=} [metaclass=Sk.builtin.type]
 * 
 * @returns {FunctionConstructor}
 * 
 */
Sk.abstr.setUpInheritance = function (childName, child, parent, metaclass) {
    metaclass = metaclass || Sk.builtin.type;
    parent = parent === undefined ? Sk.builtin.object : parent;
    const parentproto = parent !== null ? parent.prototype : null;
    Object.setPrototypeOf(child, metaclass.prototype);
    Object.setPrototypeOf(child.prototype, parentproto);
    Object.defineProperties(child.prototype, {
        sk$object: { value: child, writable: true },
        ob$type: { value: child, writable: true },
        tp$name: { value: childName, writable: true },
        tp$base: { value: parent, writable: true },
    });
};


/**
 * This function is called in {@link Sk.doOneTimeInitialization}
 * and {@link Sk.abstr.buildNativeClass}
 *
 * @param  {FunctionConstructor} child
 *
 */
Sk.abstr.setUpBuiltinMro = function (child) {
    let base = child.prototype.tp$base;
    const bases = base === null ? [] : [base];
    if (base === Sk.builtin.object || base === null) {
        Object.defineProperty(child, "sk$baseClass", { value: true, writable: true });
        Object.defineProperty(child.prototype, "sk$builtinBase", { value: child, writable: true });
    }
    const mro = [child];
    while (base !== null) {
        mro.push(base);
        base = base.prototype.tp$base;
    }
    
    // internally we keep the mro and bases as array objects
    // the wrapper descripor returns the tuple of the array
    Object.defineProperties(child.prototype, {
        sk$prototypical: { value: true, writable: true },
        tp$bases: { value: bases, writable: true },
        tp$mro: { value: mro, writable: true },
    });
    Object.defineProperty(child, "$typeLookup", {
        value: function (pyName) {
            var jsName = pyName.$mangled;
            return this.prototype[jsName];
        },
        writable: true,
    });
};

/**
 * @param {FunctionConstructor} klass 
 * @param {Object=} getsets 
 */
Sk.abstr.setUpGetSets = function (klass, getsets) {
    if (Sk.builtin.getset_descriptor === undefined) {
        return;
    }
    const klass_proto = klass.prototype;
    getsets = getsets || klass_proto.tp$getsets || {};
    Object.entries(getsets).forEach(([getset_name, getset_def]) => {
        getset_def.$name = getset_name;
        klass_proto[getset_name] = new Sk.builtin.getset_descriptor(klass, getset_def);
    });
    Object.defineProperty(klass_proto, "tp$getsets", { value: null, writable: true });
};

/**
 * 
 * @param {FunctionConstructor} klass 
 * @param {Object=} methods 
 */
Sk.abstr.setUpMethods = function (klass, methods) {
    if (Sk.builtin.method_descriptor === undefined) {
        return;
    }
    const klass_proto = klass.prototype;
    methods = methods || klass_proto.tp$methods || {};
    Object.entries(methods).forEach(([method_name, method_def]) => {
        method_def.$name = method_name;
        klass_proto[method_name] = new Sk.builtin.method_descriptor(klass, method_def);
    });
    Object.defineProperty(klass_proto, "tp$methods", { value: null, writable: true });
};

/**
 * 
 * @param {FunctionConstructor} klass 
 * @param {Object=} methods 
 */
Sk.abstr.setUpClassMethods = function (klass, methods) {
    if (Sk.builtin.classmethod_descriptor === undefined) {
        return;
    }
    const klass_proto = klass.prototype;
    methods = methods || klass_proto.tp$classmethods || {};
    Object.entries(methods).forEach(([method_name, method_def]) => {
        method_def.$name = method_name;
        klass_proto[method_name] = new Sk.builtin.classmethod_descriptor(klass, method_def);
    });
    Object.defineProperty(klass_proto, "tp$classmethods", { value: null, writable: true });
};

const op2shortcut = Object.entries({
    Eq: "ob$eq",
    NotEq: "ob$ne",
    Gt: "ob$gt",
    GtE: "ob$ge",
    Lt: "ob$lt",
    LtE: "ob$le",
});

function _set_up_richcompare_wrappers(slots) {
    op2shortcut.forEach(([op, shortcut]) => {
        slots[shortcut] = function (other) {
            return this.tp$richcompare(other, op);
        };
    });
}

function _set_up_reflected_number_slots(slots) {
    // do the reflected slots
    const reflected_slots = Sk.reflectedNumberSlots;
    Object.keys(reflected_slots).forEach((slot_name) => {
        if (slots[slot_name] !== undefined) {
            const reflected_slot_def = reflected_slots[slot_name];
            const reflect_name = reflected_slot_def.reflected;
            const reflected_slot = slots[reflect_name];
            if (reflected_slot !== undefined) {
                // the reflected slot was already defined so don't recreate it
                if (reflected_slot === null) {
                    delete slots[reflect_name]; // e.g. Counter doesn't want reflected slots
                }
            } else {
                // either the reflected slot is a function or it's undefined
                // if it's undefined then the reflected slot is the same as non reflected slot - like nb$add
                slots[reflect_name] = reflected_slot_def.slot || slots[slot_name];
            }
        }
    });
}

function _set_up_sequence_to_number_slots(slots) {
    const sequenceToNumber = Sk.sequenceAndMappingSlots;
    Object.keys(sequenceToNumber).forEach((slot_name) => {
        if (slots[slot_name] !== undefined) {
            const equiv_slots = sequenceToNumber[slot_name];
            equiv_slots.forEach((equv_slot_name) => {
                slots[equv_slot_name] = slots[slot_name];
            });
        }
    });
}

/**
 * 
 * @param {FunctionConstructor} klass
 * @param {Object=} slots 
 */
Sk.abstr.setUpSlots = function (klass, slots) {
    if (Sk.builtin.wrapper_descriptor === undefined) {
        return;
    }
    const proto = klass.prototype;
    slots = slots || proto.tp$slots || {};

    // set up the raw slots on the proto

    // start with tp$new - we couldn't call genericnew before the klass was defined so call it now
    if (slots.tp$new === Sk.generic.new) {
        slots.tp$new = Sk.generic.new(klass);
    }

    // wrap ob$eq slots into tp$richcompare
    if (slots.tp$richcompare) {
        // Either a klass defines a tp$richcompare slot or ob$eq slots
        // if tp$richcompare is defined then create all the ob$eq slots
        _set_up_richcompare_wrappers(slots);
    }

    // setup some reflected slots
    if (slots.tp$as_number) {
        _set_up_reflected_number_slots(slots);
    }

    // turn sequence slots into number slots
    if (slots.tp$as_sequence_or_mapping) {
        _set_up_sequence_to_number_slots(slots);
    }

    // make all slots non-enumerable - makes the dir implementation easier
    Object.entries(slots).forEach(([slot_name, value]) => {
        Object.defineProperty(proto, slot_name, {
            value: value,
            writable: true,
        });
    });

    if (slots.tp$new) {
        proto.__new__ = new Sk.builtin.sk_method(Sk.generic.newMethodDef, klass);
        // this property is used in the implementation of calling __new__
        Object.defineProperty(proto, "sk$staticNew", {value: klass, writable: true});
    }

    function wrap_func(dunder_name, wrapped_func) {
        const slot_def = Sk.slots[dunder_name];
        // we do this here because in the generic.wrapperCall methods the wrapped_func
        // the wrapped func should have a $name property and a $flags property (for minArgs)
        proto[dunder_name] = new Sk.builtin.wrapper_descriptor(klass, slot_def, wrapped_func);
    }

    function set_up_slot(dunder_name, wrapped_func) {
        // some slots get multpile dunders
        if (typeof dunder_name === "string") {
            wrap_func(dunder_name, wrapped_func);
        } else {
            dunder_name.forEach((related_dunder_name) => {
                wrap_func(related_dunder_name, wrapped_func);
            });
        }
    }

    // main slots
    Sk.subSlots.main_slots.forEach(([slot_name, dunder_name]) => {
        const wrapped_func = slots[slot_name];
        if (wrapped_func !== undefined) {
            set_up_slot(dunder_name, wrapped_func);
        }
    });

    // __hash__
    const hash = slots.tp$hash;
    if (hash !== undefined) {   
        if (typeof hash === "function") {
            wrap_func("__hash__", hash);
        } else if (hash === Sk.builtin.none.none$) {
            proto.__hash__ = hash;
        } else {
            Sk.asserts.fail("invalid tp$hash");
        }
    }

    // number_slots
    if (slots.tp$as_number) {
        Sk.subSlots.number_slots.forEach(([slot_name, dunder_name]) => {
            const wrapped_func = slots[slot_name];
            if (wrapped_func !== undefined) {
                set_up_slot(dunder_name, wrapped_func);
            }
        });
    }

    // as_sequence_or_mapping slots
    if (slots.tp$as_sequence_or_mapping) {
        Sk.subSlots.sequence_and_mapping_slots.forEach(([slot_name, dunder_name]) => {
            const wrapped_func = slots[slot_name];
            if (wrapped_func !== undefined) {
                set_up_slot(dunder_name, wrapped_func);
            }
        });
    }

    Object.defineProperty(proto, "tp$slots", { value: null, writable: true });
};

/**
 * @function
 * @param {string} typename
 * @param {Object} options An object literal that provides the functionality of the typobject
 * @returns {FunctionConstructor}
 *
 * @description
 * this can be called to create a native typeobj
 * options include
 * ```
 * - base: default to {@link Sk.builtin.object}
 * - meta: default to {@link Sk.builtin.type}
 *
 * - slots: skulpt slot functions that will be allocated slot wrappers
 * - methods: method objects `{$meth: Function, $flags: callmethod, $doc: string, $textsic: string|null}`,
 * - getsets: getset objects `{$get: Function, $set: Function, $doc: string}`,
 * - classmethods: classmethod objects `{$meth: Function, $flags: callmethod, $doc: string, $textsic: string|null}`,
 *
 * - flags: Object allocated directly onto class like `klass.sk$acceptable_as_base_class`
 * - proto: Object allocated onto the prototype useful for private methods
 * ```
 * See most builtin type objects for examples
 *
 */
Sk.abstr.buildNativeClass = function (typename, options) {
    options = options || {};
    Sk.asserts.assert(options.hasOwnProperty("constructor"), "A constructor is required to build a native class");

    /**@type {FunctionConstructor} */
    let typeobject = options.constructor;

    let mod;
    if (typename.includes(".")) {
        // you should define the module like "collections.defaultdict" for static classes
        const mod_typename = typename.split(".");
        typename = mod_typename.pop();
        mod = mod_typename.join(".");
    }
    // set the prototypical chains for inheritance
    Sk.abstr.setUpInheritance(typename, typeobject, options.base, options.meta);

    // Native classes can only be single inheritance objects
    Sk.abstr.setUpBuiltinMro(typeobject);

    const type_proto = typeobject.prototype;

    Object.defineProperties(type_proto, {
        // memoise these and make them null later
        tp$slots: {value: options.slots, writable: true},
        tp$getsets: {value: options.getsets, writable: true},
        tp$methods: {value: options.methods, writable: true},
        tp$classmethods: {value: options.classmethods, writable: true},
    });

    Sk.abstr.setUpSlots(typeobject, /**@lends {typeobject.prototype} */ options.slots || {});
    Sk.abstr.setUpMethods(typeobject, options.methods);
    Sk.abstr.setUpGetSets(typeobject, options.getsets);
    Sk.abstr.setUpClassMethods(typeobject, options.classmethods);

    if (mod !== undefined) {
        type_proto.__module__ = new Sk.builtin.str(mod);
    }
    
    const proto = options.proto || {};
    Object.entries(proto).forEach(([p, val]) => {
        Object.defineProperty(type_proto, p, {
            value: val,
            writable: true,
            enumerable: !(p.includes("$") || (p in Object.prototype)), 
            // only make these private in these cases otherwise they're public methods
        });
    });

    const flags = options.flags || {};
    Object.entries(flags).forEach(([flag, val]) => {
        Object.defineProperty(typeobject, flag, {
            value: val,
            writable: true,
        }); 
    });

    // str might not have been created yet
    if (Sk.builtin.str !== undefined && type_proto.hasOwnProperty("tp$doc") && !type_proto.hasOwnProperty("__doc__")) {
        const docstr = type_proto.tp$doc || null;
        if (typeof docstr === "string") {
            type_proto.__doc__ = new Sk.builtin.str(docstr);
        } else {
            type_proto.__doc__ = Sk.builtin.none.none$;
        }
    }
    return typeobject;
};

/**
 * @function
 * 
 * @param {string} typename e.g. "itertools.chain"
 * @param {Object} iterator minimum options `{constructor: function, iternext: function}`
 *
 * @description
 * effectively a wrapper for easily defining an iterator
 * `tp$iter` slot is added and returns self
 *
 * define a constructor in the usual way
 *
 * define `tp$iternext` using iternext in the object literal
 * mostly as a convenience
 * you can also define `tp$iternext` in the slots which will take priority
 *
 * the main benefit of this helper function is to reduce some repetitive code for defining an iterator class
 *
 * If you want a generic iterator see {@link Sk.miscival.iterator}
 *
 * 
 * @example
 * Sk.builtin.tuple_iter_ = Sk.abstr.buildIteratorClass("tuple_iterator", {
    constructor: function tuple_iter_(tuple) {
        this.$index = 0;
        this.$seq = tuple.sk$asarray();
    },
    iternext: function () {
        if (this.$index >= this.$seq.length) {
            return undefined;
        }
        return this.$seq[this.$index++];
    }
});
 * @returns {FunctionConstructor}
 *
 */

Sk.abstr.buildIteratorClass = function (typename, iterator) {
    Sk.asserts.assert(iterator.hasOwnProperty("constructor"), "must provide a constructor");
    iterator.slots = iterator.slots || {};
    iterator.slots.tp$iter = Sk.generic.selfIter;
    iterator.slots.tp$iternext = iterator.slots.tp$iternext || iterator.iternext;
    iterator.slots.tp$getattr = iterator.slots.tp$getattr || Sk.generic.getAttr;
    let ret = Sk.abstr.buildNativeClass(typename, iterator);
    Sk.abstr.built$iterators.push(ret);
    return ret;
};

Sk.abstr.built$iterators = [];

Sk.abstr.setUpModuleMethods = function (module_name, module, method_defs) {
    Object.entries(method_defs).forEach(([method_name, method_def]) => {
        method_def.$name = method_def.$name || method_name; // operator e.g. some methods share method_defs
        module[method_name] = new Sk.builtin.sk_method(method_def, null, module_name);
    });
};

/**
 * Call the super constructor of the provided class, with the object `self` as
 * the `this` value of that constructor. Any arguments passed to this function
 * after `self` will be passed as-is to the constructor.
 *
 * @param  {*} thisClass The subclass.
 * @param  {Object} self      The instance of the subclas.
 * @param  {...?} args Arguments to pass to the constructor.
 * @return {undefined}
 * @deprecated
 */
Sk.abstr.superConstructor = function (thisClass, self, args) {
    var argumentsForConstructor = Array.prototype.slice.call(arguments, 2);
    thisClass.prototype.tp$base.apply(self, argumentsForConstructor);
};


/***/ }),

/***/ "./src/assert-dev.js":
/*!***************************!*\
  !*** ./src/assert-dev.js ***!
  \***************************/
/*! no static exports found */
/***/ (function(module, exports) {

Sk.asserts = {ENABLE_ASSERTS: true};

/**
 * Cause assertion failure when condition is false.
 * 
 * @param {*} condition condition to check
 * @param {string=} message error message
 */
Sk.asserts.assert = function (condition, message) {
    if (Sk.asserts.ENABLE_ASSERTS && !condition) {
        var msg = "Assertion failure";
        if (message) {
            msg = msg + ": " + message;
        }
        throw new Error(msg);
    }
    return condition;
};
Sk.exportSymbol("Sk.asserts.assert", Sk.asserts.assert);

/**
 * Cause assertion failure.
 * 
 * @param {string=} message error message
 */
Sk.asserts.fail = function (message) {
    if (Sk.asserts.ENABLE_ASSERTS) {
        var msg = "Assertion failure";
        if (message) {
            msg = msg + ": " + message;
        }
        throw new Error(msg);
    }
};
Sk.exportSymbol("Sk.asserts.fail", Sk.asserts.fail);


/***/ }),

/***/ "./src/ast.js":
/*!********************!*\
  !*** ./src/ast.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

//
// This is pretty much a straight port of ast.c from CPython 3.7.3
// (with a few leftovers from 2.6.5).
//
// The previous version was easier to work with and more JS-ish, but having a
// somewhat different ast structure than cpython makes testing more difficult.
//
// This way, we can use a dump from the ast module on any arbitrary python
// code and know that we're the same up to ast level, at least.
//

var SYM = Sk.ParseTables.sym;
var TOK = Sk.token.tokens;
var COMP_GENEXP = 0;
var COMP_LISTCOMP = 1;
var COMP_SETCOMP = 2;
var NULL = null;
var _slice_kind = { 
    Slice_kind: 1,
    ExtSlice_kind: 2, 
    Index_kind: 3
};

var _expr_kind = {
    BoolOp_kind: 1, NamedExpr_kind: 2, BinOp_kind: 3, UnaryOp_kind: 4,
    Lambda_kind: 5, IfExp_kind: 6, Dict_kind: 7, Set_kind: 8,
    ListComp_kind: 9, SetComp_kind: 10, DictComp_kind: 11,
    GeneratorExp_kind: 12, Await_kind: 13, Yield_kind: 14,
    YieldFrom_kind: 15, Compare_kind: 16, Call_kind: 17,
    FormattedValue_kind: 18, JoinedStr_kind: 19, Constant_kind: 20,
    Attribute_kind: 21, Subscript_kind: 22, Starred_kind: 23,
    Name_kind: 24, List_kind: 25, Tuple_kind: 26 };

/** @constructor */
function Compiling (encoding, filename, c_flags) {
    this.c_encoding = encoding;
    this.c_filename = filename;
    this.c_flags = c_flags || 0;
}

/**
 * @return {number}
 */
function NCH (n) {
    Sk.asserts.assert(n !== undefined, "node must be defined");
    if (n.children === null) {
        return 0;
    }
    return n.children.length;
}

function CHILD (n, i) {
    Sk.asserts.assert(n !== undefined, "node must be defined");
    Sk.asserts.assert(i !== undefined, "index of child must be specified");
    return n.children[i];
}

function REQ (n, type) {
    Sk.asserts.assert(n.type === type, "node wasn't expected type");
}

function TYPE(n) {
    return n.type;
}

function LINENO(n) {
    return n.lineno;
}

function STR(ch) {
    return ch.value;
}

function ast_error(c, n, msg) {
    throw new Sk.builtin.SyntaxError(msg, c.c_filename, n.lineno);
}

function strobj (s) {
    Sk.asserts.assert(typeof s === "string", "expecting string, got " + (typeof s));
    return new Sk.builtin.str(s);
}

/** @return {number} */
function numStmts (n) {
    var ch;
    var i;
    var cnt;
    switch (n.type) {
        case SYM.single_input:
            if (CHILD(n, 0).type === TOK.T_NEWLINE) {
                return 0;
            }
            else {
                return numStmts(CHILD(n, 0));
            }
        case SYM.file_input:
            cnt = 0;
            for (i = 0; i < NCH(n); ++i) {
                ch = CHILD(n, i);
                if (ch.type === SYM.stmt) {
                    cnt += numStmts(ch);
                }
            }
            return cnt;
        case SYM.stmt:
            return numStmts(CHILD(n, 0));
        case SYM.compound_stmt:
            return 1;
        case SYM.simple_stmt:
            return Math.floor(NCH(n) / 2); // div 2 is to remove count of ;s
        case SYM.suite:
            if (NCH(n) === 1) {
                return numStmts(CHILD(n, 0));
            }
            else {
                cnt = 0;
                for (i = 2; i < NCH(n) - 1; ++i) {
                    cnt += numStmts(CHILD(n, i));
                }
                return cnt;
            }
            break;
        default:
            Sk.asserts.fail("Non-statement found");
    }
    return 0;
}

function forbiddenCheck (c, n, x, lineno) {
    if (x instanceof Sk.builtin.str) {
        x = x.v;
    }
    if (x === "None") {
        throw new Sk.builtin.SyntaxError("assignment to None", c.c_filename, lineno);
    }
    if (x === "True" || x === "False") {
        throw new Sk.builtin.SyntaxError("assignment to True or False is forbidden", c.c_filename, lineno);
    }
}

/**
 * Set the context ctx for e, recursively traversing e.
 *
 * Only sets context for expr kinds that can appear in assignment context as
 * per the asdl file.
 */
function setContext (c, e, ctx, n) {
    var i;
    var exprName;
    var s;
    Sk.asserts.assert(ctx !== Sk.astnodes.AugStore && ctx !== Sk.astnodes.AugLoad, "context not AugStore or AugLoad");
    s = null;
    exprName = null;

    switch (e.constructor) {
        case Sk.astnodes.Attribute:
        case Sk.astnodes.Name:
            if (ctx === Sk.astnodes.Store) {
                forbiddenCheck(c, n, e.attr, n.lineno);
            }
            e.ctx = ctx;
            break;
        case Sk.astnodes.Starred:
            e.ctx = ctx;
            setContext(c, e.value, ctx, n);
            break;
        case Sk.astnodes.Subscript:
            e.ctx = ctx;
            break;
        case Sk.astnodes.List:
            e.ctx = ctx;
            s = e.elts;
            break;
        case Sk.astnodes.Tuple:
            if (e.elts.length === 0) {
                throw new Sk.builtin.SyntaxError("can't assign to ()", c.c_filename, n.lineno);
            }
            e.ctx = ctx;
            s = e.elts;
            break;
        case Sk.astnodes.Lambda:
            exprName = "lambda";
            break;
        case Sk.astnodes.Call:
            exprName = "function call";
            break;
        case Sk.astnodes.BoolOp:
        case Sk.astnodes.BinOp:
        case Sk.astnodes.UnaryOp:
            exprName = "operator";
            break;
        case Sk.astnodes.GeneratorExp:
            exprName = "generator expression";
            break;
        case Sk.astnodes.Yield:
            exprName = "yield expression";
            break;
        case Sk.astnodes.ListComp:
            exprName = "list comprehension";
            break;
        case Sk.astnodes.SetComp:
            exprName = "set comprehension";
            break;
        case Sk.astnodes.DictComp:
            exprName = "dict comprehension";
            break;
        case Sk.astnodes.Dict:
        case Sk.astnodes.Set:
        case Sk.astnodes.Num:
        case Sk.astnodes.Str:
            exprName = "literal";
            break;
        case Sk.astnodes.NameConstant:
            exprName = "True, False or None";
            break;
        case Sk.astnodes.Compare:
            exprName = "comparison";
            break;
        case Sk.astnodes.Repr:
            exprName = "repr";
            break;
        case Sk.astnodes.IfExp:
            exprName = "conditional expression";
            break;
        default:
            Sk.asserts.fail("unhandled expression in assignment");
    }
    if (exprName) {
        throw new Sk.builtin.SyntaxError("can't " + (ctx === Sk.astnodes.Store ? "assign to" : "delete") + " " + exprName, c.c_filename, n.lineno);
    }

    if (s) {
        for (i = 0; i < s.length; ++i) {
            setContext(c, s[i], ctx, n);
        }
    }
}

var operatorMap = {};
(function () {
    operatorMap[TOK.T_VBAR] = Sk.astnodes.BitOr;
    operatorMap[TOK.T_CIRCUMFLEX] = Sk.astnodes.BitXor;
    operatorMap[TOK.T_AMPER] = Sk.astnodes.BitAnd;
    operatorMap[TOK.T_LEFTSHIFT] = Sk.astnodes.LShift;
    operatorMap[TOK.T_RIGHTSHIFT] = Sk.astnodes.RShift;
    operatorMap[TOK.T_PLUS] = Sk.astnodes.Add;
    operatorMap[TOK.T_MINUS] = Sk.astnodes.Sub;
    operatorMap[TOK.T_STAR] = Sk.astnodes.Mult;
    operatorMap[TOK.T_SLASH] = Sk.astnodes.Div;
    operatorMap[TOK.T_DOUBLESLASH] = Sk.astnodes.FloorDiv;
    operatorMap[TOK.T_PERCENT] = Sk.astnodes.Mod;
}());

Sk.setupOperators = function (py3) {
    if (py3) {
        operatorMap[TOK.T_AT] = Sk.astnodes.MatMult;
    } else {
        if (operatorMap[TOK.T_AT]) {
            delete operatorMap[TOK.T_AT];
        }
    }
}
Sk.exportSymbol("Sk.setupOperators", Sk.setupOperators);

function getOperator (n) {
    if (operatorMap[n.type] === undefined) {
        throw new Sk.builtin.SyntaxError("invalid syntax", n.type, n.lineno);
    }
    return operatorMap[n.type];
}

function new_identifier(n, c) {
    if (n.value) {
        return new Sk.builtin.str(n.value);
    }

    return new Sk.builtin.str(n);
}

function astForCompOp (c, n) {
    /* comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is'
     |'is' 'not'
     */
    REQ(n, SYM.comp_op);
    if (NCH(n) === 1) {
        n = CHILD(n, 0);
        switch (n.type) {
            case TOK.T_LESS:
                return Sk.astnodes.Lt;
            case TOK.T_GREATER:
                return Sk.astnodes.Gt;
            case TOK.T_EQEQUAL:
                return Sk.astnodes.Eq;
            case TOK.T_LESSEQUAL:
                return Sk.astnodes.LtE;
            case TOK.T_GREATEREQUAL:
                return Sk.astnodes.GtE;
            case TOK.T_NOTEQUAL:
                return Sk.astnodes.NotEq;
            case TOK.T_NAME:
                if (n.value === "in") {
                    return Sk.astnodes.In;
                }
                if (n.value === "is") {
                    return Sk.astnodes.Is;
                }
        }
    }
    else if (NCH(n) === 2) {
        if (CHILD(n, 0).type === TOK.T_NAME) {
            if (CHILD(n, 1).value === "in") {
                return Sk.astnodes.NotIn;
            }
            if (CHILD(n, 0).value === "is") {
                return Sk.astnodes.IsNot;
            }
        }
    }
    Sk.asserts.fail("invalid comp_op");
}

function copy_location(e, n)
{
    if (e) {
        e.lineno = LINENO(n);
        e.col_offset = n.col_offset;
        e.end_lineno = n.end_lineno;
        e.end_col_offset = n.end_col_offset;
    }
    return e;
}

function seq_for_testlist (c, n) {
    /* testlist: test (',' test)* [',']
       testlist_star_expr: test|star_expr (',' test|star_expr)* [',']
    */
    var i;
    var seq = [];
    Sk.asserts.assert(n.type === SYM.testlist ||
        n.type === SYM.testlist_star_expr ||
        n.type === SYM.listmaker ||
        n.type === SYM.testlist_comp ||
        n.type === SYM.testlist_safe ||
        n.type === SYM.testlist1, "node type must be listlike");
    for (i = 0; i < NCH(n); i += 2) {
        Sk.asserts.assert(CHILD(n, i).type === SYM.test || CHILD(n, i).type === SYM.old_test || CHILD(n, i).type === SYM.star_expr);
        seq[i / 2] = ast_for_expr(c, CHILD(n, i));
    }
    return seq;
}

function astForSuite (c, n) {
    /* suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT */
    var j;
    var num;
    var i;
    var end;
    var ch;
    var pos;
    var seq;
    REQ(n, SYM.suite);
    seq = [];
    pos = 0;
    if (CHILD(n, 0).type === SYM.simple_stmt) {
        n = CHILD(n, 0);
        /* simple_stmt always ends with an NEWLINE and may have a trailing
         * SEMI. */
        end = NCH(n) - 1;
        if (CHILD(n, end - 1).type === TOK.T_SEMI) {
            end -= 1;
        }
        for (i = 0; i < end; i += 2) // by 2 to skip ;
        {
            seq[pos++] = astForStmt(c, CHILD(n, i));
        }
    }
    else {
        for (i = 2; i < NCH(n) - 1; ++i) {
            ch = CHILD(n, i);
            REQ(ch, SYM.stmt);
            num = numStmts(ch);
            if (num === 1) {
                // small_stmt or compound_stmt w/ only 1 child
                seq[pos++] = astForStmt(c, ch);
            }
            else {
                ch = CHILD(ch, 0);
                REQ(ch, SYM.simple_stmt);
                for (j = 0; j < NCH(ch); j += 2) {
                    if (NCH(CHILD(ch, j)) === 0) {
                        Sk.asserts.assert(j + 1 === NCH(ch));
                        break;
                    }
                    seq[pos++] = astForStmt(c, CHILD(ch, j));
                }
            }
        }
    }
    Sk.asserts.assert(pos === numStmts(n));
    return seq;
}

function astForExceptClause (c, exc, body) {
    /* except_clause: 'except' [test [(',' | 'as') test]] */
    var e;
    REQ(exc, SYM.except_clause);
    REQ(body, SYM.suite);
    if (NCH(exc) === 1) {
        return new Sk.astnodes.ExceptHandler(null, null, astForSuite(c, body), exc.lineno, exc.col_offset);
    }
    else if (NCH(exc) === 2) {
        return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), null, astForSuite(c, body), exc.lineno, exc.col_offset);
    }
    else if (NCH(exc) === 4) {
        if (Sk.__future__.python3 && CHILD(exc, 2).value == ",") {
            ast_error(c, exc, "Old-style 'except' clauses are not supported in Python 3");
        }

        var expression = ast_for_expr(c, CHILD(exc, 1));
        e = ast_for_expr(c, CHILD(exc, 3));
        setContext(c, e, Sk.astnodes.Store, CHILD(exc, 3));
        return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), e, astForSuite(c, body), exc.lineno, exc.col_offset);
    }
    Sk.asserts.fail("wrong number of children for except clause");
}

function astForTryStmt (c, n) {
    var exceptSt;
    var i;
    var handlers = [];
    var nc = NCH(n);
    var nexcept = (nc - 3) / 3;
    var body, orelse = [],
        finally_ = null;

    REQ(n, SYM.try_stmt);
    body = astForSuite(c, CHILD(n, 2));
    if (CHILD(n, nc - 3).type === TOK.T_NAME) {
        if (CHILD(n, nc - 3).value === "finally") {
            if (nc >= 9 && CHILD(n, nc - 6).type === TOK.T_NAME) {
                /* we can assume it's an "else",
                 because nc >= 9 for try-else-finally and
                 it would otherwise have a type of except_clause */
                orelse = astForSuite(c, CHILD(n, nc - 4));
                nexcept--;
            }

            finally_ = astForSuite(c, CHILD(n, nc - 1));
            nexcept--;
        }
        else {
            /* we can assume it's an "else",
             otherwise it would have a type of except_clause */
            orelse = astForSuite(c, CHILD(n, nc - 1));
            nexcept--;
        }
    }
    else if (CHILD(n, nc - 3).type !== SYM.except_clause) {
        throw new Sk.builtin.SyntaxError("malformed 'try' statement", c.c_filename, n.lineno);
    }

    if (nexcept > 0) {
        /* process except statements to create a try ... except */
        for (i = 0; i < nexcept; i++) {
            handlers[i] = astForExceptClause(c, CHILD(n, 3 + i * 3), CHILD(n, 5 + i * 3));
        }
    }

    Sk.asserts.assert(!!finally_ || handlers.length != 0);
    return new Sk.astnodes.Try(body, handlers, orelse, finally_, n.lineno, n.col_offset);
}

function astForDottedName (c, n) {
    var i;
    var e;
    var id;
    var col_offset;
    var lineno;
    REQ(n, SYM.dotted_name);
    lineno = n.lineno;
    col_offset = n.col_offset;
    id = strobj(CHILD(n, 0).value);
    e = new Sk.astnodes.Name(id, Sk.astnodes.Load, lineno, col_offset);
    for (i = 2; i < NCH(n); i += 2) {
        id = strobj(CHILD(n, i).value);
        e = new Sk.astnodes.Attribute(e, id, Sk.astnodes.Load, lineno, col_offset);
    }
    return e;
}

function astForDecorator (c, n) {
    /* decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE */
    var nameExpr;
    REQ(n, SYM.decorator);
    REQ(CHILD(n, 0), TOK.T_AT);
    REQ(CHILD(n, NCH(n) - 1), TOK.T_NEWLINE);
    nameExpr = astForDottedName(c, CHILD(n, 1));
    if (NCH(n) === 3) // no args
    {
        return nameExpr;
    }
    else if (NCH(n) === 5) // call with no args
    {
        return new Sk.astnodes.Call(nameExpr, [], [], null, null, n.lineno, n.col_offset);
    }
    else {
        return ast_for_call(c, CHILD(n, 3), nameExpr);
    }
}

function astForDecorators (c, n) {
    var i;
    var decoratorSeq;
    REQ(n, SYM.decorators);
    decoratorSeq = [];
    for (i = 0; i < NCH(n); ++i) {
        decoratorSeq[i] = astForDecorator(c, CHILD(n, i));
    }
    return decoratorSeq;
}

function ast_for_decorated (c, n) {
    /* decorated: decorators (classdef | funcdef | async_funcdef) */
    var thing = null;
    var decorator_seq = null;

    REQ(n, SYM.decorated);

    decorator_seq = astForDecorators(c, CHILD(n, 0));
    Sk.asserts.assert(TYPE(CHILD(n, 1)) == SYM.funcdef ||
            TYPE(CHILD(n, 1)) == SYM.async_funcdef ||
            TYPE(CHILD(n, 1)) == SYM.classdef);

    if (TYPE(CHILD(n, 1)) == SYM.funcdef) {
        thing = ast_for_funcdef(c, CHILD(n, 1), decorator_seq);
    } else if (TYPE(CHILD(n, 1)) == SYM.classdef) {
        thing = astForClassdef(c, CHILD(n, 1), decorator_seq);
    } else if (TYPE(CHILD(n, 1)) == SYM.async_funcdef) {
        thing = ast_for_async_funcdef(c, CHILD(n, 1), decorator_seq);
    }
    /* we count the decorators in when talking about the class' or
        * function's line number */
    if (thing) {
        thing.lineno = LINENO(n);
        thing.col_offset = n.col_offset;
    }
    return thing;
}

/* with_item: test ['as' expr] */
function ast_for_with_item (c, n) {
    var context_expr, optional_vars;
    REQ(n, SYM.with_item);
    context_expr = ast_for_expr(c, CHILD(n, 0));
    if (NCH(n) == 3) {
        optional_vars = ast_for_expr(c, CHILD(n, 2));
        setContext(c, optional_vars, Sk.astnodes.Store, n);
    }

    return new Sk.astnodes.withitem(context_expr, optional_vars);
}

/* with_stmt: 'with' with_item (',' with_item)* ':' suite */
function ast_for_with_stmt(c, n0, is_async) {
    const n = is_async ? CHILD(n0, 1) : n0;
    var i
    var items = [], body;

    REQ(n, SYM.with_stmt);

    for (i = 1; i < NCH(n) - 2; i += 2) {
        var item = ast_for_with_item(c, CHILD(n, i));
        items[(i - 1) / 2] = item;
    }

    body = astForSuite(c, CHILD(n, NCH(n) - 1));

    if (is_async) {
        return new Sk.astnodes.AsyncWith(items, body, LINENO(n0), n0.col_offset);
    } else {
        return new Sk.astnodes.With(items, body, LINENO(n), n.col_offset);
    }
}

function astForExecStmt (c, n) {
    var expr1, globals = null, locals = null;
    var nchildren = NCH(n);
    Sk.asserts.assert(nchildren === 2 || nchildren === 4 || nchildren === 6);

    /* exec_stmt: 'exec' expr ['in' test [',' test]] */
    REQ(n, SYM.exec_stmt);
    expr1 = ast_for_expr(c, CHILD(n, 1));
    if (nchildren >= 4) {
        globals = ast_for_expr(c, CHILD(n, 3));
    }
    if (nchildren === 6) {
        locals = ast_for_expr(c, CHILD(n, 5));
    }
    return new Sk.astnodes.Exec(expr1, globals, locals, n.lineno, n.col_offset);
}

function astForIfStmt (c, n) {
    /* if_stmt: 'if' test ':' suite ('elif' test ':' suite)*
     ['else' ':' suite]
     */
    var off;
    var i;
    var orelse;
    var hasElse;
    var nElif;
    var decider;
    var s;
    REQ(n, SYM.if_stmt);
    if (NCH(n) === 4) {
        return new Sk.astnodes.If(
            ast_for_expr(c, CHILD(n, 1)),
            astForSuite(c, CHILD(n, 3)),
            [], n.lineno, n.col_offset);
    }

    s = CHILD(n, 4).value;
    decider = s.charAt(2); // elSe or elIf
    if (decider === "s") {
        return new Sk.astnodes.If(
            ast_for_expr(c, CHILD(n, 1)),
            astForSuite(c, CHILD(n, 3)),
            astForSuite(c, CHILD(n, 6)),
            n.lineno, n.col_offset);
    }
    else if (decider === "i") {
        nElif = NCH(n) - 4;
        hasElse = false;
        orelse = [];

        /* must reference the child nElif+1 since 'else' token is third, not
         * fourth child from the end. */
        if (CHILD(n, nElif + 1).type === TOK.T_NAME &&
            CHILD(n, nElif + 1).value.charAt(2) === "s") {
            hasElse = true;
            nElif -= 3;
        }
        nElif /= 4;

        if (hasElse) {
            orelse = [
                new Sk.astnodes.If(
                    ast_for_expr(c, CHILD(n, NCH(n) - 6)),
                    astForSuite(c, CHILD(n, NCH(n) - 4)),
                    astForSuite(c, CHILD(n, NCH(n) - 1)),
                    CHILD(n, NCH(n) - 6).lineno,
                    CHILD(n, NCH(n) - 6).col_offset)];
            nElif--;
        }

        for (i = 0; i < nElif; ++i) {
            off = 5 + (nElif - i - 1) * 4;
            orelse = [
                new Sk.astnodes.If(
                    ast_for_expr(c, CHILD(n, off)),
                    astForSuite(c, CHILD(n, off + 2)),
                    orelse,
                    CHILD(n, off).lineno,
                    CHILD(n, off).col_offset)];
        }
        return new Sk.astnodes.If(
            ast_for_expr(c, CHILD(n, 1)),
            astForSuite(c, CHILD(n, 3)),
            orelse, n.lineno, n.col_offset);
    }

    Sk.asserts.fail("unexpected token in 'if' statement");
}

function ast_for_exprlist (c, n, context) {
    var e;
    var i;
    var seq;
    REQ(n, SYM.exprlist);
    seq = [];
    for (i = 0; i < NCH(n); i += 2) {
        e = ast_for_expr(c, CHILD(n, i));
        seq[i / 2] = e;
        if (context) {
            setContext(c, e, context, CHILD(n, i));
        }
    }
    return seq;
}

function astForDelStmt (c, n) {
    /* del_stmt: 'del' exprlist */
    REQ(n, SYM.del_stmt);
    return new Sk.astnodes.Delete(ast_for_exprlist(c, CHILD(n, 1), Sk.astnodes.Del), n.lineno, n.col_offset);
}

function astForGlobalStmt (c, n) {
    /* global_stmt: 'global' NAME (',' NAME)* */
    var i;
    var s = [];
    REQ(n, SYM.global_stmt);
    for (i = 1; i < NCH(n); i += 2) {
        s[(i - 1) / 2] = strobj(CHILD(n, i).value);
    }
    return new Sk.astnodes.Global(s, n.lineno, n.col_offset);
}

function astForAssertStmt (c, n) {
    /* assert_stmt: 'assert' test [',' test] */
    REQ(n, SYM.assert_stmt);
    if (NCH(n) === 2) {
        return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), null, n.lineno, n.col_offset);
    }
    else if (NCH(n) === 4) {
        return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), ast_for_expr(c, CHILD(n, 3)), n.lineno, n.col_offset);
    }
    Sk.asserts.fail("improper number of parts to assert stmt");
}

function aliasForImportName (c, n) {
    /*
     import_as_name: NAME ['as' NAME]
     dotted_as_name: dotted_name ['as' NAME]
     dotted_name: NAME ('.' NAME)*
     */

    var i;
    var a;
    var name;
    var str;
    loop: while (true) {
        switch (n.type) {
            case SYM.import_as_name:
                str = null;
                name = strobj(CHILD(n, 0).value);
                if (NCH(n) === 3) {
                    str = CHILD(n, 2).value;
                }
                return new Sk.astnodes.alias(name, str == null ? null : strobj(str));
            case SYM.dotted_as_name:
                if (NCH(n) === 1) {
                    n = CHILD(n, 0);
                    continue loop;
                }
                else {
                    a = aliasForImportName(c, CHILD(n, 0));
                    Sk.asserts.assert(!a.asname);
                    a.asname = strobj(CHILD(n, 2).value);
                    return a;
                }
                break;
            case SYM.dotted_name:
                if (NCH(n) === 1) {
                    return new Sk.astnodes.alias(strobj(CHILD(n, 0).value), null);
                }
                else {
                    // create a string of the form a.b.c
                    str = "";
                    for (i = 0; i < NCH(n); i += 2) {
                        str += CHILD(n, i).value + ".";
                    }
                    return new Sk.astnodes.alias(strobj(str.substr(0, str.length - 1)), null);
                }
                break;
            case TOK.T_STAR:
                return new Sk.astnodes.alias(strobj("*"), null);
            default:
                throw new Sk.builtin.SyntaxError("unexpected import name", c.c_filename, n.lineno);
        }
        break;
    }
}

function astForImportStmt (c, n) {
    /*
     import_stmt: import_name | import_from
     import_name: 'import' dotted_as_names
     import_from: 'from' ('.'* dotted_name | '.') 'import'
     ('*' | '(' import_as_names ')' | import_as_names)
     */
    var modname;
    var idx;
    var nchildren;
    var ndots;
    var mod;
    var i;
    var aliases;
    var col_offset;
    var lineno;
    REQ(n, SYM.import_stmt);
    lineno = n.lineno;
    col_offset = n.col_offset;
    n = CHILD(n, 0);
    if (n.type === SYM.import_name) {
        n = CHILD(n, 1);
        REQ(n, SYM.dotted_as_names);
        aliases = [];
        for (i = 0; i < NCH(n); i += 2) {
            aliases[i / 2] = aliasForImportName(c, CHILD(n, i));
        }
        return new Sk.astnodes.Import(aliases, lineno, col_offset);
    }
    else if (n.type === SYM.import_from) {
        mod = null;
        ndots = 0;

        for (idx = 1; idx < NCH(n); ++idx) {
            if (CHILD(n, idx).type === SYM.dotted_name) {
                mod = aliasForImportName(c, CHILD(n, idx));
                idx++;
                break;
            }
            else if (CHILD(n, idx).type === TOK.T_DOT) {
                ndots++;
            }
            else if (CHILD(n, idx).type === TOK.T_ELLIPSIS) {
                ndots += 3;
            }
            else {
                break;
            }
        }
        ++idx; // skip the import keyword
        switch (CHILD(n, idx).type) {
            case TOK.T_STAR:
                // from ... import
                n = CHILD(n, idx);
                nchildren = 1;
                break;
            case TOK.T_LPAR:
                // from ... import (x, y, z)
                n = CHILD(n, idx + 1);
                nchildren = NCH(n);
                break;
            case SYM.import_as_names:
                // from ... import x, y, z
                n = CHILD(n, idx);
                nchildren = NCH(n);
                if (nchildren % 2 === 0) {
                    throw new Sk.builtin.SyntaxError("trailing comma not allowed without surrounding parentheses", c.c_filename, n.lineno);
                }
                break;
            default:
                throw new Sk.builtin.SyntaxError("Unexpected node-type in from-import", c.c_filename, n.lineno);
        }
        aliases = [];
        if (n.type === TOK.T_STAR) {
            aliases[0] = aliasForImportName(c, n);
        }
        else {
            for (i = 0; i < NCH(n); i += 2) {
                aliases[i / 2] = aliasForImportName(c, CHILD(n, i));
            }
        }
        modname = mod ? mod.name.v : "";
        return new Sk.astnodes.ImportFrom(strobj(modname), aliases, ndots, lineno, col_offset);
    }
    throw new Sk.builtin.SyntaxError("unknown import statement", c.c_filename, n.lineno);
}

function ast_for_testlistComp(c, n) {
    /* testlist_comp: test ( comp_for | (',' test)* [','] ) */
    /* argument: test [comp_for] */
    Sk.asserts.assert(n.type === SYM.testlist_comp || n.type === SYM.argument);
    if (NCH(n) > 1 && CHILD(n, 1).type === SYM.comp_for) {
        return ast_for_gen_expr(c, n);
    }
    return ast_for_testlist(c, n);
}
function ast_for_genexp(c, n)
{
    Sk.asserts.assert(TYPE(n) == SYM.testlist_comp || TYPE(n) == SYM.argument);
    return ast_for_itercomp(c, n, COMP_GENEXP);
}

function  ast_for_listcomp(c, n) {
    Sk.asserts.assert(TYPE(n) == (SYM.testlist_comp));
    return ast_for_itercomp(c, n, COMP_LISTCOMP);
}

function astForFactor (c, n) {
    /* some random peephole thing that cpy does */
    var expression;
    var pnum;
    var patom;
    var ppower;
    var pfactor;
    if (CHILD(n, 0).type === TOK.T_MINUS && NCH(n) === 2) {
        pfactor = CHILD(n, 1);
        if (pfactor.type === SYM.factor && NCH(pfactor) === 1) {
            ppower = CHILD(pfactor, 0);
            if (ppower.type === SYM.power && NCH(ppower) === 1) {
                patom = CHILD(ppower, 0);
                if (patom.type === SYM.atom) {
                    pnum = CHILD(patom, 0);
                    if (pnum.type === TOK.T_NUMBER) {
                        pnum.value = "-" + pnum.value;
                        return ast_for_atom(c, patom);
                    }
                }
            }
        }
    }

    expression = ast_for_expr(c, CHILD(n, 1));
    switch (CHILD(n, 0).type) {
        case TOK.T_PLUS:
            return new Sk.astnodes.UnaryOp(Sk.astnodes.UAdd, expression, n.lineno, n.col_offset);
        case TOK.T_MINUS:
            return new Sk.astnodes.UnaryOp(Sk.astnodes.USub, expression, n.lineno, n.col_offset);
        case TOK.T_TILDE:
            return new Sk.astnodes.UnaryOp(Sk.astnodes.Invert, expression, n.lineno, n.col_offset);
    }

    Sk.asserts.fail("unhandled factor");
}

function astForForStmt (c, n) {
    /* for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] */
    var target;
    var _target;
    var nodeTarget;
    var seq = [];
    REQ(n, SYM.for_stmt);
    if (NCH(n) === 9) {
        seq = astForSuite(c, CHILD(n, 8));
    }
    nodeTarget = CHILD(n, 1);
    _target = ast_for_exprlist(c, nodeTarget, Sk.astnodes.Store);
    if (NCH(nodeTarget) === 1) {
        target = _target[0];
    }
    else {
        target = new Sk.astnodes.Tuple(_target, Sk.astnodes.Store, n.lineno, n.col_offset);
    }

    return new Sk.astnodes.For(target,
        ast_for_testlist(c, CHILD(n, 3)),
        astForSuite(c, CHILD(n, 5)),
        seq, n.lineno, n.col_offset);
}

function ast_for_call(c, n, func, allowgen)
{
    /*
      arglist: argument (',' argument)*  [',']
      argument: ( test [comp_for] | '*' test | test '=' test | '**' test )
    */

    var i, nargs, nkeywords;
    var ndoublestars;
    var args;
    var keywords;

    REQ(n, SYM.arglist);

    nargs = 0;
    nkeywords = 0;
    for (i = 0; i < NCH(n); i++) {
        var ch = CHILD(n, i);
        if (TYPE(ch) == SYM.argument) {
            if (NCH(ch) == 1) {
                nargs++;
            } else if (TYPE(CHILD(ch, 1)) == SYM.comp_for) {
                nargs++;
                if (!allowgen) {
                    ast_error(c, ch, "invalid syntax");
                }
                if (NCH(n) > 1) {
                    ast_error(c, ch, "Generator expression must be parenthesized");
                }
            } else if (TYPE(CHILD(ch, 0)) == TOK.T_STAR) {
                nargs++;
            } else {
                /* TYPE(CHILD(ch, 0)) == DOUBLESTAR or keyword argument */
                nkeywords++;
            }
        }
    }

    args = []
    keywords = []

    nargs = 0;  /* positional arguments + iterable argument unpackings */
    nkeywords = 0;  /* keyword arguments + keyword argument unpackings */
    ndoublestars = 0;  /* just keyword argument unpackings */
    for (i = 0; i < NCH(n); i++) {
        ch = CHILD(n, i);
        if (TYPE(ch) == SYM.argument) {
            var e;
            var chch = CHILD(ch, 0);
            if (NCH(ch) == 1) {
                /* a positional argument */
                if (nkeywords) {
                    if (ndoublestars) {
                        ast_error(c, chch,
                                "positional argument follows " +
                                "keyword argument unpacking");
                    } else {
                        ast_error(c, chch,
                                "positional argument follows " +
                                "keyword argument");
                    }
                }
                e = ast_for_expr(c, chch);
                if (!e) {
                    return NULL;
                }
                args[nargs++] = e;
            } else if (TYPE(chch) == TOK.T_STAR) {
                /* an iterable argument unpacking */
                var starred;
                if (ndoublestars) {
                    ast_error(c, chch,
                            "iterable argument unpacking follows " +
                            "keyword argument unpacking");
                    return NULL;
                }
                e = ast_for_expr(c, CHILD(ch, 1));
                if (!e) {
                    return NULL;
                }
                starred = new Sk.astnodes.Starred(e, Sk.astnodes.Load, LINENO(chch),
                        chch.col_offset);
                args[nargs++] = starred;
            } else if (TYPE(chch) == TOK.T_DOUBLESTAR) {
                /* a keyword argument unpacking */
                var kw;
                i++;
                e = ast_for_expr(c, CHILD(ch, 1));
                if (!e) {
                    return NULL;
                }
                kw = new Sk.astnodes.keyword(NULL, e);
                keywords[nkeywords++] = kw;
                ndoublestars++;
            } else if (TYPE(CHILD(ch, 1)) == SYM.comp_for) {
                /* the lone generator expression */
                e = ast_for_genexp(c, ch);
                if (!e) {
                    return NULL;
                }
                args[nargs++] = e;
            } else {
                /* a keyword argument */
                var kw;
                var key, tmp;
                var k;

                /* chch is test, but must be an identifier? */
                e = ast_for_expr(c, chch);
                if (!e) {
                    return NULL;
                }
                /* f(lambda x: x[0] = 3) ends up getting parsed with
                 * LHS test = lambda x: x[0], and RHS test = 3.
                 * SF bug 132313 points out that complaining about a keyword
                 * then is very confusing.
                 */
                if (e.constructor === Sk.astnodes.Lambda) {
                    ast_error(c, chch,
                            "lambda cannot contain assignment");
                    return NULL;
                }
                else if (e.constructor !== Sk.astnodes.Name) {
                    ast_error(c, chch,
                            "keyword can't be an expression");
                    return NULL;
                }
                else if (forbiddenCheck(c, e.id, ch, 1)) {
                    return NULL;
                }
                key = e.id;
                for (k = 0; k < nkeywords; k++) {
                    tmp = keywords[k].arg;
                    if (tmp && tmp === key) {
                        ast_error(c, chch,
                                "keyword argument repeated");
                        return NULL;
                    }
                }
                e = ast_for_expr(c, CHILD(ch, 2));
                if (!e)
                    return NULL;
                kw = new Sk.astnodes.keyword(key, e);
                keywords[nkeywords++] = kw;
            }
        }
    }

    return new Sk.astnodes.Call(func, args, keywords, func.lineno, func.col_offset);
}

function ast_for_trailer(c, n, left_expr) {
    /* trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
       subscriptlist: subscript (',' subscript)* [',']
       subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
     */
    REQ(n, SYM.trailer);
    if (TYPE(CHILD(n, 0)) == TOK.T_LPAR) {
        if (NCH(n) == 2)
            return new Sk.astnodes.Call(left_expr, NULL, NULL, LINENO(n),
                        n.col_offset);
        else
            return ast_for_call(c, CHILD(n, 1), left_expr, true);
    }
    else if (TYPE(CHILD(n, 0)) == TOK.T_DOT) {
        var attr_id = new_identifier(CHILD(n, 1));
        if (!attr_id)
            return NULL;
        return new Sk.astnodes.Attribute(left_expr, attr_id, Sk.astnodes.Load,
                         LINENO(n), n.col_offset);
    }
    else {
        REQ(CHILD(n, 0), TOK.T_LSQB);
        REQ(CHILD(n, 2), TOK.T_RSQB);
        n = CHILD(n, 1);
        if (NCH(n) == 1) {
            var slc = astForSlice(c, CHILD(n, 0));
            if (!slc) {
                return NULL;
            }
            return new Sk.astnodes.Subscript(left_expr, slc, Sk.astnodes.Load, LINENO(n), n.col_offset);
        }
        else {
            /* The grammar is ambiguous here. The ambiguity is resolved
               by treating the sequence as a tuple literal if there are
               no slice features.
            */
            var j;
            var slc;
            var e;
            var simple = 1;
            var slices = [], elts;

            for (j = 0; j < NCH(n); j += 2) {
                slc = astForSlice(c, CHILD(n, j));
                if (!slc) {
                    return NULL;
                }
                if (slc.kind != _slice_kind.Index_kind) {
                    simple = 0;
                }
                slices[j / 2] = slc;
            }
            if (!simple) {
                return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.ExtSlice(slices),
                                Sk.astnodes.Load, LINENO(n), n.col_offset);
            }
            /* extract Index values and put them in a Tuple */
            elts = [];
            for (j = 0; j < slices.length; ++j) {
                // @meredydd any idea how we reach this?
                slc = slices[j];
                Sk.asserts.assert(slc.kind == _slice_kind.Index_kind  && slc.v.Index.value);
                elts[j] = slc.v.Index.value;
            }
            e = new Sk.astnodes.Tuple(elts, Sk.astnodes.Load, LINENO(n), n.col_offset);

            return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.Index(e),
                             Sk.astnodes.Load, LINENO(n), n.col_offset);
        }
    }
}

function ast_for_flow_stmt(c, n)
{
    /*
      flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt
                 | yield_stmt
      break_stmt: 'break'
      continue_stmt: 'continue'
      return_stmt: 'return' [testlist]
      yield_stmt: yield_expr
      yield_expr: 'yield' testlist | 'yield' 'from' test
      raise_stmt: 'raise' [test [',' test [',' test]]]
    */
    var ch;

    REQ(n, SYM.flow_stmt);
    ch = CHILD(n, 0);
    switch (TYPE(ch)) {
        case SYM.break_stmt:
            return new Sk.astnodes.Break(LINENO(n), n.col_offset,
                         n.end_lineno, n.end_col_offset);
        case SYM.continue_stmt:
            return new Sk.astnodes.Continue(LINENO(n), n.col_offset,
                            n.end_lineno, n.end_col_offset);
        case SYM.yield_stmt: { /* will reduce to yield_expr */
            var exp = ast_for_expr(c, CHILD(ch, 0));
            if (!exp) {
                return null;
            }
            return new Sk.astnodes.Expr(exp, LINENO(n), n.col_offset,
                        n.end_lineno, n.end_col_offset);
        }
        case SYM.return_stmt:
            if (NCH(ch) == 1)
                return new Sk.astnodes.Return(null, LINENO(n), n.col_offset,
                              n.end_lineno, n.end_col_offset);
            else {
                var expression = ast_for_testlist(c, CHILD(ch, 1));
                if (!expression) {
                    return null;
                }
                return new Sk.astnodes.Return(expression, LINENO(n), n.col_offset,
                              n.end_lineno, n.end_col_offset);
            }
        case SYM.raise_stmt:
            // This is tricky and Skulpt-specific, because we need to handle
            // both Python 3-style and Python 2-style 'raise' statements
            if (NCH(ch) == 1)
                return new Sk.astnodes.Raise(null, null, null, null, LINENO(n), n.col_offset,
                             n.end_lineno, n.end_col_offset);
            else if (NCH(ch) >= 2) {
                var cause = null;
                var expression = ast_for_expr(c, CHILD(ch, 1));
                var inst = null, tback = null;

                // raise [expression] from [cause]
                if (NCH(ch) == 4 && CHILD(ch, 2).value == 'from') {
                    if (!Sk.__future__.python3) {
                        ast_error(c, CHILD(ch, 2), "raise ... from ... is not available in Python 2");
                    }
                    cause = ast_for_expr(c, CHILD(ch, 3));
                } else if (NCH(ch) >= 4 && CHILD(ch, 2).value == ',') {
                    if (Sk.__future__.python3) {
                        ast_error(c, n, "Old raise syntax is not available in Python 3")
                    }
                    // raise [exception_type], [instantiation value] [, [traceback]]
                    // NB traceback isn't implemented in Skulpt yet
                    inst = ast_for_expr(c, CHILD(ch, 3));

                    if (NCH(ch) == 6) {
                        tback = ast_for_expr(c, CHILD(ch, 5));
                    }
                }
                return new Sk.astnodes.Raise(expression, cause, inst, tback, LINENO(n), n.col_offset,
                             n.end_lineno, n.end_col_offset);
            }
            /* fall through */
        default:
            Sk.asserts.fail("unexpected flow_stmt: ", TYPE(ch));
            return null;
    }
}

function astForArg(c, n)
{
    var name;
    var annotation = null;
    var ch;

    Sk.asserts.assert(n.type === SYM.tfpdef || n.type === SYM.vfpdef);
    ch = CHILD(n, 0);
    forbiddenCheck(c, ch, ch.value, ch.lineno);
    name = strobj(ch.value);

    if (NCH(n) == 3 && CHILD(n, 1).type === TOK.T_COLON) {
        annotation = ast_for_expr(c, CHILD(n, 2));
    }

    return new Sk.astnodes.arg(name, annotation, n.lineno, n.col_offset);
}

/* returns -1 if failed to handle keyword only arguments
   returns new position to keep processing if successful
               (',' tfpdef ['=' test])*
                     ^^^
   start pointing here
 */
function handleKeywordonlyArgs(c, n, start, kwonlyargs, kwdefaults)
{
    var argname;
    var ch;
    var expression;
    var annotation;
    var arg;
    var i = start;
    var j = 0; /* index for kwdefaults and kwonlyargs */

    if (!kwonlyargs) {
        ast_error(c, CHILD(n, start), "named arguments must follow bare *");
    }
    Sk.asserts.assert(kwdefaults);
    while (i < NCH(n)) {
        ch = CHILD(n, i);
        switch (ch.type) {
            case SYM.vfpdef:
            case SYM.tfpdef:
                if (i + 1 < NCH(n) && CHILD(n, i + 1).type == TOK.T_EQUAL) {
                    kwdefaults[j] = ast_for_expr(c, CHILD(n, i + 2));
                    i += 2; /* '=' and test */
                }
                else { /* setting NULL if no default value exists */
                    kwdefaults[j] = null;
                }
                if (NCH(ch) == 3) {
                    /* ch is NAME ':' test */
                    annotation = ast_for_expr(c, CHILD(ch, 2));
                }
                else {
                    annotation = null;
                }
                ch = CHILD(ch, 0);
                forbiddenCheck(c, ch, ch.value, ch.lineno);
                argname = strobj(ch.value);
                kwonlyargs[j++] = new Sk.astnodes.arg(argname, annotation, ch.lineno, ch.col_offset);
                i += 2; /* the name and the comma */
                break;
            case TOK.T_DOUBLESTAR:
                return i;
            default:
                ast_error(c, ch, "unexpected node");
        }
    }
    return i;
}

function astForArguments (c, n) {
    var k;
    var j;
    var i;
    var foundDefault;
    var posargs = [];
    var posdefaults = [];
    var kwonlyargs = [];
    var kwdefaults = [];
    var vararg = null;
    var kwarg = null;
    var ch = null;

    /* This function handles both typedargslist (function definition)
       and varargslist (lambda definition).

       parameters: '(' [typedargslist] ')'
       typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
               '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
             | '**' tfpdef [',']]]
         | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
         | '**' tfpdef [','])
       tfpdef: NAME [':' test]
       varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
               '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
             | '**' vfpdef [',']]]
         | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
         | '**' vfpdef [',']
       )
       vfpdef: NAME

    */
    if (n.type === SYM.parameters) {
        if (NCH(n) === 2) // () as arglist
        {
            return new Sk.astnodes.arguments_([], null, [], [], null, []);
        }
        n = CHILD(n, 1);
    }
    Sk.asserts.assert(n.type === SYM.varargslist ||
                        n.type === SYM.typedargslist);


    // Skulpt note: the "counting numbers of args" section
    // from ast.c is omitted because JS arrays autoexpand

    /* tfpdef: NAME [':' test]
       vfpdef: NAME
    */
    i = 0;
    j = 0;  /* index for defaults */
    k = 0;  /* index for args */
    while (i < NCH(n)) {
        ch = CHILD(n, i);
        switch (ch.type) {
            case SYM.tfpdef:
            case SYM.vfpdef:
                /* XXX Need to worry about checking if TYPE(CHILD(n, i+1)) is
                   anything other than EQUAL or a comma? */
                /* XXX Should NCH(n) check be made a separate check? */
                if (i + 1 < NCH(n) && CHILD(n, i + 1).type == TOK.T_EQUAL) {
                    posdefaults[j++] = ast_for_expr(c, CHILD(n, i + 2));
                    i += 2;
                    foundDefault = 1;
                }
                else if (foundDefault) {
                    throw new Sk.builtin.SyntaxError("non-default argument follows default argument", c.c_filename, n.lineno);
                }
                posargs[k++] = astForArg(c, ch);
                i += 2; /* the name and the comma */
                break;
            case TOK.T_STAR:
                if (i+1 >= NCH(n) ||
                    (i+2 == NCH(n) && CHILD(n, i+1).type == TOK.T_COMMA)) {
                    throw new Sk.builtin.SyntaxError("named arguments must follow bare *", c.c_filename, n.lineno);
                }
                ch = CHILD(n, i+1);  /* tfpdef or COMMA */
                if (ch.type == TOK.T_COMMA) {
                    i += 2; /* now follows keyword only arguments */
                    i = handleKeywordonlyArgs(c, n, i,
                                                  kwonlyargs, kwdefaults);
                }
                else {
                    vararg = astForArg(c, ch);

                    i += 3;
                    if (i < NCH(n) && (CHILD(n, i).type == SYM.tfpdef
                                    || CHILD(n, i).type == SYM.vfpdef)) {
                        i = handleKeywordonlyArgs(c, n, i,
                                                      kwonlyargs, kwdefaults);
                    }
                }
                break;
            case TOK.T_DOUBLESTAR:
                ch = CHILD(n, i+1);  /* tfpdef */
                Sk.asserts.assert(ch.type == SYM.tfpdef || ch.type == SYM.vfpdef);
                kwarg = astForArg(c, ch);
                i += 3;
                break;
            default:
                Sk.asserts.fail("unexpected node in varargslist");
                return;
        }
    }
    return new Sk.astnodes.arguments_(posargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults);
}

function ast_for_async_funcdef(c, n, decorator_seq)
{
    /* async_funcdef: 'async' funcdef */
    REQ(n, SYM.async_funcdef);
    REQ(CHILD(n, 0), TOK.T_NAME);
    Sk.asserts.assert(STR(CHILD(n, 0) === "async"));
    REQ(CHILD(n, 1), SYM.funcdef);

    return ast_for_funcdef_impl(c, n, decorator_seq,
                                true /* is_async */);
}

function ast_for_funcdef(c, n, decorator_seq) {
    /* funcdef: 'def' NAME parameters ['->' test] ':' suite */
    return ast_for_funcdef_impl(c, n, decorator_seq,
        false /* is_async */);
}

function ast_for_funcdef_impl(c, n0, decorator_seq, is_async) {
    /* funcdef: 'def' NAME parameters ['->' test] ':' [TYPE_COMMENT] suite */
    var n = is_async ? CHILD(n0, 1) : n0;
    var name;
    var args;
    var body;
    var returns = NULL;
    var name_i = 1;
    var end_lineno, end_col_offset;
    var tc;
    var type_comment = NULL;

    if (is_async && c.c_feature_version < 5) {
        ast_error(c, n,
                  "Async functions are only supported in Python 3.5 and greater");
        return NULL;
    }

    REQ(n, SYM.funcdef);

    name = new_identifier(CHILD(n, name_i));

    if (forbiddenCheck(c, name, CHILD(n, name_i), 0)) {
        return NULL;
    }
    args = astForArguments(c, CHILD(n, name_i + 1));
    if (!args) {
        return NULL;
    }
    if (TYPE(CHILD(n, name_i+2)) == TOK.T_RARROW) {
        returns = ast_for_expr(c, CHILD(n, name_i + 3));
        if (!returns) {
            return NULL
        }
        name_i += 2;
    }

    if (TYPE(CHILD(n, name_i + 3)) == TOK.T_TYPE_COMMENT) {
        type_comment = TOK.T_NEW_TYPE_COMMENT(CHILD(n, name_i + 3));
        if (!type_comment)
            return NULL;
        name_i += 1;
    }

    body = astForSuite(c, CHILD(n, name_i + 3));
    if (!body) {
        return NULL;
    }
    // get_last_end_pos(body, &end_lineno, &end_col_offset);

    if (NCH(CHILD(n, name_i + 3)) > 1) {
        /* Check if the suite has a type comment in it. */
        tc = CHILD(CHILD(n, name_i + 3), 1);

        if (TYPE(tc) == TOK.T_TYPE_COMMENT) {
            if (type_comment != NULL) {
                ast_error(c, n, "Cannot have two type comments on def");
                return NULL;
            }
            type_comment = TOK.T_NEW_TYPE_COMMENT(tc);
            if (!type_comment)
                return NULL;
        }
    }

    if (is_async)
        return new Sk.astnodes.AsyncFunctionDef(name, args, body, decorator_seq, returns, type_comment,
                                LINENO(n0), n0.col_offset, end_lineno, end_col_offset);
    else
        return new Sk.astnodes.FunctionDef(name, args, body, decorator_seq, returns, type_comment,
                           LINENO(n), n.col_offset, end_lineno, end_col_offset);
}

function astForClassBases (c, n) {
    /* testlist: test (',' test)* [','] */
    Sk.asserts.assert(NCH(n) > 0);
    REQ(n, SYM.testlist);
    if (NCH(n) === 1) {
        return [ ast_for_expr(c, CHILD(n, 0)) ];
    }
    return seq_for_testlist(c, n);
}

function astForClassdef (c, n, decoratorSeq) {
    /* classdef: 'class' NAME ['(' arglist ')'] ':' suite */
    var classname;
    var call;
    var s;

    REQ(n, SYM.classdef);

    if (NCH(n) == 4) { /* class NAME ':' suite */
        s = astForSuite(c, CHILD(n, 3));
        classname = new_identifier(CHILD(n, 1).value);
        forbiddenCheck(c, CHILD(n,3), classname, n.lineno);

        return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq,
                                    /*TODO docstring*/null, LINENO(n), n.col_offset);
    }

    if (TYPE(CHILD(n, 3)) === TOK.T_RPAR) { /* class NAME '(' ')' ':' suite */
        s = astForSuite(c, CHILD(n, 5));
        classname = new_identifier(CHILD(n, 1).value);
        forbiddenCheck(c, CHILD(n, 3), classname, CHILD(n, 3).lineno);
        return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq,
                                    /*TODO docstring*/null, LINENO(n), n.col_offset);
    }

    /* class NAME '(' arglist ')' ':' suite */
    /* build up a fake Call node so we can extract its pieces */
    {
        var dummy_name;
        var dummy;
        dummy_name = new_identifier(CHILD(n, 1));
        dummy = new Sk.astnodes.Name(dummy_name, Sk.astnodes.Load, LINENO(n), n.col_offset);
        call = ast_for_call(c, CHILD(n, 3), dummy, false);
    }
    s = astForSuite(c, CHILD(n, 6));
    classname = new_identifier(CHILD(n, 1).value);
    forbiddenCheck(c, CHILD(n,1), classname, CHILD(n,1).lineno);

    return new Sk.astnodes.ClassDef(classname, call.args, call.keywords, s,
                               decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset);
}

function astForLambdef (c, n) {
    /* lambdef: 'lambda' [varargslist] ':' test */
    var args;
    var expression;
    if (NCH(n) === 3) {
        args = new Sk.astnodes.arguments_([], null, null, []);
        expression = ast_for_expr(c, CHILD(n, 2));
    }
    else {
        args = astForArguments(c, CHILD(n, 1));
        expression = ast_for_expr(c, CHILD(n, 3));
    }
    return new Sk.astnodes.Lambda(args, expression, n.lineno, n.col_offset);
}

function astForComprehension(c, n) {
    /* testlist_comp: test ( comp_for | (',' test)* [','] )
       argument: test [comp_for] | test '=' test       # Really [keyword '='] test */

    var j;
    var ifs;
    var nifs;
    var ge;
    var expression;
    var t;
    var forch;
    var i;
    var ch;
    var genexps;
    var nfors;
    var elt;
    var comps;
    var comp;

    function countCompFors(c, n) {
        var nfors = 0;
        count_comp_for: while (true) {
            nfors++;
            REQ(n, SYM.comp_for);
            if (NCH(n) === 5) {
                n = CHILD(n, 4);
            } else {
                return nfors;
            }
            count_comp_iter: while (true) {
                REQ(n, SYM.comp_iter);
                n = CHILD(n, 0);
                if (n.type === SYM.comp_for) {
                    continue count_comp_for;
                } else if (n.type === SYM.comp_if) {
                    if (NCH(n) === 3) {
                        n = CHILD(n, 2);
                        continue count_comp_iter;
                    } else {
                        return nfors;
                    }
                }
                break;
            }
            break;
        }
        Sk.asserts.fail("logic error in countCompFors");
    }

    function countCompIfs(c, n) {
        var nifs = 0;
        while (true) {
            REQ(n, SYM.comp_iter);
            if (CHILD(n, 0).type === SYM.comp_for) {
                return nifs;
            }
            n = CHILD(n, 0);
            REQ(n, SYM.comp_if);
            nifs++;
            if (NCH(n) === 2) {
                return nifs;
            }
            n = CHILD(n, 2);
        }
    }

    nfors = countCompFors(c, n);
    comps = [];
    for (i = 0; i < nfors; ++i) {
        REQ(n, SYM.comp_for);
        forch = CHILD(n, 1);
        t = ast_for_exprlist(c, forch, Sk.astnodes.Store);
        expression = ast_for_expr(c, CHILD(n, 3));
        if (NCH(forch) === 1) {
            comp = new Sk.astnodes.comprehension(t[0], expression, []);
        } else {
            comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, n.lineno, n.col_offset), expression, []);
        }
        if (NCH(n) === 5) {
            n = CHILD(n, 4);
            nifs = countCompIfs(c, n);
            ifs = [];
            for (j = 0; j < nifs; ++j) {
                REQ(n, SYM.comp_iter);
                n = CHILD(n, 0);
                REQ(n, SYM.comp_if);
                expression = ast_for_expr(c, CHILD(n, 1));
                ifs[j] = expression;
                if (NCH(n) === 3) {
                    n = CHILD(n, 2);
                }
            }
            if (n.type === SYM.comp_iter) {
                n = CHILD(n, 0);
            }
            comp.ifs = ifs;
        }
        comps[i] = comp;
    }
    return comps;
}

function astForIterComp(c, n, type) {
    var elt, comps;
    Sk.asserts.assert(NCH(n) > 1);
    elt = ast_for_expr(c, CHILD(n, 0));
    comps = astForComprehension(c, CHILD(n, 1));
    if (type === COMP_GENEXP) {
        return new Sk.astnodes.GeneratorExp(elt, comps, n.lineno, n.col_offset);
    } else if (type === COMP_SETCOMP) {
        return new Sk.astnodes.SetComp(elt, comps, n.lineno, n.col_offset);
    }
}

/*
   Count the number of 'for' loops in a comprehension.
   Helper for ast_for_comprehension().
*/
function count_comp_fors(c, n) {
    var n_fors = 0;
    var is_async;
    count_comp_for: while (true) {
        // @meredydd needs new grammar
        // REQ(n, SYM.comp_for);
        // if (NCH(n) === 2) {
        //     REQ(CHILD(n, 0), TOK.T_ASYNC);
        //     n = CHILD(n, 1);
        // } else if (NCH(n) === 1) {
        //     n = CHILD(n, 0);
        // } else {
        //     Sk.asserts.fail("logic error in count_comp_fors");
        // }
        // if (NCH(n) == (5)) {
        //     n = CHILD(n, 4);
        // } else {
        //     return n_fors;
        // }
        is_async = 0;
        n_fors++;
        REQ(n, SYM.comp_for);
        if (TYPE(CHILD(n, 0)) == TOK.T_ASYNC) {
            is_async = 1;
        }
        if (NCH(n) == (5 + is_async)) {
            n = CHILD(n, 4 + is_async);
        }
        else {
            return n_fors;
        }
        count_comp_iter: while (true) {
            REQ(n, SYM.comp_iter);
            n = CHILD(n, 0);
            if (TYPE(n) === SYM.comp_for) {
                continue count_comp_for;
            } else if (TYPE(n) === SYM.comp_if) {
                if (NCH(n) === 3) {
                    n = CHILD(n, 2);
                    continue count_comp_iter;
                } else {
                    return n_fors;
                }
            }
            break;
        }
        break;
    }
}

function count_comp_ifs(c, n)
{
    var n_ifs = 0;

    while (true) {
        REQ(n, SYM.comp_iter);
        if (TYPE(CHILD(n, 0)) == SYM.comp_for)
            return n_ifs;
        n = CHILD(n, 0);
        REQ(n, SYM.comp_if);
        n_ifs++;
        if (NCH(n) == 2) {
            return n_ifs;
        }
        n = CHILD(n, 2);
    }
}

function ast_for_comprehension(c, n) {
    var i, n_fors;
    var comps = [];
    n_fors = count_comp_fors(c, n);

    for (i = 0; i < n_fors; i++) {
        var comp;
        var t;
        var expression, first;
        var for_ch;
        var is_async = 0;

        if (TYPE(CHILD(n, 0)) == TOK.T_ASYNC) {
            is_async = 1;
        }

        for_ch = CHILD(n, 1 + is_async);
        t = ast_for_exprlist(c, for_ch, Sk.astnodes. Store);
        if (!t) {
            return null;
        }

        expression = ast_for_expr(c, CHILD(n, 3 + is_async));

        if (!expression) {
            return null;
        }

        // again new grammar needed
        // REQ(n, SYM.comp_for);

        // if (NCH(n) == 2) {
        //     is_async = 1;
        //     REQ(CHILD(n, 0), TOK.T_ASYNC);
        //     sync_n = CHILD(n, 1);
        // }
        // else {
        //     sync_n = CHILD(n, 0);
        // }
        // REQ(sync_n, SYM.sync_comp_for);

        // /* Async comprehensions only allowed in Python 3.6 and greater */
        // /* @meredydd see below for the joys of the future! */
        // if (is_async && c.c_feature_version < 6) {
        //     ast_error(c, n,
        //               "Async comprehensions are only supported in Python 3.6 and greater");
        //     return null;
        // }

        // for_ch = CHILD(sync_n, 1);
        // t = ast_for_exprlist(c, for_ch, Sk.astnodes.Store);

        // expression = ast_for_expr(c, CHILD(sync_n, 3));

        /* Check the # of children rather than the length of t, since
           (x for x, in ...) has 1 element in t, but still requires a Tuple. */
        first = t[0];
        if (NCH(for_ch) == 1)
            comp = new Sk.astnodes.comprehension(first, expression, null, is_async);
        else
            comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, first.lineno, first.col_offset,
                                       for_ch.end_lineno, for_ch.end_col_offset),
                                 expression, null, is_async);

        if (NCH(n) == (5 + is_async)) {
            var j, n_ifs;
            var ifs = [];

            n = CHILD(n, 4 + is_async);
            n_ifs = count_comp_ifs(c, n);
            if (n_ifs == -1) {
                return null;
            }

            for (j = 0; j < n_ifs; j++) {
                REQ(n, SYM.comp_iter);
                n = CHILD(n, 0);
                REQ(n, SYM.comp_if);

                expression = ast_for_expr(c, CHILD(n, 1));
                if (!expression) {
                    return null;
                }

                ifs[j] = expression;
                if (NCH(n) == 3) {
                    n = CHILD(n, 2);
                }
            }
            /* on exit, must guarantee that n is a comp_for */
            if (TYPE(n) == SYM.comp_iter) {
                n = CHILD(n, 0);
            }
            comp.ifs = ifs;
        }
        // if (NCH(sync_n) == 5) {
        //     var j, n_ifs;
        //     var ifs = [];

        //     n = CHILD(sync_n, 4);
        //     n_ifs = count_comp_ifs(c, n);

        //     for (j = 0; j < n_ifs; j++) {
        //         REQ(n, comp_iter);
        //         n = CHILD(n, 0);
        //         REQ(n, comp_if);

        //         expression = ast_for_expr(c, CHILD(n, 1));
        //         if (!expression) {
        //             return null;
        //         }

        //         ifs[j] = expression;
        //         if (NCH(n) == 3) {
        //             n = CHILD(n, 2);
        //         }
        //     }
        //     /* on exit, must guarantee that n is a comp_for */
        //     if (TYPE(n) == SYM.comp_iter) {
        //         n = CHILD(n, 0);
        //     }
        //     comp.ifs = ifs;
        // }
        comps[i] = comp;
    }
    return comps;
}

function ast_for_itercomp(c, n, type) {
    /* testlist_comp: (test|star_expr)
     *                ( comp_for | (',' (test|star_expr))* [','] ) */
    var elt;
    var comps;
    var ch;

    Sk.asserts.assert(NCH(n) > 1);

    ch = CHILD(n, 0);
    elt = ast_for_expr(c, ch);

    if (elt.constructor === Sk.astnodes.Starred) {
        ast_error(c, ch, "iterable unpacking cannot be used in comprehension");
        return NULL;
    }

    comps = ast_for_comprehension(c, CHILD(n, 1));

    if (type == COMP_GENEXP) {
        return new Sk.astnodes.GeneratorExp(elt, comps, LINENO(n), n.col_offset,
                            n.end_lineno, n.end_col_offset);
    } else if (type == COMP_LISTCOMP) {
        return new Sk.astnodes.ListComp(elt, comps, LINENO(n), n.col_offset,
                        n.end_lineno, n.end_col_offset);
    } else if (type == COMP_SETCOMP) {
        return new Sk.astnodes.SetComp(elt, comps, LINENO(n), n.col_offset,
                       n.end_lineno, n.end_col_offset);
    } else {
        /* Should never happen */
        return null;
    }
}

/* Fills in the key, value pair corresponding to the dict element.  In case
 * of an unpacking, key is NULL.  *i is advanced by the number of ast
 * elements.  Iff successful, nonzero is returned.
 */
function ast_for_dictelement(c, n, i)
{
    var expression;
    if (TYPE(CHILD(n, i)) == TOK.T_DOUBLESTAR) {
        Sk.asserts.assert(NCH(n) - i >= 2);

        expression = ast_for_expr(c, CHILD(n, i + 1));

        return { key: null, value: expression, i: i + 2 }
    } else {
        Sk.asserts.assert(NCH(n) - i >= 3);

        expression = ast_for_expr(c, CHILD(n, i));
        if (!expression)
            return 0;
        var key = expression;

        REQ(CHILD(n, i + 1), TOK.T_COLON);

        expression = ast_for_expr(c, CHILD(n, i + 2));
        if (!expression) {
            return false;
        }

        var value = expression;

        return { key: key, value: value, i: i + 3 };
    }
}

function ast_for_dictcomp(c, n) {
    var key, value;
    var comps = [];
    Sk.asserts.assert(NCH(n) > 3);
    REQ(CHILD(n, 1), TOK.T_COLON);
    key = ast_for_expr(c, CHILD(n, 0));
    value = ast_for_expr(c, CHILD(n, 2));
    comps = astForComprehension(c, CHILD(n, 3));
    return new Sk.astnodes.DictComp(key, value, comps, n.lineno, n.col_offset);
}

function ast_for_dictdisplay(c, n)
{
    var i;
    var j;
    var keys = [], values = [];

    j = 0;
    for (i = 0; i < NCH(n); i++) {
        var res = ast_for_dictelement(c, n, i);
        i = res.i
        keys[j] = res.key;
        values[j] = res.value;
        j++;
    }

    return new Sk.astnodes.Dict(keys, values, LINENO(n), n.col_offset,
                n.end_lineno, n.end_col_offset);
}

function ast_for_gen_expr(c, n) {
    Sk.asserts.assert(n.type === SYM.testlist_comp || n.type === SYM.argument);
    return astForIterComp(c, n, COMP_GENEXP);
}

function ast_for_setcomp(c, n) {
    Sk.asserts.assert(n.type === SYM.dictorsetmaker);
    return astForIterComp(c, n, COMP_SETCOMP);
}

function astForWhileStmt (c, n) {
    /* while_stmt: 'while' test ':' suite ['else' ':' suite] */
    REQ(n, SYM.while_stmt);
    if (NCH(n) === 4) {
        return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), [], n.lineno, n.col_offset);
    }
    else if (NCH(n) === 7) {
        return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), n.lineno, n.col_offset);
    }
    Sk.asserts.fail("wrong number of tokens for 'while' stmt");
}

function astForAugassign (c, n) {
    REQ(n, SYM.augassign);
    n = CHILD(n, 0);
    switch (n.value.charAt(0)) {
        case "+":
            return Sk.astnodes.Add;
        case "-":
            return Sk.astnodes.Sub;
        case "/":
            if (n.value.charAt(1) === "/") {
                return Sk.astnodes.FloorDiv;
            }
            return Sk.astnodes.Div;
        case "%":
            return Sk.astnodes.Mod;
        case "<":
            return Sk.astnodes.LShift;
        case ">":
            return Sk.astnodes.RShift;
        case "&":
            return Sk.astnodes.BitAnd;
        case "^":
            return Sk.astnodes.BitXor;
        case "|":
            return Sk.astnodes.BitOr;
        case "*":
            if (n.value.charAt(1) === "*") {
                return Sk.astnodes.Pow;
            }
            return Sk.astnodes.Mult;
        case "@":
            if (Sk.__future__.python3) {
                return Sk.astnodes.MatMult;
            }
        default:
            Sk.asserts.fail("invalid augassign");
    }
}

function astForBinop (c, n) {
    /* Must account for a sequence of expressions.
     How should A op B op C by represented?
     BinOp(BinOp(A, op, B), op, C).
     */
    var tmp;
    var newoperator;
    var nextOper;
    var i;
    var result = new Sk.astnodes.BinOp(
        ast_for_expr(c, CHILD(n, 0)),
        getOperator(CHILD(n, 1)),
        ast_for_expr(c, CHILD(n, 2)),
        n.lineno, n.col_offset);
    var nops = (NCH(n) - 1) / 2;
    for (i = 1; i < nops; ++i) {
        nextOper = CHILD(n, i * 2 + 1);
        newoperator = getOperator(nextOper);
        tmp = ast_for_expr(c, CHILD(n, i * 2 + 2));
        result = new Sk.astnodes.BinOp(result, newoperator, tmp, nextOper.lineno, nextOper.col_offset);
    }
    return result;
}

function ast_for_testlist (c, n) {
    /* testlist_comp: test (',' comp_for | (',' test)* [',']) */
    /* testlist: test (',' test)* [','] */
    Sk.asserts.assert(NCH(n) > 0);
    if (n.type === SYM.testlist_comp) {
        if (NCH(n) > 1) {
            Sk.asserts.assert(CHILD(n, 1).type !== SYM.comp_for);
        }
    }
    else {
        Sk.asserts.assert(n.type === SYM.testlist || n.type === SYM.testlist_star_expr);
    }

    if (NCH(n) === 1) {
        return ast_for_expr(c, CHILD(n, 0));
    }
    else {
        return new Sk.astnodes.Tuple(seq_for_testlist(c, n), Sk.astnodes.Load, n.lineno, n.col_offset/*, c.c_arena */);
    }
}

function ast_for_exprStmt (c, n) {
    var expression;
    var value;
    var e;
    var i;
    var targets;
    var expr2;
    var varName;
    var expr1;
    var ch;
    var ann;
    var simple;
    var deep;
    var expr3;
    REQ(n, SYM.expr_stmt);
    /* expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                            ('=' (yield_expr|testlist_star_expr))*)
       annassign: ':' test ['=' test]
       testlist_star_expr: (test|star_expr) (',' test|star_expr)* [',']
       augassign: '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^='
                | '<<=' | '>>=' | '**=' | '//='
       test: ... here starts the operator precedence dance
     */
    if (NCH(n) === 1) {
        return new Sk.astnodes.Expr(ast_for_testlist(c, CHILD(n, 0)), n.lineno, n.col_offset);
    }
    else if (CHILD(n, 1).type === SYM.augassign) {
        ch = CHILD(n, 0);
        expr1 = ast_for_testlist(c, ch);
        setContext(c, expr1, Sk.astnodes.Store, ch);
        switch (expr1.constructor) {
            case Sk.astnodes.Name:
                varName = expr1.id;
                forbiddenCheck(c, ch, varName, n.lineno);
                break;
            case Sk.astnodes.Attribute:
            case Sk.astnodes.Subscript:
                break;
            case Sk.astnodes.GeneratorExp:
                throw new Sk.builtin.SyntaxError("augmented assignment to generator expression not possible", c.c_filename, n.lineno);
            case Sk.astnodes.Yield:
                throw new Sk.builtin.SyntaxError("augmented assignment to yield expression not possible", c.c_filename, n.lineno);
            default:
                throw new Sk.builtin.SyntaxError("illegal expression for augmented assignment", c.c_filename, n.lineno);
        }

        ch = CHILD(n, 2);
        if (ch.type === SYM.testlist) {
            expr2 = ast_for_testlist(c, ch);
        }
        else {
            expr2 = ast_for_expr(c, ch);
        }

        return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset);
    }
    else if (CHILD(n, 1).type === SYM.annassign) {
        if (!Sk.__future__.python3) {
            throw new Sk.builtin.SyntaxError("Annotated assignment is not supported in Python 2", c.c_filename, n.lineno);
        }
        // annotated assignment
        ch = CHILD(n, 0);
        ann = CHILD(n, 1);
        simple = 1;
        deep = ch;
        while (NCH(deep) == 1) {
            deep = CHILD(deep, 0);
        }
        if (NCH(deep) > 0 && TYPE(CHILD(deep, 0)) == TOK.T_LPAR) {
            simple = 0;
        }
        expr1 = ast_for_testlist(c, ch);
        switch (expr1.constructor) {
            case Sk.astnodes.Name:
                varName = expr1.id;
                forbiddenCheck(c, ch, varName, n.lineno);
                setContext(c, expr1, Sk.astnodes.Store, ch);
                break;
            case Sk.astnodes.Attribute:
                varName = expr1.attr;
                forbiddenCheck(c, ch, varName, n.lineno);
                setContext(c, expr1, Sk.astnodes.Store, ch);
                break;
            case Sk.astnodes.Subscript:
                setContext(c, expr1, Sk.astnodes.Store, ch);
                break;
            case Sk.astnodes.List:
                throw new Sk.builtin.SyntaxError("only single target (not list) can be annotated", c.c_filename, n.lineno);
            case Sk.astnodes.Tuple:
                throw new Sk.builtin.SyntaxError("only single target (not tuple) can be annotated", c.c_filename, n.lineno);
            default:
                throw new Sk.builtin.SyntaxError("illegal target for annotation", c.c_filename, n.lineno);
        }
        
        if (expr1.constructor != Sk.astnodes.Name) {
            simple = 0;
        }
        
        ch = CHILD(ann, 1);
        expr2 = ast_for_expr(c, ch);
        if (NCH(ann) == 2) {
            return new Sk.astnodes.AnnAssign(expr1, expr2, null, simple, n.lineno, n.col_offset);
        } else {
            ch = CHILD(ann, 3);
            expr3 = ast_for_expr(c, ch);
            return new Sk.astnodes.AnnAssign(expr1, expr2, expr3, simple, n.lineno, n.col_offset);
        }
    }
    else {
        // normal assignment
        REQ(CHILD(n, 1), TOK.T_EQUAL);
        targets = [];
        for (i = 0; i < NCH(n) - 2; i += 2) {
            ch = CHILD(n, i);
            if (ch.type === SYM.yield_expr) {
                throw new Sk.builtin.SyntaxError("assignment to yield expression not possible", c.c_filename, n.lineno);
            }
            e = ast_for_testlist(c, ch);
            setContext(c, e, Sk.astnodes.Store, CHILD(n, i));
            targets[i / 2] = e;
        }
        value = CHILD(n, NCH(n) - 1);
        if (value.type === SYM.testlist_star_expr) {
            expression = ast_for_testlist(c, value);
        }
        else {
            expression = ast_for_expr(c, value);
        }
        return new Sk.astnodes.Assign(targets, expression, n.lineno, n.col_offset);
    }
}

function astForIfexpr (c, n) {
    /* test: or_test 'if' or_test 'else' test */
    Sk.asserts.assert(NCH(n) === 5);
    return new Sk.astnodes.IfExp(
        ast_for_expr(c, CHILD(n, 2)),
        ast_for_expr(c, CHILD(n, 0)),
        ast_for_expr(c, CHILD(n, 4)),
        n.lineno, n.col_offset);
}

/**
 * s is a python-style string literal, including quote characters and u/r/b
 * prefixes. Returns [decoded string object, is-an-fstring]
 */
function parsestr (c, n, s) {
    var quote = s.charAt(0);
    var rawmode = false;
    var unicode = false;
    var fmode = false;
    var bytesmode = false;

    var decodeEscape = function (s, quote) {
        var d3;
        var d2;
        var d1;
        var d0;
        var ch;
        var i;
        var len = s.length;
        var ret = "";
        for (i = 0; i < len; ++i) {
            ch = s.charAt(i);
            if (ch === "\\") {
                ++i;
                ch = s.charAt(i);
                if (ch === "n") {
                    ret += "\n";
                }
                else if (ch === "\\") {
                    ret += "\\";
                }
                else if (ch === "t") {
                    ret += "\t";
                }
                else if (ch === "r") {
                    ret += "\r";
                }
                else if (ch === "b") {
                    ret += "\b";
                }
                else if (ch === "f") {
                    ret += "\f";
                }
                else if (ch === "v") {
                    ret += "\v";
                }
                else if (ch === "0") {
                    ret += "\0";
                }
                else if (ch === '"') {
                    ret += '"';
                }
                else if (ch === '\'') {
                    ret += '\'';
                }
                else if (ch === "\n") /* escaped newline, join lines */ {
                }
                else if (ch === "x") {
                    if (i+2 >= len) {
                        ast_error(c, n, "Truncated \\xNN escape");
                    }
                    ret += String.fromCharCode(parseInt(s.substr(i+1,2), 16));
                    i += 2;
                }
                else if (!bytesmode && ch === "u") {
                    if (i+4 >= len) {
                        ast_error(c, n, "Truncated \\uXXXX escape");
                    }
                    ret += String.fromCharCode(parseInt(s.substr(i+1, 4), 16))
                    i += 4;
                }
                else if (!bytesmode && ch === "U") {
                    if (i+8 >= len) {
                        ast_error(c, n, "Truncated \\UXXXXXXXX escape");
                    }
                    ret += String.fromCodePoint(parseInt(s.substr(i+1, 8), 16))
                    i += 8;
                }
                else {
                    // Leave it alone
                    ret += "\\" + ch;
                    // Sk.asserts.fail("unhandled escape: '" + ch.charCodeAt(0) + "'");
                }
            }
            else if (bytesmode && ch.charCodeAt(0) > 0x7f) {
                ast_error(c, n, "bytes can only contain ASCII literal characters");
            } else {
                ret += ch;
            }
        }
        return ret;
    };

    //console.log("parsestr", s);

    // treats every sequence as unicodes even if they are not treated with uU prefix
    // kinda hacking though working for most purposes
    if((c.c_flags & Sk.Parser.CO_FUTURE_UNICODE_LITERALS || Sk.__future__.unicode_literals === true)) {
        unicode = true;
    }

    let seenflags = {};

    while(true) {
        if (quote === "u" || quote === "U") {
            unicode = true;
        }
        else if (quote === "r" || quote === "R") {
            rawmode = true;
        }
        else if (quote === "b" || quote === "B") {
            bytesmode = true;
        }
        else if (quote === "f" || quote === "F") {
            fmode = true;
        }
        else {
            break;
        }
        s = s.substr(1);
        quote = s.charAt(0);
    }

    Sk.asserts.assert(quote === "'" || quote === '"' && s.charAt(s.length - 1) === quote);
    s = s.substr(1, s.length - 2);

    if (s.length >= 4 && s.charAt(0) === quote && s.charAt(1) === quote) {
        Sk.asserts.assert(s.charAt(s.length - 1) === quote && s.charAt(s.length - 2) === quote);
        s = s.substr(2, s.length - 4);
    }

    if (rawmode || s.indexOf("\\") === -1) {
        if (bytesmode) {
            for (let i=0; i<s.length; i++) {
                if (s.charCodeAt(i) > 0x7f) {
                    ast_error(c, n, "bytes can only contain ASCII literal characters");
                }
            }
        }
        return [strobj(s), fmode, bytesmode];
    }
    return [strobj(decodeEscape(s, quote)), fmode, bytesmode];
}

function fstring_compile_expr(str, expr_start, expr_end, c, n) {
    Sk.asserts.assert(expr_end >= expr_start);
    Sk.asserts.assert(str.charAt(expr_start-1) == '{');
    Sk.asserts.assert(str.charAt(expr_end) == '}' || str.charAt(expr_end) == '!' || str.charAt(expr_end) == ':');

    let s = str.substring(expr_start, expr_end);

    /* If the substring is all whitespace, it's an error.  We need to catch this
       here, and not when we call PyParser_SimpleParseStringFlagsFilename,
       because turning the expression '' in to '()' would go from being invalid
       to valid. */
    if (/^\s*$/.test(s)) {
        ast_error(c, n, "f-string: empty expression not allowed");
    }
    s = "(" + s + ")";

    let ast;
    try {
        let parsed = Sk.parse("<fstring>", s);
        ast = Sk.astFromParse(parsed.cst, "<fstring>", parsed.flags);
    } catch(e) {
        if (e.traceback && e.traceback[0]) {
            let tb = e.traceback[0];
            tb.lineno = (tb.lineno || 1) - 1 + LINENO(n);
            tb.filename = c.c_filename;
        }
        throw e;
    }

    // TODO fstring_fix_node_location

    Sk.asserts.assert(ast.body.length == 1 && ast.body[0].constructor === Sk.astnodes.Expr);

    return ast.body[0].value;
}

function fstring_find_expr(str, start, end, raw, recurse_lvl, c, n) {
    let i = start;
    Sk.asserts.assert(str.charAt(i) == "{");
    i++;
    let expr_start = i;
    /* null if we're not in a string, else the quote char we're trying to
       match (single or double quote). */
    let quote_char = null;
    /* If we're inside a string, 1=normal, 3=triple-quoted. */
    let string_type = 0;
    /* Keep track of nesting level for braces/parens/brackets in
       expressions. */
    let nested_depth = 0;

    let format_spec, conversion;

    let unexpected_end_of_string = () => ast_error(c, n, "f-string: expecting '}'");

    Sk.asserts.assert(i <= end);

    for (; i < end; i++) {
        let ch = str.charAt(i);

        /* Nowhere inside an expression is a backslash allowed. */
        if (ch == '\\') {
            /* Error: can't include a backslash character, inside
               parens or strings or not. */
            ast_error(c, n, "f-string expression part cannot include a backslash");
        }
        if (quote_char) {
            /* We're inside a string. See if we're at the end. */
            /* This code needs to implement the same non-error logic
               as tok_get from tokenizer.c, at the letter_quote
               label. To actually share that code would be a
               nightmare. But, it's unlikely to change and is small,
               so duplicate it here. Note we don't need to catch all
               of the errors, since they'll be caught when parsing the
               expression. We just need to match the non-error
               cases. Thus we can ignore \n in single-quoted strings,
               for example. Or non-terminated strings. */
            if (ch == quote_char) {
                /* Does this match the string_type (single or triple
                   quoted)? */
                if (string_type == 3) {
                    if (i+2 < end && str.charAt(i+1) == ch && str.charAt(i+2) == ch) {
                        /* We're at the end of a triple quoted string. */
                        i += 2;
                        string_type = 0;
                        quote_char = 0;
                        continue;
                    }
                } else {
                    /* We're at the end of a normal string. */
                    quote_char = 0;
                    string_type = 0;
                    continue;
                }
            }
        } else if (ch == '\'' || ch == '"') {
            /* Is this a triple quoted string? */
            if (i+2 < end && str.charAt(i+1) == ch && str.charAt(i+2) == ch) {
                string_type = 3;
                i += 2;
            } else {
                /* Start of a normal string. */
                string_type = 1;
            }
            /* Start looking for the end of the string. */
            quote_char = ch;
        } else if (ch == '[' || ch == '{' || ch == '(') {
            nested_depth++;
        } else if (nested_depth != 0 &&
                   (ch == ']' || ch == '}' || ch == ')')) {
            nested_depth--;
        } else if (ch == '#') {
            /* Error: can't include a comment character, inside parens
               or not. */
            ast_error(c, n, "f-string expression part cannot include '#'");
        } else if (nested_depth == 0 &&
                   (ch == '!' || ch == ':' || ch == '}')) {
            /* First, test for the special case of "!=". Since '=' is
               not an allowed conversion character, nothing is lost in
               this test. */
            if (ch == '!' && i+1 < end && str.charAt(i+1) == '=') {
                /* This isn't a conversion character, just continue. */
                continue;
            }
            /* Normal way out of this loop. */
            break;
        } else {
            /* Just consume this char and loop around. */
        }
    }

    /* If we leave this loop in a string or with mismatched parens, we
       don't care. We'll get a syntax error when compiling the
       expression. But, we can produce a better error message, so
       let's just do that.*/
    if (quote_char) {
        ast_error(c, n, "f-string: unterminated string");
    }
    if (nested_depth) {
        ast_error(c, n, "f-string: mismatched '(', '{', or '['");
    }

    let expr_end = i;

    /* Compile the expression as soon as possible, so we show errors
       related to the expression before errors related to the
       conversion or format_spec. */
    let simple_expression = fstring_compile_expr(str, expr_start, expr_end, c, n);
 
    /* Check for a conversion char, if present. */
    if (str.charAt(i) == '!') {
        i++;
        if (i >= end)
            unexpected_end_of_string();

        conversion = str.charAt(i);
        i++;

        /* Validate the conversion. */
        if (!(conversion == 's' || conversion == 'r'
              || conversion == 'a')) {
            ast_error(c, n, "f-string: invalid conversion character: expected 's', 'r', or 'a'");
        }
    }

    /* Check for the format spec, if present. */
    if (i >= end)
        unexpected_end_of_string();
    if (str.charAt(i) == ':') {
        i++
        if (i >= end)
            unexpected_end_of_string();

        /* Parse the format spec. */
        [format_spec, i] = fstring_parse(str, i, end, raw, recurse_lvl+1, c, n);
    }

    if (i >= end || str.charAt(i) != '}')
        unexpected_end_of_string();

    /* We're at a right brace. Consume it. */
    i++;

    /* And now create the FormattedValue node that represents this
       entire expression with the conversion and format spec. */
    let expr = new Sk.astnodes.FormattedValue(simple_expression, conversion,
                                              format_spec, LINENO(n), n.col_offset);

    return [expr, i];
}

function fstring_parse(str, start, end, raw, recurse_lvl, c, n) {
    let values = [];
    let idx = start;

    let addLiteral = (literal) => {
        if (literal.indexOf("}") !== -1) {
            // We need to error out on any lone }s, and
            // replace doubles with singles.
            if (/(^|[^}])}(}})*($|[^}])/.test(literal)) {
                throw new SyntaxError("f-string: single '}' is not allowed", LINENO(n), n.col_offset);
            }
            literal = literal.replace(/}}/g, "}");
        }
        values.push(new Sk.astnodes.Str(new Sk.builtin.str(literal), LINENO(n), n.col_offset, c.end_lineno, n.end_col_offset));
    };

    
    while (idx < end) {
        let bidx = str.indexOf("{", idx);
        if (recurse_lvl !== 0) {
            // If there's a closing brace before the next open brace,
            // that's our end-of-expression
            let cbidx = str.indexOf("}", idx);
            if (cbidx !== -1) {
                if (bidx === -1) {
                    end = cbidx;
                } else if (bidx > cbidx) {
                    bidx = -1;
                    end = cbidx;
                }
            }
        }
        if (bidx === -1) {
            addLiteral(str.substring(idx, end));
            idx = end;
            break;
        }
        else if (bidx+1 < end && str.charAt(bidx+1) === "{") {
            // Swallow the double {{
            addLiteral(str.substring(idx, bidx+1));
            idx = bidx + 2;
            continue;
        }
        else {
            addLiteral(str.substring(idx, bidx));
            idx = bidx;

            // And now parse the f-string expression itself
            let [expr, endIdx] = fstring_find_expr(str, bidx, end, raw, recurse_lvl, c, n);
            values.push(expr);
            idx = endIdx;
        }
    }
    return [new Sk.astnodes.JoinedStr(values, LINENO(n), n.col_offset), idx];
}

function parsestrplus (c, n) {
    let strs = [];
    let lastStrNode;
    let bytesmode;

    for (let i = 0; i < NCH(n); ++i) {
        let chstr = CHILD(n, i).value;
        let r = parsestr(c, CHILD(n,i), chstr);
        let str = r[0];
        let fmode = r[1];
        let this_bytesmode = r[2];


        /* Check that we're not mixing bytes with unicode. */
        if (i != 0 && bytesmode !== this_bytesmode) {
            ast_error(c, n, "cannot mix bytes and nonbytes literals");
        }
        bytesmode = this_bytesmode;

        if (fmode) {
            if (!Sk.__future__.python3) {
                throw new Sk.builtin.SyntaxError("invalid string (f-strings are not supported in Python 2)", c.c_filename, CHILD(n, i).lineno);
            }

            let jss = str.$jsstr();
            let [astnode, _] = fstring_parse(jss, 0, jss.length, false, 0, c, CHILD(n, i));
            strs.push.apply(strs, astnode.values);
            lastStrNode = null;
        } else {
            if (lastStrNode) {
                lastStrNode.s = lastStrNode.s.sq$concat(str);
            } else {
                let type = bytesmode ? Sk.astnodes.Bytes : Sk.astnodes.Str;
                lastStrNode = new type(str, LINENO(n), n.col_offset, c.end_lineno, n.end_col_offset)
                strs.push(lastStrNode);
            }
        }
    }

    if (strs.length === 1 && strs[0].constructor === Sk.astnodes.Str) {
        return strs[0];
    } else {
        return new Sk.astnodes.JoinedStr(strs, LINENO(n), n.col_offset, c.end_lineno, n.end_col_offset);
    }
}

const invalidSyntax = /_[eE]|[eE]_|\._|j_/;
const invalidDecimalLiteral = /_\.|[+-]_|^0_\D|_j/;
const validUnderscores = /_(?=[^_])/g;
function parsenumber (c, s, lineno) {
    var neg;
    var val;
    var tmp;
    var end = s.charAt(s.length - 1);
    
    if (s.indexOf("_") !== -1) {
        if (invalidSyntax.test(s)) {
            throw new Sk.builtin.SyntaxError("invalid syntax", c.c_filename, lineno);
        }
    
        if (invalidDecimalLiteral.test(s)) {
            throw new Sk.builtin.SyntaxError("invalid decimal literal", c.c_filename, lineno);
        }
        
        s = s.replace(validUnderscores, "");
    }
    
    // call internal complex type constructor for complex strings
    if (end === "j" || end === "J") {
        return Sk.builtin.complex.complex_subtype_from_string(s);
    }

    // Handle longs
    if (end === "l" || end === "L") {
        return Sk.longFromStr(s.substr(0, s.length - 1), 0);
    }

    // todo; we don't currently distinguish between int and float so
    // str is wrong for these.
    if (s.indexOf(".") !== -1) {
        return new Sk.builtin.float_(parseFloat(s));
    }

    // Handle integers of various bases
    tmp = s;
    neg = false;
    if (s.charAt(0) === "-") {
        tmp = s.substr(1);
        neg = true;
    }

    if (tmp.charAt(0) === "0" && (tmp.charAt(1) === "x" || tmp.charAt(1) === "X")) {
        // Hex
        tmp = tmp.substring(2);
        val = parseInt(tmp, 16);
    } else if ((s.indexOf("e") !== -1) || (s.indexOf("E") !== -1)) {
        // Float with exponent (needed to make sure e/E wasn't hex first)
        return new Sk.builtin.float_(parseFloat(s));
    } else if (tmp.charAt(0) === "0" && (tmp.charAt(1) === "b" || tmp.charAt(1) === "B")) {
        // Binary
        tmp = tmp.substring(2);
        val = parseInt(tmp, 2);
    } else if (tmp.charAt(0) === "0") {
        if (tmp === "0") {
            // Zero
            val = 0;
        } else {
            // Octal
            tmp = tmp.substring(1);
            if ((tmp.charAt(0) === "o") || (tmp.charAt(0) === "O")) {
                tmp = tmp.substring(1);
            }
            val = parseInt(tmp, 8);
        }
    }
    else {
        // Decimal
        val = parseInt(tmp, 10);
    }

    // Convert to long
    if (val > Number.MAX_SAFE_INTEGER &&
        Math.floor(val) === val &&
        (s.indexOf("e") === -1 && s.indexOf("E") === -1)) {
        return Sk.longFromStr(s, 0);
    }

    // Small enough, return parsed number
    if (neg) {
        return new Sk.builtin.int_(-val);
    } else {
        return new Sk.builtin.int_(val);
    }
}

function astForSlice (c, n) {
    var n2;
    var step;
    var upper;
    var lower;
    var ch;
    REQ(n, SYM.subscript);

    /*
     subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
     sliceop: ':' [test]
     */
    ch = CHILD(n, 0);
    lower = null;
    upper = null;
    step = null;
    if (ch.type === TOK.T_DOT) {
        return new Sk.astnodes.Ellipsis();
    }
    if (NCH(n) === 1 && ch.type === SYM.test) {
        return new Sk.astnodes.Index(ast_for_expr(c, ch));
    }
    if (ch.type === SYM.test) {
        lower = ast_for_expr(c, ch);
    }
    if (ch.type === TOK.T_COLON) {
        if (NCH(n) > 1) {
            n2 = CHILD(n, 1);
            if (n2.type === SYM.test) {
                upper = ast_for_expr(c, n2);
            }
        }
    }
    else if (NCH(n) > 2) {
        n2 = CHILD(n, 2);
        if (n2.type === SYM.test) {
            upper = ast_for_expr(c, n2);
        }
    }

    ch = CHILD(n, NCH(n) - 1);
    if (ch.type === SYM.sliceop) {
        if (NCH(ch) === 1) {
            ch = CHILD(ch, 0);
            step = new Sk.astnodes.NameConstant(Sk.builtin.none.none$, Sk.astnodes.Load, ch.lineno, ch.col_offset);
        }
        else {
            ch = CHILD(ch, 1);
            if (ch.type === SYM.test) {
                step = ast_for_expr(c, ch);
            }
        }
    }
    return new Sk.astnodes.Slice(lower, upper, step);
}

function ast_for_atom(c, n)
{
    /* atom: '(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']'
       | '{' [dictmaker|testlist_comp] '}' | NAME | NUMBER | STRING+
       | '...' | 'None' | 'True' | 'False'
    */
    var ch = CHILD(n, 0);

    switch (TYPE(ch)) {
        case TOK.T_NAME: {
            var name;
            var s = STR(ch);
            if (s.length >= 4 && s.length <= 5) {
                if (s === "None") {
                    return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset);
                }

                if (s === "True") {
                    return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset);
                }

                if (s === "False") {
                    return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset);
                }
            }
            name = new_identifier(s, c);
            /* All names start in Load context, but may later be changed. */
            return new Sk.astnodes.Name(name, Sk.astnodes.Load, LINENO(n), n.col_offset,
                        n.end_lineno, n.end_col_offset);
        }
        case TOK.T_STRING:
            return parsestrplus(c, n);
        case TOK.T_NUMBER:
            return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset);
        case TOK.T_ELLIPSIS: /* Ellipsis */
            return new Sk.astnodes.Ellipsis(LINENO(n), n.col_offset,
                            n.end_lineno, n.end_col_offset);
        case TOK.T_LPAR: /* some parenthesized expressions */
            ch = CHILD(n, 1);

            if (TYPE(ch) == TOK.T_RPAR)
                return new Sk.astnodes.Tuple([], Sk.astnodes.Load, LINENO(n), n.col_offset,
                            n.end_lineno, n.end_col_offset);

            if (TYPE(ch) == SYM.yield_expr) {
                return ast_for_expr(c, ch);
            }

            /* testlist_comp: test ( comp_for | (',' test)* [','] ) */
            if (NCH(ch) == 1) {
                return ast_for_testlist(c, ch);
            }

            if (TYPE(CHILD(ch, 1)) == SYM.comp_for) {
                return copy_location(ast_for_genexp(c, ch), n);
            }
            else {
                return copy_location(ast_for_testlist(c, ch), n);
            }
        case TOK.T_LSQB: /* list (or list comprehension) */
            ch = CHILD(n, 1);

            if (TYPE(ch) == TOK.T_RSQB)
                return new Sk.astnodes.List([], Sk.astnodes.Load, LINENO(n), n.col_offset,
                            n.end_lineno, n.end_col_offset);

            REQ(ch, SYM.testlist_comp);
            if (NCH(ch) == 1 || TYPE(CHILD(ch, 1)) == TOK.T_COMMA) {
                var elts = seq_for_testlist(c, ch);
                if (!elts) {
                    return null;
                }
                return new Sk.astnodes.List(elts, Sk.astnodes.Load, LINENO(n), n.col_offset,
                            n.end_lineno, n.end_col_offset);
            }
            else {
                return copy_location(ast_for_listcomp(c, ch), n);
            }
        case TOK.T_LBRACE: {
            /* dictorsetmaker: ( ((test ':' test | '**' test)
            *                    (comp_for | (',' (test ':' test | '**' test))* [','])) |
            *                   ((test | '*' test)
            *                    (comp_for | (',' (test | '*' test))* [','])) ) */
            var res;
            ch = CHILD(n, 1);
            if (TYPE(ch) == TOK.T_RBRACE) {
                /* It's an empty dict. */
                return new Sk.astnodes.Dict(null, null, LINENO(n), n.col_offset,
                    n.end_lineno, n.end_col_offset);
            }
            else {
                var is_dict = (TYPE(CHILD(ch, 0)) == TOK.T_DOUBLESTAR);
                if (NCH(ch) == 1 ||
                        (NCH(ch) > 1 &&
                        TYPE(CHILD(ch, 1)) == TOK.T_COMMA)) {
                    /* It's a set display. */
                    res = ast_for_setdisplay(c, ch);
                }
                else if (NCH(ch) > 1 &&
                        TYPE(CHILD(ch, 1)) == SYM.comp_for) {
                    /* It's a set comprehension. */
                    res = ast_for_setcomp(c, ch);
                }
                else if (NCH(ch) > 3 - is_dict &&
                        TYPE(CHILD(ch, 3 - is_dict)) == SYM.comp_for) {
                    /* It's a dictionary comprehension. */
                    if (is_dict) {
                        ast_error(c, n,
                                "dict unpacking cannot be used in dict comprehension");
                        return null;
                    }
                    res = ast_for_dictcomp(c, ch);
                }
                else {
                    /* It's a dictionary display. */
                    res = ast_for_dictdisplay(c, ch);
                }
                return copy_location(res, n);
            }
        }
        default:
            Sk.asserts.fail("unhandled atom " + TYPE(ch));
            return null;
    }
}

function ast_for_setdisplay(c, n) {
    var i;
    var elts = [];

    Sk.asserts.assert(TYPE(n) === SYM.dictorsetmaker);

    for (i = 0; i < NCH(n); i += 2) {
        var expression;
        expression = ast_for_expr(c, CHILD(n, i));
        elts[i / 2] = expression;
    }

    return new Sk.astnodes.Set(elts, LINENO(n), n.col_offset);
}

function astForAtomExpr(c, n) {
    var i, nch, start = 0;
    var e, tmp;

    REQ(n, SYM.atom_expr);
    nch = NCH(n);

    if (CHILD(n, 0).type === TOK.T_AWAIT) {
        start = 1;
        Sk.asserts.assert(nch > 1);
    }

    e = ast_for_atom(c, CHILD(n, start));
    if (!e) {
        return null;
    }

    if (nch === 1) {
        return e;
    }

    if (start && nch === 2) {
        return new Sk.astnodes.Await(e, n.lineno, n.col_offset /*, c->c_arena*/);
    }

    for (i = start + 1; i < nch; i++) {
        var ch = CHILD(n, i);
        if (ch.type !== SYM.trailer) {
            break;
        }
        tmp = ast_for_trailer(c, ch, e);
        if (!tmp) {
            return null;
        }

        tmp.lineno = e.lineno;
        tmp.col_offset = e.col_offset;
        e = tmp;
    }

    if (start) {
        /* there was an AWAIT */
        return new Sk.astnodes.Await(e, n.line, n.col_offset /*, c->c_arena*/);
    }
    else {
        return e;
    }
}

function astForPower (c, n) {
    /* power: atom trailer* ('**' factor)*
     */
    var f;
    var tmp;
    var ch;
    var i;
    var e;
    REQ(n, SYM.power);
    e = astForAtomExpr(c, CHILD(n, 0));
    if (NCH(n) === 1) {
        return e;
    }
    if (CHILD(n, NCH(n) - 1).type === SYM.factor) {
        f = ast_for_expr(c, CHILD(n, NCH(n) - 1));
        e = new Sk.astnodes.BinOp(e, Sk.astnodes.Pow, f, n.lineno, n.col_offset);
    }
    return e;
}

function astForStarred(c, n) {
    REQ(n, SYM.star_expr);

    /* The Load context is changed later */
    return new Sk.astnodes.Starred(ast_for_expr(c, CHILD(n ,1)), Sk.astnodes.Load, n.lineno, n.col_offset /*, c.c_arena */)
}

function ast_for_expr (c, n) {
    /*
     handle the full range of simple expressions
     test: or_test ['if' or_test 'else' test] | lambdef
     test_nocond: or_test | lambdef_nocond
     or_test: and_test ('or' and_test)*
     and_test: not_test ('and' not_test)*
     not_test: 'not' not_test | comparison
     comparison: expr (comp_op expr)*
     expr: xor_expr ('|' xor_expr)*
     xor_expr: and_expr ('^' and_expr)*
     and_expr: shift_expr ('&' shift_expr)*
     shift_expr: arith_expr (('<<'|'>>') arith_expr)*
     arith_expr: term (('+'|'-') term)*
     term: factor (('*'|'/'|'%'|'//') factor)*
     factor: ('+'|'-'|'~') factor | power
     power: atom_expr ['**' factor]
     atom_expr: [AWAIT] atom trailer*
     yield_expr: 'yield' [yield_arg]
    */

    var exp;
    var cmps;
    var ops;
    var i;
    var seq;
    LOOP: while (true) {
        switch (n.type) {
            case SYM.test:
            case SYM.test_nocond:
                if (CHILD(n, 0).type === SYM.lambdef || CHILD(n, 0).type === SYM.lambdef_nocond) {
                    return astForLambdef(c, CHILD(n, 0));
                }
                else if (NCH(n) > 1) {
                    return astForIfexpr(c, n);
                }
                // fallthrough
            case SYM.or_test:
            case SYM.and_test:
                if (NCH(n) === 1) {
                    n = CHILD(n, 0);
                    continue LOOP;
                }
                seq = [];
                for (i = 0; i < NCH(n); i += 2) {
                    seq[i / 2] = ast_for_expr(c, CHILD(n, i));
                }
                if (CHILD(n, 1).value === "and") {
                    return new Sk.astnodes.BoolOp(Sk.astnodes.And, seq, n.lineno, n.col_offset /*, c.c_arena*/);
                }
                Sk.asserts.assert(CHILD(n, 1).value === "or");
                return new Sk.astnodes.BoolOp(Sk.astnodes.Or, seq, n.lineno, n.col_offset);
            case SYM.not_test:
                if (NCH(n) === 1) {
                    n = CHILD(n, 0);
                    continue LOOP;
                }
                else {
                    return new Sk.astnodes.UnaryOp(Sk.astnodes.Not, ast_for_expr(c, CHILD(n, 1)), n.lineno, n.col_offset);
                }
                break;
            case SYM.comparison:
                if (NCH(n) === 1) {
                    n = CHILD(n, 0);
                    continue LOOP;
                }
                else {
                    ops = [];
                    cmps = [];
                    for (i = 1; i < NCH(n); i += 2) {
                        ops[(i - 1) / 2] = astForCompOp(c, CHILD(n, i));
                        cmps[(i - 1) / 2] = ast_for_expr(c, CHILD(n, i + 1));
                    }
                    return new Sk.astnodes.Compare(ast_for_expr(c, CHILD(n, 0)), ops, cmps, n.lineno, n.col_offset);
                }
                break;
            case SYM.star_expr:
                return astForStarred(c, n);
            /* The next fize cases all handle BinOps  The main body of code
               is the same in each case, but the switch turned inside out to
               reuse the code for each type of operator
             */
            case SYM.expr:
            case SYM.xor_expr:
            case SYM.and_expr:
            case SYM.shift_expr:
            case SYM.arith_expr:
            case SYM.term:
                if (NCH(n) === 1) {
                    n = CHILD(n, 0);
                    continue LOOP;
                }
                return astForBinop(c, n);
            case SYM.yield_expr:
                var an;
                var en
                var is_from = false;
                exp = null;
                if (NCH(n) > 1) {
                    an = CHILD(n, 1); /* yield_arg */
                }

                if (an) {
                    en = CHILD(an, NCH(an) - 1);
                    if (NCH(an) == 2) {
                        is_from = true;
                        exp = ast_for_expr(c, en);
                    } else {
                        exp = ast_for_testlist(c, en);
                    }
                }

                if (is_from) {
                    return new Sk.astnodes.YieldFrom(exp, n.lineno, n.col_offset);
                }

                return new Sk.astnodes.Yield(exp, n.lineno, n.col_offset);
            case SYM.factor:
                if (NCH(n) === 1) {
                    n = CHILD(n, 0);
                    continue LOOP;
                }
                return astForFactor(c, n);
            case SYM.power:
                return astForPower(c, n);
            default:
                Sk.asserts.fail("unhandled expr", "n.type: %d", n.type);
        }
        break;
    }
}

function astForNonLocalStmt(c, n) {
    ast_error(c, n, "Not implemented: nonlocal");
}

function astForAsyncStmt(c, n) {
    ast_error(c, n, "Not implemented: async");
}

// This is only used for Python 2 support.
function astForPrintStmt (c, n) {

    if (Sk.__future__.print_function) {
        ast_error(c, n, "Missing parentheses in call to 'print'");
    }

    /* print_stmt: 'print' ( [ test (',' test)* [','] ]
     | '>>' test [ (',' test)+ [','] ] )
     */
    var nl;
    var i, j;
    var seq;
    var start = 1;
    var dest = null;
    REQ(n, SYM.print_stmt);
    if (NCH(n) >= 2 && CHILD(n, 1).type === TOK.T_RIGHTSHIFT) {
        dest = ast_for_expr(c, CHILD(n, 2));
        start = 4;
    }
    seq = [];
    for (i = start, j = 0; i < NCH(n); i += 2, ++j) {
        seq[j] = ast_for_expr(c, CHILD(n, i));
    }
    nl = (CHILD(n, NCH(n) - 1)).type === TOK.T_COMMA ? false : true;
    return new Sk.astnodes.Print(dest, seq, nl, n.lineno, n.col_offset);
}

function astForStmt (c, n) {
    var ch;
    if (n.type === SYM.stmt) {
        Sk.asserts.assert(NCH(n) === 1);
        n = CHILD(n, 0);
    }
    if (n.type === SYM.simple_stmt) {
        Sk.asserts.assert(numStmts(n) === 1);
        n = CHILD(n, 0);
    }
    if (n.type === SYM.small_stmt) {
        n = CHILD(n, 0);
        /* small_stmt: expr_stmt | del_stmt | pass_stmt | flow_stmt
                   | import_stmt | global_stmt | nonlocal_stmt | assert_stmt
                   | debugger_stmt (skulpt special)
        */
        switch (n.type) {
            case SYM.expr_stmt:
                return ast_for_exprStmt(c, n);
            case SYM.del_stmt:
                return astForDelStmt(c, n);
            case SYM.pass_stmt:
                return new Sk.astnodes.Pass(n.lineno, n.col_offset);
            case SYM.flow_stmt:
                return ast_for_flow_stmt(c, n);
            case SYM.import_stmt:
                return astForImportStmt(c, n);
            case SYM.global_stmt:
                return astForGlobalStmt(c, n);
            case SYM.nonlocal_stmt:
                return astForNonLocalStmt(c, n);
            case SYM.assert_stmt:
                return astForAssertStmt(c, n);
            case SYM.print_stmt:
                return astForPrintStmt(c, n);
            case SYM.debugger_stmt:
                return new Sk.astnodes.Debugger(n.lineno, n.col_offset);
            default:
                Sk.asserts.fail("unhandled small_stmt");
        }
    }
    else {
        /* compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt
                        | funcdef | classdef | decorated | async_stmt
        */
        ch = CHILD(n, 0);
        REQ(n, SYM.compound_stmt);
        switch (ch.type) {
            case SYM.if_stmt:
                return astForIfStmt(c, ch);
            case SYM.while_stmt:
                return astForWhileStmt(c, ch);
            case SYM.for_stmt:
                return astForForStmt(c, ch);
            case SYM.try_stmt:
                return astForTryStmt(c, ch);
            case SYM.with_stmt:
                return ast_for_with_stmt(c, ch);
            case SYM.funcdef:
                return ast_for_funcdef(c, ch, []);
            case SYM.classdef:
                return astForClassdef(c, ch, []);
            case SYM.decorated:
                return ast_for_decorated(c, ch);
            case SYM.async_stmt:
                return astForAsyncStmt(c, ch);
            default:
                Sk.asserts.assert("unhandled compound_stmt");
        }
    }
};

Sk.astFromParse = function (n, filename, c_flags) {
    var j;
    var num;
    var ch;
    var i;
    var c = new Compiling("utf-8", filename, c_flags);
    var stmts = [];
    var k = 0;
    switch (n.type) {
        case SYM.file_input:
            for (i = 0; i < NCH(n) - 1; ++i) {
                ch = CHILD(n, i);
                if (n.type === TOK.T_NEWLINE) {
                    continue;
                }
                REQ(ch, SYM.stmt);
                num = numStmts(ch);
                if (num === 1) {
                    stmts[k++] = astForStmt(c, ch);
                }
                else {
                    ch = CHILD(ch, 0);
                    REQ(ch, SYM.simple_stmt);
                    for (j = 0; j < num; ++j) {
                        stmts[k++] = astForStmt(c, CHILD(ch, j * 2));
                    }
                }
            }
            return new Sk.astnodes.Module(stmts);
        case SYM.eval_input:
            Sk.asserts.fail("todo;");
        case SYM.single_input:
            Sk.asserts.fail("todo;");
        default:
            Sk.asserts.fail("todo;");
    }
};

Sk.astDump = function (node) {
    var spaces = function (n) // todo; blurgh
    {
        var i;
        var ret = "";
        for (i = 0; i < n; ++i) {
            ret += " ";
        }
        return ret;
    };

    var _format = function (node, indent) {
        var ret;
        var elemsstr;
        var x;
        var elems;
        var fieldstr;
        var field;
        var attrs;
        var fieldlen;
        var b;
        var a;
        var i;
        var fields;
        var namelen;
        if (node === null) {
            return indent + "None";
        }
        else if (node.prototype && node.prototype._astname !== undefined && node.prototype._isenum) {
            return indent + node.prototype._astname + "()";
        }
        else if (node._astname !== undefined) {
            namelen = spaces(node._astname.length + 1);
            fields = [];
            for (i = 0; i < node._fields.length; i += 2) // iter_fields
            {
                a = node._fields[i]; // field name
                b = node._fields[i + 1](node); // field getter func
                fieldlen = spaces(a.length + 1);
                fields.push([a, _format(b, indent + namelen + fieldlen)]);
            }
            attrs = [];
            for (i = 0; i < fields.length; ++i) {
                field = fields[i];
                attrs.push(field[0] + "=" + field[1].replace(/^\s+/, ""));
            }
            fieldstr = attrs.join(",\n" + indent + namelen);
            return indent + node._astname + "(" + fieldstr + ")";
        }
        else if (Sk.isArrayLike(node)) {
            //Sk.debugout("arr", node.length);
            elems = [];
            for (i = 0; i < node.length; ++i) {
                x = node[i];
                elems.push(_format(x, indent + " "));
            }
            elemsstr = elems.join(",\n");
            return indent + "[" + elemsstr.replace(/^\s+/, "") + "]";
        }
        else {
            if (node === true) {
                ret = "True";
            }
            else if (node === false) {
                ret = "False";
            }
            else if (node instanceof Sk.builtin.lng) {
                ret = node.tp$str().v;
            }
            else if (node instanceof Sk.builtin.str) {
                ret = node["$r"]().v;
            }
            else {
                ret = "" + node;
            }
            return indent + ret;
        }
    };

    return _format(node, "");
};

Sk.exportSymbol("Sk.astFromParse", Sk.astFromParse);
Sk.exportSymbol("Sk.astDump", Sk.astDump);


/***/ }),

/***/ "./src/bool.js":
/*!*********************!*\
  !*** ./src/bool.js ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports) {

const int_proto = Sk.builtin.int_.prototype;

/**
 * @constructor
 * Sk.builtin.bool
 *
 * @description
 * Constructor for Python bool. Also used for builtin bool() function.
 *
 * Where possible, do not create a new instance but use the constants
 * Sk.builtin.bool.true$ or Sk.builtin.bool.false$. These are defined in src/constant.js
 *
 * @extends {Sk.builtin.int_}
 *
 * @param  {(Object|number|boolean)} x Value to evaluate as true or false
 * @return {Sk.builtin.bool} Sk.builtin.bool.true$ if x is true, Sk.builtin.bool.false$ otherwise
 */
Sk.builtin.bool = Sk.abstr.buildNativeClass("bool", {
    constructor: function bool (x) {
        if (Sk.misceval.isTrue(x)) {
            return Sk.builtin.bool.true$;
        } else {
            return Sk.builtin.bool.false$;
        }
    },
    base: Sk.builtin.int_,
    slots: {
        tp$doc:
            "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed.",
        tp$new(args, kwargs) {
            Sk.abstr.checkNoKwargs("bool", kwargs);
            Sk.abstr.checkArgsLen("bool", args, 0, 1);
            return new Sk.builtin.bool(args[0]); //technically we don't need new but easier to keep consistent
        },
        $r() {
            return this.v ? this.str$True : this.str$False;
        },

        tp$as_number: true,
        nb$and(other) {
            if (other.ob$type === Sk.builtin.bool) {
                return new Sk.builtin.bool(this.v & other.v);
            } 
            return int_proto.nb$and.call(this, other);
        },
        nb$or(other) {
            if (other.ob$type === Sk.builtin.bool) {
                return new Sk.builtin.bool(this.v | other.v);
            } 
            return int_proto.nb$or.call(this, other);
        },
        nb$xor(other) {
            if (other.ob$type === Sk.builtin.bool) {
                return new Sk.builtin.bool(this.v ^ other.v);
            } 
            return int_proto.nb$xor.call(this, other);
        },
    },
    flags: {
        sk$acceptable_as_base_class: false,
    },
    methods: {
        __format__: {
            $meth() {
                return this.$r();
            },
            $flags: {OneArg: true},
        }
    },
    proto: {
        str$False: new Sk.builtin.str("False"),
        str$True: new Sk.builtin.str("True"),
    }
});
Sk.exportSymbol("Sk.builtin.bool", Sk.builtin.bool);

/**
 * Python bool True constant.
 * @type {Sk.builtin.bool}
 * @member {Sk.builtin.bool}
 */
Sk.builtin.bool.true$ = /** @type {Sk.builtin.bool} */ (Object.create(Sk.builtin.bool.prototype, {
    v: { value: 1, enumerable: true },
}));

/**
 * Python bool False constant.
 * @type {Sk.builtin.bool}
 * @member {Sk.builtin.bool}
 */
Sk.builtin.bool.false$ = /** @type {Sk.builtin.bool} */ (Object.create(Sk.builtin.bool.prototype, {
    v: { value: 0, enumerable: true },
}));


/***/ }),

/***/ "./src/builtin.js":
/*!************************!*\
  !*** ./src/builtin.js ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {

/** @typedef {Sk.builtin.object} */ var pyObject;

/**
 * builtins are supposed to come from the __builtin__ module, but we don't do
 * that yet.
 * todo; these should all be func objects too, otherwise str() of them won't
 * work, etc.
 */


Sk.builtin.asnum$ = function (a) {
    if (a === undefined) {
        return a;
    }
    if (a === null) {
        return a;
    }
    if (typeof a === "number") {
        return a;
    }
    if (a instanceof Sk.builtin.int_) {
        if (typeof a.v === "number") {
            return a.v;
        }
        return a.v.toString(); // then we have a BigInt
    }
    if (a instanceof Sk.builtin.float_) {
        return a.v;
    }
    if (a === Sk.builtin.none.none$) {
        return null;
    }
    if (typeof a === "string") {
        return a;
    }
    return a;
};

Sk.exportSymbol("Sk.builtin.asnum$", Sk.builtin.asnum$);

/**
 * Return a Python number (either float or int) from a Javascript number.
 *
 * Javacsript function, returns Python object.
 *
 * @param  {number} a Javascript number to transform into Python number.
 * @return {(Sk.builtin.int_|Sk.builtin.float_)} A Python number.
 */
Sk.builtin.assk$ = function (a) {
    if (a % 1 === 0) {
        return new Sk.builtin.int_(a);
    } else {
        return new Sk.builtin.float_(a);
    }
};
Sk.exportSymbol("Sk.builtin.assk$", Sk.builtin.assk$);

Sk.builtin.asnum$nofloat = function (a) {
    var decimal;
    var mantissa;
    var expon;
    if (a === undefined) {
        return a;
    } else if (a === null) {
        return a;
    } else if (typeof a === "number") {
        a = a.toString();
    } else if (a instanceof Sk.builtin.int_) {
        a = a.v.toString();
    } else if (a instanceof Sk.builtin.float_) {
        a = a.v.toString();
    } else if (a === Sk.builtin.none.none$) {
        return null;
    } else {
        return undefined;
    }

    //  Sk.debugout("INITIAL: " + a);

    //  If not a float, great, just return this
    if (a.indexOf(".") < 0 && a.indexOf("e") < 0 && a.indexOf("E") < 0) {
        return a;
    }

    expon = 0;

    if (a.indexOf("e") >= 0) {
        mantissa = a.substr(0, a.indexOf("e"));
        expon = a.substr(a.indexOf("e") + 1);
    } else if (a.indexOf("E") >= 0) {
        mantissa = a.substr(0, a.indexOf("e"));
        expon = a.substr(a.indexOf("E") + 1);
    } else {
        mantissa = a;
    }

    expon = parseInt(expon, 10);

    decimal = mantissa.indexOf(".");

    //  Simplest case, no decimal
    if (decimal < 0) {
        if (expon >= 0) {
            // Just add more zeroes and we're done
            while (expon-- > 0) {
                mantissa += "0";
            }
            return mantissa;
        } else {
            if (mantissa.length > -expon) {
                return mantissa.substr(0, mantissa.length + expon);
            } else {
                return 0;
            }
        }
    }

    //  Negative exponent OR decimal (neg or pos exp)
    if (decimal === 0) {
        mantissa = mantissa.substr(1);
    } else if (decimal < mantissa.length) {
        mantissa = mantissa.substr(0, decimal) + mantissa.substr(decimal + 1);
    } else {
        mantissa = mantissa.substr(0, decimal);
    }

    decimal = decimal + expon;
    while (decimal > mantissa.length) {
        mantissa += "0";
    }

    if (decimal <= 0) {
        mantissa = 0;
    } else {
        mantissa = mantissa.substr(0, decimal);
    }

    return mantissa;
};
Sk.exportSymbol("Sk.builtin.asnum$nofloat", Sk.builtin.asnum$nofloat);

Sk.builtin.round = function round(number, ndigits) {
    if (number === undefined) {
        throw new Sk.builtin.TypeError("a float is required");
    }
    if (!Sk.__future__.dunder_round) {
        if (!Sk.builtin.checkNumber(number)) {
            throw new Sk.builtin.TypeError("a float is required");
        }
        if (number.round$) {
            return number.round$(ndigits);
        } else {
            throw new Sk.builtin.AttributeError(Sk.abstr.typeName(number) + " instance has no attribute '__float__'");
        }
    }

    if (ndigits !== undefined && !Sk.builtin.checkNone(ndigits) && !Sk.misceval.isIndex(ndigits)) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(ndigits) + "' object cannot be interpreted as an index");
    }

    // try calling internal magic method
    const special = Sk.abstr.lookupSpecial(number, Sk.builtin.str.$round);
    if (special !== undefined) {
        // method on builtin, provide this arg
        if (ndigits !== undefined) {
            return Sk.misceval.callsimArray(special, [ndigits]);
        } else {
            return Sk.misceval.callsimArray(special, []);
        }
    } else {
        throw new Sk.builtin.TypeError("a float is required");
    }
};

Sk.builtin.len = function len(item) {
    // checking will happen in slot wrapper
    let res;
    if (item.sq$length) {
        res = item.sq$length(true);
    } else {
        throw new Sk.builtin.TypeError("object of type '" + Sk.abstr.typeName(item) + "' has no len()");
    }
    return Sk.misceval.chain(res, (r) => {
        return new Sk.builtin.int_(r);
    });
};

Sk.builtin.min = function min(args, kwargs) {
    let iter;
    const nargs = args.length;
    if (!nargs) {
        throw new Sk.builtin.TypeError("min expected 1 argument, got 0");
    }
    const [$default, key] = Sk.abstr.copyKeywordsToNamedArgs("min", ["default", "key"], [], kwargs, [null, Sk.builtin.none.none$]);

    // if args is not a single iterable then default should not be included as a kwarg
    if (nargs > 1 && $default !== null) {
        throw new Sk.builtin.TypeError("Cannot specify a default for min() with multiple positional arguments");
    }

    if (nargs == 1) {
        iter = Sk.abstr.iter(args[0]);
    } else {
        iter = Sk.abstr.iter(new Sk.builtin.tuple(args));
    }

    if (!Sk.builtin.checkNone(key) && !Sk.builtin.checkCallable(key)) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(key) + "' object is not callable");
    }

    let lowest;
    return Sk.misceval.chain(
        iter.tp$iternext(true),
        (i) => {
            lowest = i;
            if (lowest === undefined) {
                return;
            }
            if (Sk.builtin.checkNone(key)) {
                return Sk.misceval.iterFor(iter, (i) => {
                    if (Sk.misceval.richCompareBool(i, lowest, "Lt")) {
                        lowest = i;
                    }
                });
            } else {
                return Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(key, [lowest]), (lowest_compare) =>
                    Sk.misceval.iterFor(iter, (i) =>
                        Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(key, [i]), (i_compare) => {
                            if (Sk.misceval.richCompareBool(i_compare, lowest_compare, "Lt")) {
                                lowest = i;
                                lowest_compare = i_compare;
                            }
                        })
                    )
                );
            }
        },
        () => {
            if (lowest === undefined) {
                if ($default === null) {
                    throw new Sk.builtin.ValueError("min() arg is an empty sequence");
                } else {
                    lowest = $default;
                }
            }
            return lowest;
        }
    );
};

Sk.builtin.max = function max(args, kwargs) {
    let iter;
    const nargs = args.length;

    if (!nargs) {
        throw new Sk.builtin.TypeError("max expected 1 argument, got 0");
    }
    const [$default, key] = Sk.abstr.copyKeywordsToNamedArgs("min", ["default", "key"], [], kwargs, [null, Sk.builtin.none.none$]);

    // if args is not a single iterable then default should not be included as a kwarg
    if (nargs > 1 && $default !== null) {
        throw new Sk.builtin.TypeError("Cannot specify a default for max() with multiple positional arguments");
    }

    if (nargs === 1) {
        iter = Sk.abstr.iter(args[0]);
    } else {
        iter = Sk.abstr.iter(new Sk.builtin.tuple(args));
    }

    if (!Sk.builtin.checkNone(key) && !Sk.builtin.checkCallable(key)) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(key) + "' object is not callable");
    }
    let highest;
    return Sk.misceval.chain(
        iter.tp$iternext(true),
        (i) => {
            highest = i;
            if (highest === undefined) {
                return;
            }
            if (Sk.builtin.checkNone(key)) {
                return Sk.misceval.iterFor(iter, (i) => {
                    if (Sk.misceval.richCompareBool(i, highest, "Gt")) {
                        highest = i;
                    }
                });
            } else {
                return Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(key, [highest]), (highest_compare) =>
                    Sk.misceval.iterFor(iter, (i) =>
                        Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(key, [i]), (i_compare) => {
                            if (Sk.misceval.richCompareBool(i_compare, highest_compare, "Gt")) {
                                highest = i;
                                highest_compare = i_compare;
                            }
                        })
                    )
                );
            }
        },
        () => {
            if (highest === undefined) {
                if ($default === null) {
                    throw new Sk.builtin.ValueError("min() arg is an empty sequence");
                } else {
                    highest = $default;
                }
            }
            return highest;
        }
    );
};

// incase someone calls these functions via Sk.misceval.call
Sk.builtin.min.co_fastcall = Sk.builtin.max.co_fastcall = 1;

Sk.builtin.any = function any(iter) {
    return Sk.misceval.chain(
        Sk.misceval.iterFor(Sk.abstr.iter(iter), function (i) {
            if (Sk.misceval.isTrue(i)) {
                return new Sk.misceval.Break(Sk.builtin.bool.true$);
            }
        }),
        (brValue) => brValue || Sk.builtin.bool.false$
    );
};

Sk.builtin.all = function all(iter) {
    return Sk.misceval.chain(
        Sk.misceval.iterFor(Sk.abstr.iter(iter), function (i) {
            if (!Sk.misceval.isTrue(i)) {
                return new Sk.misceval.Break(Sk.builtin.bool.false$);
            }
        }),
        (brValue) => brValue || Sk.builtin.bool.true$
    );
};

Sk.builtin.sum = function sum(iter, start) {
    var tot;
    // follows the order of CPython checks
    const it = Sk.abstr.iter(iter);
    if (start === undefined) {
        tot = new Sk.builtin.int_(0);
    } else if (Sk.builtin.checkString(start)) {
        throw new Sk.builtin.TypeError("sum() can't sum strings [use ''.join(seq) instead]");
    } else {
        tot = start;
    }

    function fastSumInt() {
        return Sk.misceval.iterFor(it, (i) => {
            if (i.constructor === Sk.builtin.int_) {
                tot = tot.nb$add(i);
            } else if (i.constructor === Sk.builtin.float_) {
                tot = tot.nb$float().nb$add(i);
                return new Sk.misceval.Break("float");
            } else {
                tot = Sk.abstr.numberBinOp(tot, i, "Add");
                return new Sk.misceval.Break("slow");
            }
        });
    }

    function fastSumFloat() {
        return Sk.misceval.iterFor(it, (i) => {
            if (i.constructor === Sk.builtin.float_ || i.constructor === Sk.builtin.int_) {
                tot = tot.nb$add(i);
            } else {
                tot = Sk.abstr.numberBinOp(tot, i, "Add");
                return new Sk.misceval.Break("slow");
            }
        });
    }

    function slowSum() {
        return Sk.misceval.iterFor(it, (i) => {
            tot = Sk.abstr.numberBinOp(tot, i, "Add");
        });
    }

    let sumType;
    if (start === undefined || start.constructor === Sk.builtin.int_) {
        sumType = fastSumInt();
    } else if (start.constructor === Sk.builtin.float_) {
        sumType = "float";
    } else {
        sumType = "slow";
    }

    return Sk.misceval.chain(
        sumType,
        (sumType) => {
            if (sumType === "float") {
                return fastSumFloat();
            }
            return sumType;
        },
        (sumType) => {
            if (sumType === "slow") {
                return slowSum();
            }
        },
        () => tot
    );
};

Sk.builtin.zip = function zip() {
    var el;
    var tup;
    var done;
    var res;
    var i;
    var iters;
    if (arguments.length === 0) {
        return new Sk.builtin.list([]);
    }

    iters = [];
    for (i = 0; i < arguments.length; i++) {
        if (Sk.builtin.checkIterable(arguments[i])) {
            iters.push(Sk.abstr.iter(arguments[i]));
        } else {
            throw new Sk.builtin.TypeError("argument " + i + " must support iteration");
        }
    }
    res = [];
    done = false;
    while (!done) {
        tup = [];
        for (i = 0; i < arguments.length; i++) {
            el = iters[i].tp$iternext();
            if (el === undefined) {
                done = true;
                break;
            }
            tup.push(el);
        }
        if (!done) {
            res.push(new Sk.builtin.tuple(tup));
        }
    }
    return new Sk.builtin.list(res);
};

Sk.builtin.abs = function abs(x) {
    if (x.nb$abs) {
        return x.nb$abs();
    }
    throw new TypeError("bad operand type for abs(): '" + Sk.abstr.typeName(x) + "'");
};

// fabs belongs in the math module but has been a Skulpt builtin since 41665a97d (2012).
// Left in for backwards compatibility for now
Sk.builtin.fabs = function fabs(x) {
    return Sk.builtin.abs(x);
};

Sk.builtin.ord = function ord (x) {
    if (Sk.builtin.checkString(x)) {
        if (x.v.length !== 1 && x.sq$length() !== 1) {
            // ^^ avoid the astral check unless necessary ^^
            throw new Sk.builtin.TypeError("ord() expected a character, but string of length " + x.v.length + " found");
        }
        return new Sk.builtin.int_(x.v.codePointAt(0));
    } else if (Sk.builtin.checkBytes(x)) {
        if (x.sq$length() !== 1) {
            throw new Sk.builtin.TypeError("ord() expected a character, but string of length " + x.v.length + " found");
        }
        return new Sk.builtin.int_(x.v[0]);
    }
    throw new Sk.builtin.TypeError("ord() expected a string of length 1, but " + Sk.abstr.typeName(x) + " found");
};

Sk.builtin.chr = function chr(x) {
    if (!Sk.builtin.checkInt(x)) {
        throw new Sk.builtin.TypeError("an integer is required");
    }
    x = Sk.builtin.asnum$(x);
    if (Sk.__future__.python3) {
        if ((x < 0) || (x >= 0x110000)) {
            throw new Sk.builtin.ValueError("chr() arg not in range(0x110000)");
        }
    } else {
        if ((x < 0) || (x >= 256)) {
            throw new Sk.builtin.ValueError("chr() arg not in range(256)");
        }
    }

    return new Sk.builtin.str(String.fromCodePoint(x));
};

Sk.builtin.unichr = function unichr(x) {
    if (!Sk.builtin.checkInt(x)) {
        throw new Sk.builtin.TypeError("an integer is required");
    }
    x = Sk.builtin.asnum$(x);

    try {
        return new Sk.builtin.str(String.fromCodePoint(x));
    } catch (err) {
        if (err instanceof RangeError) {
            throw new Sk.builtin.ValueError(err.message);
        }
        throw err;
    }
};

/**
 * This is a helper function and we already know that x is an int or has an nb$index slot
 */
Sk.builtin.int2str_ = function helper_(x, radix, prefix) {
    let v;
    if (x.constructor === Sk.builtin.int_ || x instanceof Sk.builtin.int_) {
        v = x.v; // we don't use asnum$ because it returns a str rather than a bigint.
    } else {
        x = x.nb$index();
        v = x.v;
    }
    let str = v.toString(radix);
    if (x.nb$isnegative()) {
        str = "-" + prefix + str.slice(1);
    } else {
        str = prefix + str;
    }
    if (radix !== 2 && !Sk.__future__.python3 && (x instanceof Sk.builtin.lng || JSBI.__isBigInt(v))) {
        str += "L";
    }
    return new Sk.builtin.str(str);
};

Sk.builtin.hex = function hex(x) {
    if (!Sk.misceval.isIndex(x)) {
        throw new Sk.builtin.TypeError("hex() argument can't be converted to hex");
    }
    return Sk.builtin.int2str_(x, 16, "0x");
};

Sk.builtin.oct = function oct(x) {
    if (!Sk.misceval.isIndex(x)) {
        throw new Sk.builtin.TypeError("oct() argument can't be converted to hex");
    }
    if (Sk.__future__.octal_number_literal) {
        return Sk.builtin.int2str_(x, 8, "0o");
    } else {
        return Sk.builtin.int2str_(x, 8, "0");
    }
};

Sk.builtin.bin = function bin(x) {
    if (!Sk.misceval.isIndex(x)) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(x) + "' object can't be interpreted as an index");
    }
    return Sk.builtin.int2str_(x, 2, "0b");
};


Sk.builtin.dir = function dir(obj) {
    if (obj !== undefined) {
        const obj_dir_func = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$dir);
        return Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(obj_dir_func, []), (dir) => Sk.builtin.sorted(dir));
        // now iter through the keys and check they are all stings
    }
    // then we want all the objects in the global scope
    //todo
    throw new Sk.builtin.NotImplementedError("skulpt does not yet support dir with no args");
};

Sk.builtin.repr = function repr(x) {
    return x.$r();
};

Sk.builtin.ascii = function ascii (x) {
    return Sk.misceval.chain(x.$r(), (r) => {
        let ret;
        let i;
        // Fast path
        for (i=0; i < r.v.length; i++) {
            if (r.v.charCodeAt(i) >= 0x7f) {
                ret = r.v.substr(0, i);
                break;
            }
        }
        if (!ret) {
            return r;
        }
        for (; i < r.v.length; i++) {
            let c = r.v.charAt(i);
            let cc = r.v.charCodeAt(i);

            if (cc > 0x7f && cc <= 0xff) {
                let ashex = cc.toString(16);
                if (ashex.length < 2) {
                    ashex = "0" + ashex;
                }
                ret += "\\x" + ashex;
            } else if (cc > 0x7f && cc < 0xd800 || cc >= 0xe000) {
                // BMP
                ret += "\\u" + ("000"+cc.toString(16)).slice(-4);
            } else if (cc >= 0xd800) {
                // Surrogate pair stuff
                let val = r.v.codePointAt(i);
                i++;

                val = val.toString(16);
                let s = ("0000000"+val.toString(16));
                if (val.length > 4) {
                    ret += "\\U" + s.slice(-8);
                } else {
                    ret += "\\u" + s.slice(-4);
                }
            } else {
                ret += c;
            }
        }
        return new Sk.builtin.str(ret);
    });
};

Sk.builtin.open = function open (filename, mode, bufsize) {
    if (mode === undefined) {
        mode = new Sk.builtin.str("r");
    }

    if (/\+/.test(mode.v)) {
        throw "todo; haven't implemented read/write mode";
    } else if ((mode.v === "w" || mode.v === "wb" || mode.v === "a" || mode.v === "ab") && !Sk.nonreadopen) {
        throw "todo; haven't implemented non-read opens";
    }

    return new Sk.builtin.file(filename, mode, bufsize);
};


Sk.builtin.isinstance = function isinstance(obj, type) {
    if (!Sk.builtin.checkClass(type) && !(type instanceof Sk.builtin.tuple)) {
        throw new Sk.builtin.TypeError("isinstance() arg 2 must be a class, type, or tuple of classes and types");
    }

    // Fast path
    const act_type = obj.ob$type;
    if (act_type === type) {
        return Sk.builtin.bool.true$;
    }
    if (!(type instanceof Sk.builtin.tuple)) {
        // attempt 1
        if (act_type.$isSubType(type)) {
            return Sk.builtin.bool.true$;
        }
        // fail so check if we have overriden __class__
        const maybe_type = obj.tp$getattr(Sk.builtin.str.$class);
        if (maybe_type == act_type) {
            return Sk.builtin.bool.false$;
        } else if (Sk.builtin.checkClass(maybe_type) && maybe_type.$isSubType(type)) {
            return Sk.builtin.bool.true$;
        }
        return Sk.builtin.bool.false$;
    }
    // Handle tuple type argument
    for (let i = 0; i < type.v.length; ++i) {
        if (Sk.misceval.isTrue(Sk.builtin.isinstance(obj, type.v[i]))) {
            return Sk.builtin.bool.true$;
        }
    }
    return Sk.builtin.bool.false$;
};

Sk.builtin.hash = function hash(obj) {
    return new Sk.builtin.int_(Sk.abstr.objectHash(obj));
};

Sk.builtin.getattr = function getattr(obj, pyName, default_) {
    if (!Sk.builtin.checkString(pyName)) {
        throw new Sk.builtin.TypeError("attribute name must be string");
    }
    const res = Sk.misceval.tryCatch(
        () => obj.tp$getattr(pyName, true),
        (e) => {
            if (e instanceof Sk.builtin.AttributeError) {
                return undefined;
            } else {
                throw e;
            }
        }
    );
    return Sk.misceval.chain(res, (r) => {
        if (r === undefined) {
            if (default_ !== undefined) {
                return default_;
            }
            throw new Sk.builtin.AttributeError(obj.sk$attrError() + " has no attribute " + Sk.misceval.objectRepr(pyName));
        }
        return r;
    });
};

Sk.builtin.setattr = function setattr(obj, pyName, value) {
    // cannot set or del attr from builtin type
    if (!Sk.builtin.checkString(pyName)) {
        throw new Sk.builtin.TypeError("attribute name must be string");
    }
    return Sk.misceval.chain(obj.tp$setattr(pyName, value, true), () => Sk.builtin.none.none$);
};

Sk.builtin.raw_input = function (prompt) {
    var lprompt = prompt ? prompt : "";

    return Sk.misceval.chain(Sk.importModule("sys", false, true), function (sys) {
        if (Sk.inputfunTakesPrompt) {
            return Sk.builtin.file.$readline(sys["$d"]["stdin"], null, lprompt);
        } else {
            return Sk.misceval.chain(
                undefined,
                function () {
                    return Sk.misceval.callsimOrSuspendArray(sys["$d"]["stdout"]["write"], [sys["$d"]["stdout"], new Sk.builtin.str(lprompt)]);
                },
                function () {
                    return Sk.misceval.callsimOrSuspendArray(sys["$d"]["stdin"]["readline"], [sys["$d"]["stdin"]]);
                }
            );
        }
    });
};

Sk.builtin.input = Sk.builtin.raw_input;

Sk.builtin.jseval = function jseval (evalcode) {
    const result = Sk.global["eval"](Sk.ffi.remapToJs(evalcode));
    return Sk.ffi.remapToPy(result);
};

/**
 * @deprecated
 */
Sk.builtin.jsmillis = function jsmillis() {
    console.warn("jsmillis is deprecated");
    var now = new Date();
    return now.valueOf();
};

Sk.builtin.eval_ = function eval_() {
    throw new Sk.builtin.NotImplementedError("eval is not yet implemented");
};

Sk.builtin.map = function map(fun, seq) {
    var retval = [];
    var next;
    var nones;
    var args;
    var argnum;
    var i;
    var iterables;
    var combined;
    Sk.builtin.pyCheckArgsLen("map", arguments.length, 2);

    if (arguments.length > 2) {
        // Pack sequences into one list of Javascript Arrays

        combined = [];
        iterables = Array.prototype.slice.apply(arguments).slice(1);
        for (i = 0; i < iterables.length; i++) {
            if (!Sk.builtin.checkIterable(iterables[i])) {
                argnum = parseInt(i, 10) + 2;
                throw new Sk.builtin.TypeError("argument " + argnum + " to map() must support iteration");
            }
            iterables[i] = Sk.abstr.iter(iterables[i]);
        }

        while (true) {
            args = [];
            nones = 0;
            for (i = 0; i < iterables.length; i++) {
                next = iterables[i].tp$iternext();
                if (next === undefined) {
                    args.push(Sk.builtin.none.none$);
                    nones++;
                } else {
                    args.push(next);
                }
            }
            if (nones !== iterables.length) {
                combined.push(args);
            } else {
                // All iterables are done
                break;
            }
        }
        seq = new Sk.builtin.list(combined);
    }

    if (!Sk.builtin.checkIterable(seq)) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq) + "' object is not iterable");
    }

    return Sk.misceval.chain(
        Sk.misceval.iterFor(Sk.abstr.iter(seq), function (item) {
            if (fun === Sk.builtin.none.none$) {
                if (item instanceof Array) {
                    // With None function and multiple sequences,
                    // map should return a list of tuples
                    item = new Sk.builtin.tuple(item);
                }
                retval.push(item);
            } else {
                if (!(item instanceof Array)) {
                    // If there was only one iterable, convert to Javascript
                    // Array for call to apply.
                    item = [item];
                }

                return Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(fun, item), function (result) {
                    retval.push(result);
                });
            }
        }),
        function () {
            return new Sk.builtin.list(retval);
        }
    );
};

Sk.builtin.reduce = function reduce(fun, seq, initializer) {
    var item;
    var accum_value;
    var iter;
    if (!Sk.builtin.checkIterable(seq)) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq) + "' object is not iterable");
    }

    iter = Sk.abstr.iter(seq);
    if (initializer === undefined) {
        initializer = iter.tp$iternext();
        if (initializer === undefined) {
            throw new Sk.builtin.TypeError("reduce() of empty sequence with no initial value");
        }
    }
    accum_value = initializer;
    for (item = iter.tp$iternext(); item !== undefined; item = iter.tp$iternext()) {
        accum_value = Sk.misceval.callsimArray(fun, [accum_value, item]);
    }

    return accum_value;
};

/**
 *
 * @param {pyObject} iterable
 * @param {*=} cmp
 * @param {*=} key
 * @param {*=} reverse
 */
Sk.builtin.sorted = function sorted(iterable, cmp, key, reverse) {
    const lst = Sk.misceval.arrayFromIterable(iterable, true);
    return Sk.misceval.chain(lst, (L) => {
        L = new Sk.builtin.list(L);
        L.list$sort(cmp, key, reverse);
        return L;
    });
};

Sk.builtin.filter = function filter(fun, iterable) {
    var result;
    var iter, item;
    var retval;
    var ret;
    var add;
    var ctor;
    Sk.builtin.pyCheckArgsLen("filter", arguments.length, 2, 2);
    if (!Sk.builtin.checkIterable(iterable)) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iterable) + "' object is not iterable");
    }
    ctor = function () {
        return [];
    };
    add = function (iter, item) {
        iter.push(item);
        return iter;
    };
    ret = function (iter) {
        return new Sk.builtin.list(iter);
    };

    if (iterable.ob$type === Sk.builtin.str) {
        ctor = function () {
            return new Sk.builtin.str("");
        };
        add = function (iter, item) {
            return iter.sq$concat(item);
        };
        ret = function (iter) {
            return iter;
        };
    } else if (iterable.ob$type === Sk.builtin.tuple) {
        ret = function (iter) {
            return new Sk.builtin.tuple(iter);
        };
    }

    retval = ctor();

    for (iter = Sk.abstr.iter(iterable), item = iter.tp$iternext(); item !== undefined; item = iter.tp$iternext()) {
        if (fun === Sk.builtin.none.none$) {
            result = new Sk.builtin.bool(item);
        } else {
            result = Sk.misceval.callsimArray(fun, [item]);
        }

        if (Sk.misceval.isTrue(result)) {
            retval = add(retval, item);
        }
    }

    return ret(retval);
};

Sk.builtin.hasattr = function hasattr(obj, pyName) {
    if (!Sk.builtin.checkString(pyName)) {
        throw new Sk.builtin.TypeError("hasattr(): attribute name must be string");
    }
    const res = Sk.misceval.tryCatch(
        () => obj.tp$getattr(pyName, true),
        (e) => {
            if (e instanceof Sk.builtin.AttributeError) {
                return undefined;
            } else {
                throw e;
            }
        }
    );
    return Sk.misceval.chain(res, (val) => (val === undefined ? Sk.builtin.bool.false$ : Sk.builtin.bool.true$));
};

Sk.builtin.pow = function pow(v, w, z) {
    // skulpt does support ternary slots
    if (z === undefined || Sk.builtin.checkNone(z)) {
        return Sk.abstr.numberBinOp(v, w, "Pow");
    }
    // only support a third argument if they're all the integers.
    if (!(Sk.builtin.checkInt(v) && Sk.builtin.checkInt(w) && Sk.builtin.checkInt(z))) {
        if (Sk.builtin.checkFloat(v) || Sk.builtin.checkComplex(v)) {
            return v.nb$power(w, z); // these slots for float and complex throw the correct errors
        }
        throw new Sk.builtin.TypeError(
            "unsupported operand type(s) for ** or pow(): '" + Sk.abstr.typeName(v) + "', '" + Sk.abstr.typeName(w) + "', '" + Sk.abstr.typeName(z) + "'"
        );
    }
    return v.nb$power(w, z);
};

Sk.builtin.quit = function quit(msg) {
    var s = new Sk.builtin.str(msg).v;
    throw new Sk.builtin.SystemExit(s);
};

Sk.builtin.issubclass = function issubclass(c1, c2) {
    if (!Sk.builtin.checkClass(c1)) {
        throw new Sk.builtin.TypeError("issubclass() arg 1 must be a class");
    }
    let c2_isClass = Sk.builtin.checkClass(c2);
    if (!c2_isClass && !(c2 instanceof Sk.builtin.tuple)) {
        throw new Sk.builtin.TypeError("issubclass() arg 2 must be a class or tuple of classes");
    }
    if (c2_isClass) {
        return c1.$isSubType(c2) ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$;
    }
    // Handle tuple type argument
    for (let i = 0; i < c2.v.length; ++i) {
        if (Sk.misceval.isTrue(Sk.builtin.issubclass(c1, c2.v[i]))) {
            return Sk.builtin.bool.true$;
        }
    }
    return Sk.builtin.bool.false$;
};

Sk.builtin.globals = function globals () {
    var i, unmangled;
    var ret = new Sk.builtin.dict([]);
    for (i in Sk["globals"]) {
        unmangled = Sk.unfixReserved(i);
        ret.mp$ass_subscript(new Sk.builtin.str(unmangled), Sk["globals"][i]);
    }

    return ret;
};

Sk.builtin.divmod = function divmod(a, b) {
    return Sk.abstr.numberBinOp(a, b, "DivMod");
};

/**
 * Convert a value to a “formatted” representation, as controlled by format_spec. The interpretation of format_spec
 * will depend on the type of the value argument, however there is a standard formatting syntax that is used by most
 * built-in types: Format Specification Mini-Language.
 */
Sk.builtin.format = function format(value, format_spec) {
    if (format_spec === undefined) {
        format_spec = Sk.builtin.str.$emptystr;
    }

    return Sk.abstr.objectFormat(value, format_spec);
};

const idMap = new Map();
let _id = 0;
Sk.builtin.id = function (obj) {
    const id = idMap.get(obj);
    if (id !== undefined) {
        return new Sk.builtin.int_(id);
    }
    idMap.set(obj, _id);
    return new Sk.builtin.int_(_id++);
};

Sk.builtin.bytearray = function bytearray() {
    throw new Sk.builtin.NotImplementedError("bytearray is not yet implemented");
};

Sk.builtin.callable = function callable(obj) {
    // check num of args

    if (Sk.builtin.checkCallable(obj)) {
        return Sk.builtin.bool.true$;
    }
    return Sk.builtin.bool.false$;
};

Sk.builtin.delattr = function delattr(obj, attr) {
    return Sk.builtin.setattr(obj, attr, undefined);
};

Sk.builtin.execfile = function execfile() {
    throw new Sk.builtin.NotImplementedError("execfile is not yet implemented");
};

Sk.builtin.help = function help() {
    throw new Sk.builtin.NotImplementedError("help is not yet implemented");
};

Sk.builtin.iter = function iter(obj, sentinel) {
    if (arguments.length === 1) {
        return Sk.abstr.iter(obj);
    } else {
        return Sk.abstr.iter(new Sk.builtin.callable_iter_(obj, sentinel));
    }
};

Sk.builtin.locals = function locals() {
    throw new Sk.builtin.NotImplementedError("locals is not yet implemented");
};
Sk.builtin.memoryview = function memoryview() {
    throw new Sk.builtin.NotImplementedError("memoryview is not yet implemented");
};

Sk.builtin.next_ = function next_(iter, default_) {
    var nxt;
    if (!iter.tp$iternext) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iter) + "' object is not an iterator");
    }
    nxt = iter.tp$iternext();
    if (nxt === undefined) {
        if (default_) {
            return default_;
        }
        throw new Sk.builtin.StopIteration();
    }
    return nxt;
};

Sk.builtin.reload = function reload() {
    throw new Sk.builtin.NotImplementedError("reload is not yet implemented");
};
Sk.builtin.vars = function vars() {
    throw new Sk.builtin.NotImplementedError("vars is not yet implemented");
};

Sk.builtin.apply_ = function apply_() {
    throw new Sk.builtin.NotImplementedError("apply is not yet implemented");
};
Sk.builtin.buffer = function buffer_() {
    throw new Sk.builtin.NotImplementedError("buffer is not yet implemented");
};
Sk.builtin.coerce = function coerce() {
    throw new Sk.builtin.NotImplementedError("coerce is not yet implemented");
};
Sk.builtin.intern = function intern() {
    throw new Sk.builtin.NotImplementedError("intern is not yet implemented");
};

/*
 Sk.builtinFiles = {};
 Sk.builtin.read = function read(x) {
 if (Sk.builtinFiles === undefined || Sk.builtinFiles["files"][x] === undefined)
 throw "File not found: '" + x + "'";
 return Sk.builtinFiles["files"][x];
 };
 Sk.builtinFiles = undefined;
 */


/***/ }),

/***/ "./src/builtindict.js":
/*!****************************!*\
  !*** ./src/builtindict.js ***!
  \****************************/
/*! no static exports found */
/***/ (function(module, exports) {

// Note: the hacky names on int, long, float have to correspond with the
// uniquization that the compiler does for words that are reserved in
// Javascript. This is a bit hokey.

Sk.builtins = {
    "round"     : null,
    "len"       : null,
    "min"       : null,
    "max"       : null,
    "sum"       : null,
    "abs"       : null,
    "fabs"      : null,
    "ord"       : null,
    "chr"       : null,
    "hex"       : null,
    "oct"       : null,
    "bin"       : null,
    "dir"       : null,
    "repr"      : null,
    "open"      : null,
    "isinstance": null,
    "hash"      : null,
    "getattr"   : null,
    "hasattr"   : null,
    "id"        : null,
    
    "reduce"    : new Sk.builtin.func(Sk.builtin.reduce),
    "sorted"    : null,
    "any"       : null,
    "all"       : null,
    
    // iterator objects if py2 mode we replace these with sk_methods
    "enumerate" : Sk.builtin.enumerate,
    "filter"    : Sk.builtin.filter_,
    "map"       : Sk.builtin.map_,
    "range"     : Sk.builtin.range_,
    "reversed"  : Sk.builtin.reversed,
    "zip"       : Sk.builtin.zip_,

    "BaseException"      : Sk.builtin.BaseException, 
    "AttributeError"     : Sk.builtin.AttributeError,
    "ValueError"         : Sk.builtin.ValueError,
    "Exception"          : Sk.builtin.Exception,
    "ZeroDivisionError"  : Sk.builtin.ZeroDivisionError,
    "AssertionError"     : Sk.builtin.AssertionError,
    "ImportError"        : Sk.builtin.ImportError,
    "IndentationError"   : Sk.builtin.IndentationError,
    "IndexError"         : Sk.builtin.IndexError,
    "LookupError"        : Sk.builtin.LookupError,
    "KeyError"           : Sk.builtin.KeyError,
    "TypeError"          : Sk.builtin.TypeError,
    "UnicodeDecodeError" : Sk.builtin.UnicodeDecodeError,
    "UnicodeEncodeError" : Sk.builtin.UnicodeEncodeError,
    "NameError"          : Sk.builtin.NameError,
    "IOError"            : Sk.builtin.IOError,
    "NotImplementedError": Sk.builtin.NotImplementedError,
    "SystemExit"         : Sk.builtin.SystemExit,
    "OverflowError"      : Sk.builtin.OverflowError,
    "OperationError"     : Sk.builtin.OperationError,
    "NegativePowerError" : Sk.builtin.NegativePowerError,
    "RuntimeError"       : Sk.builtin.RuntimeError,
    "RecursionError"     : Sk.builtin.RecursionError,
    "StopIteration"      : Sk.builtin.StopIteration,
    "SyntaxError"        : Sk.builtin.SyntaxError,
    "SystemError"        : Sk.builtin.SystemError,

    "float_$rw$": Sk.builtin.float_,
    "int_$rw$"  : Sk.builtin.int_,
    "bool"      : Sk.builtin.bool,
    "complex"   : Sk.builtin.complex,
    "dict"      : Sk.builtin.dict,
    "file"      : Sk.builtin.file,
    "frozenset" : Sk.builtin.frozenset,
    "function"  : Sk.builtin.func,
    "generator" : Sk.builtin.generator,
    "list"      : Sk.builtin.list,
    "long_$rw$" : Sk.builtin.lng,
    "method"    : Sk.builtin.method,
    "object"    : Sk.builtin.object,
    "slice"     : Sk.builtin.slice,
    "str"       : Sk.builtin.str,
    "set"       : Sk.builtin.set,
    "tuple"     : Sk.builtin.tuple,
    "type"      : Sk.builtin.type,

    "input"     : null,
    "raw_input" : new Sk.builtin.func(Sk.builtin.raw_input),
    "setattr"   : null,
    /*'read': Sk.builtin.read,*/
    "jseval"    : Sk.builtin.jseval,
    "jsmillis"  : Sk.builtin.jsmillis,
    "quit"      : new Sk.builtin.func(Sk.builtin.quit),
    "exit"      : new Sk.builtin.func(Sk.builtin.quit),
    "print"     : null,
    "divmod"    : null,
    "format"    : null,
    "globals"   : null,
    "issubclass": null,
    "iter"      : null,

    // Functions below are not implemented
    // "bytearray" : Sk.builtin.bytearray,
    // "callable"  : Sk.builtin.callable,
    // "delattr"   : Sk.builtin.delattr,
    // "eval_$rw$" : Sk.builtin.eval_,
    "execfile"  : Sk.builtin.execfile,
    
    "help"      : Sk.builtin.help,
    // "locals"    : Sk.builtin.locals,
    "memoryview": Sk.builtin.memoryview,
    // "next"      : Sk.builtin.next_,
    // "pow"       : Sk.builtin.pow,
    "reload"    : Sk.builtin.reload,
    "super_$rw$"     : Sk.builtin.super_,
    "unichr"    : Sk.builtin.unichr,
    "vars"      : Sk.builtin.vars,
    "apply_$rw$": Sk.builtin.apply_,
    "buffer"    : Sk.builtin.buffer,
    "coerce"    : Sk.builtin.coerce,
    "intern"    : Sk.builtin.intern,


    "property"     : Sk.builtin.property,
    "classmethod"  : Sk.builtin.classmethod,
    "staticmethod" : Sk.builtin.staticmethod,
};


Sk.abstr.setUpModuleMethods("builtins", Sk.builtins, {
    // __build_class__: {
    //     $meth: Sk.builtin.__build_class__,
    //     $flags: {},
    //     $textsig: null,
    //     $doc: "__build_class__(func, name, *bases, metaclass=None, **kwds) -> class\n\nInternal helper function used by the class statement."
    // },

    __import__: {
        $meth: Sk.builtin.__import__,
        $flags: { NamedArgs: ["name", "globals", "locals", "fromlist", "level"] },
        $textsig: null,
        $doc:
            "__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module\n\nImport a module. Because this function is meant for use by the Python\ninterpreter and not for general use, it is better to use\nimportlib.import_module() to programmatically import a module.\n\nThe globals argument is only used to determine the context;\nthey are not modified.  The locals argument is unused.  The fromlist\nshould be a list of names to emulate ``from name import ...'', or an\nempty list to emulate ``import name''.\nWhen importing a module from a package, note that __import__('A.B', ...)\nreturns package A when fromlist is empty, but its submodule B when\nfromlist is not empty.  The level argument is used to determine whether to\nperform absolute or relative imports: 0 is absolute, while a positive number\nis the number of parent directories to search relative to the current module.",
    },

    abs: {
        $meth: Sk.builtin.abs,
        $flags: { OneArg: true },
        $textsig: "($module, x, /)",
        $doc: "Return the absolute value of the argument.",
    },

    all: {
        $meth: Sk.builtin.all,
        $flags: { OneArg: true },
        $textsig: "($module, iterable, /)",
        $doc: "Return True if bool(x) is True for all values x in the iterable.\n\nIf the iterable is empty, return True.",
    },

    any: {
        $meth: Sk.builtin.any,
        $flags: { OneArg: true },
        $textsig: "($module, iterable, /)",
        $doc: "Return True if bool(x) is True for any x in the iterable.\n\nIf the iterable is empty, return False.",
    },

    ascii: {
        $meth: Sk.builtin.ascii,
        $flags: {OneArg: true},
        $textsig: "($module, obj, /)",
        $doc: "Return an ASCII-only representation of an object.\n\nAs repr(), return a string containing a printable representation of an\nobject, but escape the non-ASCII characters in the string returned by\nrepr() using \\\\x, \\\\u or \\\\U escapes. This generates a string similar\nto that returned by repr() in Python 2."
    },

    bin: {
        $meth: Sk.builtin.bin,
        $flags: { OneArg: true },
        $textsig: "($module, number, /)",
        $doc: "Return the binary representation of an integer.\n\n   >>> bin(2796202)\n   '0b1010101010101010101010'",
    },

    // breakpoint: {
    //     $meth: Sk.builtin.breakpoint,
    //     $flags: {},
    //     $textsig: null,
    //     $doc: "breakpoint(*args, **kws)\n\nCall sys.breakpointhook(*args, **kws).  sys.breakpointhook() must accept\nwhatever arguments are passed.\n\nBy default, this drops you into the pdb debugger."
    // },

    callable: {
        $meth: Sk.builtin.callable,
        $flags: { OneArg: true },
        $textsig: "($module, obj, /)",
        $doc:
            "Return whether the object is callable (i.e., some kind of function).\n\nNote that classes are callable, as are instances of classes with a\n__call__() method.",
    },

    chr: {
        $meth: Sk.builtin.chr,
        $flags: { OneArg: true },
        $textsig: "($module, i, /)",
        $doc: "Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.",
    },

    // compile: {
    //     $meth: Sk.builtin.compile,
    //     $flags: {},
    //     $textsig: "($module, /, source, filename, mode, flags=0,\n        dont_inherit=False, optimize=-1)",
    //     $doc: "Compile source into a code object that can be executed by exec() or eval().\n\nThe source code may represent a Python module, statement or expression.\nThe filename will be used for run-time error messages.\nThe mode must be 'exec' to compile a module, 'single' to compile a\nsingle (interactive) statement, or 'eval' to compile an expression.\nThe flags argument, if present, controls which future statements influence\nthe compilation of the code.\nThe dont_inherit argument, if true, stops the compilation inheriting\nthe effects of any future statements in effect in the code calling\ncompile; if absent or false these statements do influence the compilation,\nin addition to any features explicitly specified."
    // },

    delattr: {
        $meth: Sk.builtin.delattr,
        $flags: { MinArgs: 2, MaxArgs: 2 },
        $textsig: "($module, obj, name, /)",
        $doc: "Deletes the named attribute from the given object.\n\ndelattr(x, 'y') is equivalent to ``del x.y''",
    },

    dir: {
        $meth: Sk.builtin.dir,
        $flags: { MinArgs: 0, MaxArgs: 1 },
        $textsig: null,
        $doc:
            "dir([object]) -> list of strings\n\nIf called without an argument, return the names in the current scope.\nElse, return an alphabetized list of names comprising (some of) the attributes\nof the given object, and of attributes reachable from it.\nIf the object supplies a method named __dir__, it will be used; otherwise\nthe default dir() logic is used and returns:\n  for a module object: the module's attributes.\n  for a class object:  its attributes, and recursively the attributes\n    of its bases.\n  for any other object: its attributes, its class's attributes, and\n    recursively the attributes of its class's base classes.",
    },

    divmod: {
        $meth: Sk.builtin.divmod,
        $flags: { MinArgs: 2, MaxArgs: 2 },
        $textsig: "($module, x, y, /)",
        $doc: "Return the tuple (x//y, x%y).  Invariant: div*y + mod == x.",
    },

    eval_$rw$: {
        $name: "eval",
        $meth: Sk.builtin.eval_,
        $flags: { MinArgs: 1, MaxArgs: 3 },
        $textsig: "($module, source, globals=None, locals=None, /)",
        $doc:
            "Evaluate the given source in the context of globals and locals.\n\nThe source may be a string representing a Python expression\nor a code object as returned by compile().\nThe globals must be a dictionary and locals can be any mapping,\ndefaulting to the current globals and locals.\nIf only globals is given, locals defaults to it.",
    },

    // exec: {
    //     $meth: Sk.builtin.exec,
    //     $flags: {MinArgs:2, MaxArgs: 3},
    //     $textsig: "($module, source, globals=None, locals=None, /)",
    //     $doc: "Execute the given source in the context of globals and locals.\n\nThe source may be a string representing one or more Python statements\nor a code object as returned by compile().\nThe globals must be a dictionary and locals can be any mapping,\ndefaulting to the current globals and locals.\nIf only globals is given, locals defaults to it."
    // },

    format: {
        $meth: Sk.builtin.format,
        $flags: { MinArgs: 1, MaxArgs: 2 },
        $textsig: "($module, value, format_spec='', /)",
        $doc:
            "Return value.__format__(format_spec)\n\nformat_spec defaults to the empty string.\nSee the Format Specification Mini-Language section of help('FORMATTING') for\ndetails.",
    },

    getattr: {
        $meth: Sk.builtin.getattr,
        $flags: { MinArgs: 2, MaxArgs: 3 },
        $textsig: null,
        $doc:
            "getattr(object, name[, default]) -> value\n\nGet a named attribute from an object; getattr(x, 'y') is equivalent to x.y.\nWhen a default argument is given, it is returned when the attribute doesn't\nexist; without it, an exception is raised in that case.",
    },

    globals: {
        $meth: Sk.builtin.globals,
        $flags: { NoArgs: true },
        $textsig: "($module, /)",
        $doc:
            "Return the dictionary containing the current scope's global variables.\n\nNOTE: Updates to this dictionary *will* affect name lookups in the current\nglobal scope and vice-versa.",
    },

    hasattr: {
        $meth: Sk.builtin.hasattr,
        $flags: { MinArgs: 2, MaxArgs: 2 },
        $textsig: "($module, obj, name, /)",
        $doc:
            "Return whether the object has an attribute with the given name.\n\nThis is done by calling getattr(obj, name) and catching AttributeError.",
    },

    hash: {
        $meth: Sk.builtin.hash,
        $flags: { OneArg: true },
        $textsig: "($module, obj, /)",
        $doc:
            "Return the hash value for the given object.\n\nTwo objects that compare equal must also have the same hash value, but the\nreverse is not necessarily true.",
    },

    hex: {
        $meth: Sk.builtin.hex,
        $flags: { OneArg: true },
        $textsig: "($module, number, /)",
        $doc: "Return the hexadecimal representation of an integer.\n\n   >>> hex(12648430)\n   '0xc0ffee'",
    },

    id: {
        $meth: Sk.builtin.id,
        $flags: { OneArg: true },
        $textsig: "($module, obj, /)",
        $doc:
            "Return the identity of an object.\n\nThis is guaranteed to be unique among simultaneously existing objects.\n(CPython uses the object's memory address.)",
    },

    input: {
        $meth: Sk.builtin.input,
        $flags: { MinArgs: 0, MaxArgs: 1 },
        $textsig: "($module, prompt=None, /)",
        $doc:
            "Read a string from standard input.  The trailing newline is stripped.\n\nThe prompt string, if given, is printed to standard output without a\ntrailing newline before reading input.\n\nIf the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.\nOn *nix systems, readline is used if available.",
    },

    isinstance: {
        $meth: Sk.builtin.isinstance,
        $flags: { MinArgs: 2, MaxArgs: 2 },
        $textsig: "($module, obj, class_or_tuple, /)",
        $doc:
            "Return whether an object is an instance of a class or of a subclass thereof.\n\nA tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)\nor ...`` etc.",
    },

    issubclass: {
        $meth: Sk.builtin.issubclass,
        $flags: { MinArgs: 2, MaxArgs: 2 },
        $textsig: "($module, cls, class_or_tuple, /)",
        $doc:
            "Return whether 'cls' is a derived from another class or is the same class.\n\nA tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``issubclass(x, A) or issubclass(x, B)\nor ...`` etc.",
    },

    iter: {
        $meth: Sk.builtin.iter,
        $flags: { MinArgs: 1, MaxArgs: 2 },
        $textsig: "($module, iterable /)",
        $doc:
            "iter(iterable) -> iterator\niter(callable, sentinel) -> iterator\n\nGet an iterator from an object.  In the first form, the argument must\nsupply its own iterator, or be a sequence.\nIn the second form, the callable is called until it returns the sentinel.",
    },

    len: {
        $meth: Sk.builtin.len,
        $flags: { OneArg: true },
        $textsig: "($module, obj, /)",
        $doc: "Return the number of items in a container.",
    },

    locals: {
        $meth: Sk.builtin.locals,
        $flags: { NoArgs: true },
        $textsig: "($module, /)",
        $doc:
            "Return a dictionary containing the current scope's local variables.\n\nNOTE: Whether or not updates to this dictionary will affect name lookups in\nthe local scope and vice-versa is *implementation dependent* and not\ncovered by any backwards compatibility guarantees.",
    },

    max: {
        $meth: Sk.builtin.max,
        $flags: { FastCall: true },
        $textsig: null,
        $doc:
            "max(iterable, *[, default=obj, key=func]) -> value\nmax(arg1, arg2, *args, *[, key=func]) -> value\n\nWith a single iterable argument, return its biggest item. The\ndefault keyword-only argument specifies an object to return if\nthe provided iterable is empty.\nWith two or more arguments, return the largest argument.",
    },

    min: {
        $meth: Sk.builtin.min,
        $flags: { FastCall: true },
        $textsig: null,
        $doc:
            "min(iterable, *[, default=obj, key=func]) -> value\nmin(arg1, arg2, *args, *[, key=func]) -> value\n\nWith a single iterable argument, return its smallest item. The\ndefault keyword-only argument specifies an object to return if\nthe provided iterable is empty.\nWith two or more arguments, return the smallest argument.",
    },

    next: {
        $name: "next",
        $meth: Sk.builtin.next_,
        $flags: { MinArgs: 1, MaxArgs: 2 },
        $textsig: null,
        $doc:
            "next(iterator[, default])\n\nReturn the next item from the iterator. If default is given and the iterator\nis exhausted, it is returned instead of raising StopIteration.",
    },

    oct: {
        $meth: Sk.builtin.oct,
        $flags: { OneArg: true },
        $textsig: "($module, number, /)",
        $doc: "Return the octal representation of an integer.\n\n   >>> oct(342391)\n   '0o1234567'",
    },

    open: {
        $meth: Sk.builtin.open,
        $flags: {
            MinArgs: 1,
            MaxArgs: 3,
            //NamedArgs: ["file, mode, buffering, encoding, errors, newline, closefd, opener"],
            //Defaults: [new Sk.builtin.str("r"), new Sk.builtin.int_(-1), Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.true$, Sk.builtin.none.none$]
        },
        $textsig: null,
        // $textsig: "($module, /, file, mode='r', buffering=-1, encoding=None,\n     errors=None, newline=None, closefd=True, opener=None)",
        // this is the python 2 documentation since we don't support the py3 version
        $doc:
            "open(name[, mode[, buffering]]) -> file object\n\nOpen a file using the file() type, returns a file object.  This is the\npreferred way to open a file.  See file.__doc__ for further information.",
    },

    ord: {
        $meth: Sk.builtin.ord,
        $flags: { OneArg: true },
        $textsig: "($module, c, /)",
        $doc: "Return the Unicode code point for a one-character string.",
    },

    pow: {
        $meth: Sk.builtin.pow,
        $flags: { MinArgs: 2, MaxArgs: 3 },
        $textsig: "($module, x, y, z=None, /)",
        $doc:
            "Equivalent to x**y (with two arguments) or x**y % z (with three arguments)\n\nSome types, such as ints, are able to use a more efficient algorithm when\ninvoked using the three argument form.",
    },

    print: {
        $meth: Sk.builtin.print,
        $flags: { FastCall: true },
        $textsig: null,
        $doc:
            "print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\nPrints the values to a stream, or to sys.stdout by default.\nOptional keyword arguments:\nfile:  a file-like object (stream); defaults to the current sys.stdout.\nsep:   string inserted between values, default a space.\nend:   string appended after the last value, default a newline.\nflush: whether to forcibly flush the stream.",
    },

    repr: {
        $meth: Sk.builtin.repr,
        $flags: { OneArg: true },
        $textsig: "($module, obj, /)",
        $doc: "Return the canonical string representation of the object.\n\nFor many object types, including most builtins, eval(repr(obj)) == obj.",
    },

    round: {
        $meth: Sk.builtin.round,
        $flags: {
            NamedArgs: ["number", "ndigits"],
        },
        $textsig: "($module, /, number, ndigits=None)",
        $doc:
            "Round a number to a given precision in decimal digits.\n\nThe return value is an integer if ndigits is omitted or None.  Otherwise\nthe return value has the same type as the number.  ndigits may be negative.",
    },

    setattr: {
        $meth: Sk.builtin.setattr,
        $flags: { MinArgs: 3, MaxArgs: 3 },
        $textsig: "($module, obj, name, value, /)",
        $doc: "Sets the named attribute on the given object to the specified value.\n\nsetattr(x, 'y', v) is equivalent to ``x.y = v''",
    },

    sorted: {
        $meth: Sk.builtin.sorted,
        $flags: {
            NamedArgs: [null, "cmp", "key", "reverse"],
            Defaults: [Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.false$],
        }, // should be fast call leave for now
        $textsig: "($module, iterable, /, *, key=None, reverse=False)",
        $doc:
            "Return a new list containing all items from the iterable in ascending order.\n\nA custom key function can be supplied to customize the sort order, and the\nreverse flag can be set to request the result in descending order.",
    },

    sum: {
        $meth: Sk.builtin.sum,
        $flags: {
            NamedArgs: [null, "start"],
            Defaults: [new Sk.builtin.int_(0)],
        },
        $textsig: "($module, iterable, /, start=0)", //changed in python 3.8 start
        $doc:
            "Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n\nWhen the iterable is empty, return the start value.\nThis function is intended specifically for use with numeric values and may\nreject non-numeric types.",
    },

    vars: {
        $meth: Sk.builtin.vars,
        $flags: { MinArgs: 0, MaxArgs: 1 },
        $textsig: null,
        $doc: "vars([object]) -> dictionary\n\nWithout arguments, equivalent to locals().\nWith an argument, equivalent to object.__dict__.",
    },
});


Sk.setupObjects = function (py3) {
    if (py3) {
        Sk.builtins["filter"] = Sk.builtin.filter_;
        Sk.builtins["map"] = Sk.builtin.map_;
        Sk.builtins["zip"] = Sk.builtin.zip_;
        Sk.builtins["range"] = Sk.builtin.range_;
        delete Sk.builtins["xrange"];
        delete Sk.builtins["StandardError"];
        delete Sk.builtins["unicode"];
        delete Sk.builtins["basestring"];
        delete Sk.builtins["long_$rw$"];
        Sk.builtin.int_.prototype.$r = function () {
            return new Sk.builtin.str(this.v.toString());
        };
        delete Sk.builtin.int_.prototype.tp$str;
        delete Sk.builtin.bool.prototype.tp$str;
        delete Sk.builtins["raw_input"];
        delete Sk.builtin.str.prototype.decode;
        Sk.builtins["bytes"] = Sk.builtin.bytes;
        Sk.builtins["ascii"] = new Sk.builtin.sk_method(
            {
                $meth: Sk.builtin.ascii,
                $flags: { OneArg: true },
                $textsig: "($module, obj, /)",
                $doc:
                    "Return an ASCII-only representation of an object.\n\nAs repr(), return a string containing a printable representation of an\nobject, but escape the non-ASCII characters in the string returned by\nrepr() using \\\\x, \\\\u or \\\\U escapes. This generates a string similar\nto that returned by repr() in Python 2.",
            },
            null,
            "builtins"
        );
    } else {
        Sk.builtins["range"] = new Sk.builtin.sk_method(
            {
                $meth: Sk.builtin.range,
                $name: "range",
                $flags: { MinArgs: 1, MaxArgs: 3 },
            },
            undefined,
            "builtins"
        );
        Sk.builtins["xrange"] = new Sk.builtin.sk_method(
            {
                $meth: Sk.builtin.xrange,
                $name: "xrange",
                $flags: { MinArgs: 1, MaxArgs: 3 },
            },
            null,
            "builtins"
        );
        Sk.builtins["filter"] = new Sk.builtin.func(Sk.builtin.filter);
        Sk.builtins["map"] = new Sk.builtin.func(Sk.builtin.map);
        Sk.builtins["zip"] = new Sk.builtin.func(Sk.builtin.zip);
        Sk.builtins["StandardError"] = Sk.builtin.Exception;
        Sk.builtins["unicode"] = Sk.builtin.str;
        Sk.builtins["basestring"] = Sk.builtin.str;
        Sk.builtins["long_$rw$"] = Sk.builtin.lng;
        Sk.builtin.int_.prototype.$r = function () {
            const v = this.v;
            if (typeof v === "number") {
                return new Sk.builtin.str(v.toString());
            } else {
                return new Sk.builtin.str(v.toString() + "L");
            }
        };
        Sk.builtin.int_.prototype.tp$str = function () {
            return new Sk.builtin.str(this.v.toString());
        };
        Sk.builtin.bool.prototype.tp$str = function () {
            return this.$r();
        };
        Sk.builtins["raw_input"] = new Sk.builtin.func(Sk.builtin.raw_input);
        Sk.builtin.str.prototype.decode = Sk.builtin.str.$py2decode;
        delete Sk.builtins["bytes"];
        delete Sk.builtins["ascii"];
    }
};

Sk.exportSymbol("Sk.setupObjects", Sk.setupObjects);
Sk.exportSymbol("Sk.builtins", Sk.builtins);


/***/ }),

/***/ "./src/bytes.js":
/*!**********************!*\
  !*** ./src/bytes.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(/*! fastestsmallesttextencoderdecoder */ "./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js");

// Mapping from supported valid encodings to normalized encoding name
const supportedEncodings = {
    utf: "utf-8",
    utf8: "utf-8",
    utf_8: "utf-8",
    ascii: "ascii",
};

var space_reg = /\s+/g;
var underscore_hyphen_reg = /[_-]+/g;
function normalizeEncoding(encoding) {
    const normalized = encoding.replace(space_reg, "").replace(underscore_hyphen_reg, "_").toLowerCase();
    const supported = supportedEncodings[normalized];
    if (supported === undefined) {
        return encoding;
    } else {
        return supported;
    }
}
const Encoder = new TextEncoder();
const Decoder = new TextDecoder();

/**
 * @constructor
 * @param {undefined|Uint8Array|Array|number|string} source Using constructor with new should be a js object
 * @return {Sk.builtin.bytes}
 * @extends {Sk.builtin.object}
 */
Sk.builtin.bytes = Sk.abstr.buildNativeClass("bytes", {
    constructor: function bytes(source) {
        if (!(this instanceof Sk.builtin.bytes)) {
            throw new TypeError("bytes is a constructor use 'new'");
        }
        // deal with internal calls
        if (source === undefined) {
            this.v = new Uint8Array();
        } else if (source instanceof Uint8Array) {
            this.v = source;
        } else if (Array.isArray(source)) {
            Sk.asserts.assert(
                source.every((x) => x >= 0 && x <= 0xff),
                "bad internal call to bytes with array"
            );
            this.v = new Uint8Array(source);
        } else if (typeof source === "string") {
            // fast path must be binary string https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary
            // i.e. the reverse of this.$jsstr();
            let cc;
            const uint8 = new Uint8Array(source.length);
            const len = source.length;
            for (let i = 0; i < len; i++) {
                cc = source.charCodeAt(i);
                if (cc > 0xff) {
                    throw new Sk.builtin.UnicodeDecodeError("invalid string at index " + i + " (possibly contains a unicode character)");
                }
                uint8[i] = cc;
            }
            this.v = uint8;
        } else if (typeof source === "number") {
            this.v = new Uint8Array(source);
        } else {
            throw new Sk.builtin.TypeError("bad argument to bytes constructor");
        }
    },
    slots: /**@lends {Sk.builtin.bytes.prototype} */ {
        tp$getattr: Sk.generic.getAttr,
        tp$doc:
            "bytes(iterable_of_ints) -> bytes\nbytes(string, encoding[, errors]) -> bytes\nbytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\nbytes(int) -> bytes object of size given by the parameter initialized with null bytes\nbytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n  - an iterable yielding integers in range(256)\n  - a text string encoded using the specified encoding\n  - any object implementing the buffer API.\n  - an integer",
        tp$new(args, kwargs) {
            if (this !== Sk.builtin.bytes.prototype) {
                return this.$subtype_new(args, kwargs);
            }
            kwargs = kwargs || [];
            let source, pySource, dunderBytes, encoding, errors;
            if (args.length <= 1 && +kwargs.length === 0) {
                pySource = args[0];
            } else {
                [pySource, encoding, errors] = Sk.abstr.copyKeywordsToNamedArgs("bytes", [null, "pySource", "errors"], args, kwargs);
                ({ encoding, errors } = checkGetEncodingErrors("bytes", encoding, errors));
                if (!Sk.builtin.checkString(pySource)) {
                    throw new Sk.builtin.TypeError("encoding or errors without a string argument");
                }
                return strEncode(pySource, encoding, errors);
            }

            if (pySource === undefined) {
                return new Sk.builtin.bytes();
            } else if ((dunderBytes = Sk.abstr.lookupSpecial(pySource, Sk.builtin.str.$bytes)) !== undefined) {
                const ret = Sk.misceval.callsimOrSuspendArray(dunderBytes, []);
                return Sk.misceval.chain(ret, (bytesSource) => {
                    if (!Sk.builtin.checkBytes(bytesSource)) {
                        throw new Sk.builtin.TypeError("__bytes__ returned non-bytes (type " + Sk.abstr.typeName(bytesSource) + ")");
                    }
                    return bytesSource;
                });
            } else if (Sk.misceval.isIndex(pySource)) {
                source = Sk.misceval.asIndexSized(pySource, Sk.builtin.OverflowError);
                if (source < 0) {
                    throw new Sk.builtin.ValueError("negative count");
                }
                return new Sk.builtin.bytes(source);
            } else if (Sk.builtin.checkBytes(pySource)) {
                return new Sk.builtin.bytes(pySource.v);
            } else if (Sk.builtin.checkString(pySource)) {
                throw new Sk.builtin.TypeError("string argument without an encoding");
            } else if (Sk.builtin.checkIterable(pySource)) {
                let source = [];
                let r = Sk.misceval.iterFor(Sk.abstr.iter(pySource), (byte) => {
                    const n = Sk.misceval.asIndexSized(byte);
                    if (n < 0 || n > 255) {
                        throw new Sk.builtin.ValueError("bytes must be in range(0, 256)");
                    }
                    source.push(n);
                });
                return Sk.misceval.chain(r, () => new Sk.builtin.bytes(source));
            }
            throw new Sk.builtin.TypeError("cannot convert '" + Sk.abstr.typeName(source) + "' object into bytes");
        },
        $r() {
            let num;
            let quote = "'";
            const hasdbl = this.v.indexOf(34) !== -1;
            let ret = "";

            for (let i = 0; i < this.v.length; i++) {
                num = this.v[i];
                if (num < 9 || (num > 10 && num < 13) || (num > 13 && num < 32) || num > 126) {
                    ret += makehexform(num);
                } else if (num === 9 || num === 10 || num === 13 || num === 39 || num === 92) {
                    switch (num) {
                        case 9:
                            ret += "\\t";
                            break;
                        case 10:
                            ret += "\\n";
                            break;
                        case 13:
                            ret += "\\r";
                            break;
                        case 39:
                            if (hasdbl) {
                                ret += "\\'";
                            } else {
                                ret += "'";
                                quote = '"';
                            }
                            break;
                        case 92:
                            ret += "\\\\";
                            break;
                    }
                } else {
                    ret += String.fromCharCode(num);
                }
            }
            ret = "b" + quote + ret + quote;
            return new Sk.builtin.str(ret);
        },
        tp$str() {
            return this.$r();
        },
        tp$iter() {
            return new bytes_iter_(this);
        },
        tp$richcompare(other, op) {
            if (this === other && Sk.misceval.opAllowsEquality(op)) {
                return true;
            } else if (!(other instanceof Sk.builtin.bytes)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            const v = this.v;
            const w = other.v;
            if (v.length !== w.length && (op === "Eq" || op === "NotEq")) {
                /* Shortcut: if the lengths differ, the bytes differ */
                return op === "Eq" ? false : true;
            }
            let i;
            const min_len = Math.min(v.length, w.length);
            for (i = 0; i < min_len; i++) {
                if (v[i] !== w[i]) {
                    break; // we've found a different element
                }
            }
            switch (op) {
                case "Lt":
                    return (i === min_len && v.length < w.length) || v[i] < w[i];
                case "LtE":
                    return (i === min_len && v.length <= w.length) || v[i] <= w[i];
                case "Eq":
                    return i === min_len;
                case "NotEq":
                    return i < min_len;
                case "Gt":
                    return (i === min_len && v.length > w.length) || v[i] > w[i];
                case "GtE":
                    return (i === min_len && v.length >= w.length) || v[i] >= w[i];
            }
        },
        tp$hash() {
            return new Sk.builtin.str(this.$jsstr()).tp$hash();
        },
        tp$as_sequence_or_mapping: true,
        mp$subscript(index) {
            if (Sk.misceval.isIndex(index)) {
                let i = Sk.misceval.asIndexSized(index, Sk.builtin.IndexError);
                if (i !== undefined) {
                    if (i < 0) {
                        i = this.v.length + i;
                    }
                    if (i < 0 || i >= this.v.length) {
                        throw new Sk.builtin.IndexError("index out of range");
                    }
                    return new Sk.builtin.int_(this.v[i]);
                }
            } else if (index instanceof Sk.builtin.slice) {
                const ret = [];
                index.sssiter$(this.v.length, (i) => {
                    ret.push(this.v[i]);
                });
                return new Sk.builtin.bytes(new Uint8Array(ret));
            }
            throw new Sk.builtin.TypeError("byte indices must be integers or slices, not " + Sk.abstr.typeName(index));
        },
        sq$length() {
            return this.v.length;
        },
        sq$concat(other) {
            if (!(other instanceof Sk.builtin.bytes)) {
                throw new Sk.builtin.TypeError("can't concat " + Sk.abstr.typeName(other) + " to bytes");
            }
            const ret = new Uint8Array(this.v.length + other.v.length);
            let i;
            for (i = 0; i < this.v.length; i++) {
                ret[i] = this.v[i];
            }
            for (let j = 0; j < other.v.length; j++, i++) {
                ret[i] = other.v[j];
            }
            return new Sk.builtin.bytes(ret);
        },
        sq$repeat(n) {
            if (!Sk.misceval.isIndex(n)) {
                throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'");
            }
            n = Sk.misceval.asIndexSized(n, Sk.builtin.OverflowError);
            const len = n * this.v.length;
            if (len > Number.MAX_SAFE_INTEGER) {
                throw new Sk.builtin.OverflowError();
            } else if (n <= 0) {
                return new Sk.builtin.bytes();
            }
            const ret = new Uint8Array(len);
            let j = 0;
            while (j < len) {
                for (let i = 0; i < this.v.length; i++) {
                    ret[j++] = this.v[i];
                }
            }
            return new Sk.builtin.bytes(ret);
        },
        sq$contains(tgt) {
            return this.find$left(tgt) !== -1;
        },
        tp$as_number: true,
        nb$remainder: Sk.builtin.str.prototype.nb$remainder,
    },
    proto: {
        $jsstr() {
            // returns binary string - not bidirectional for non ascii characters - use with caution
            // i.e. new Sk.builtin.bytes(x.$jsstr()).v  may be different to x.v;
            let ret = "";
            for (let i = 0; i < this.v.length; i++) {
                ret += String.fromCharCode(this.v[i]);
            }
            return ret;
        },
        get$tgt(tgt) {
            if (tgt instanceof Sk.builtin.bytes) {
                return tgt.v;
            }
            tgt = Sk.misceval.asIndexOrThrow(tgt, "argument should be integer or bytes-like object, not {tp$name}");
            if (tgt < 0 || tgt > 0xff) {
                throw new Sk.builtin.ValueError("bytes must be in range(0, 256)");
            }
            return tgt;
        },
        get$raw(tgt) {
            if (tgt instanceof Sk.builtin.bytes) {
                return tgt.v;
            }
            throw new Sk.builtin.TypeError("a bytes-like object is required, not '" + Sk.abstr.typeName(tgt) + "'");
        },
        get$splitArgs: checkSepMaxSplit,
        find$left: mkFind(false),
        find$right: mkFind(true),
        find$subleft: function findSubLeft(uint8, start, end) {
            end = end - uint8.length + 1;
            let i = start;
            while (i < end) {
                if (uint8.every((val, j) => val === this.v[i + j])) {
                    return i;
                }
                i++;
            }
            return -1;
        },
        find$subright(uint8, start, end) {
            let i = end - uint8.length;
            while (i >= start) {
                if (uint8.every((val, j) => val === this.v[i + j])) {
                    return i;
                }
                i--;
            }
            return -1;
        },
        $subtype_new(args, kwargs) {
            const instance = new this.constructor();
            // we call bytes new method with all the args and kwargs
            const bytes_instance = Sk.builtin.bytes.prototype.tp$new(args, kwargs);
            instance.v = bytes_instance.v;
            return instance;
        },
        sk$asarray() {
            const ret = [];
            this.v.forEach((x) => {ret.push(new Sk.builtin.int_(x));});
            return ret;
        }
    },
    flags: {
        str$encode: strEncode,
        $decode: bytesDecode,
        check$encodeArgs: checkGetEncodingErrors,
    },
    methods: {
        __getnewargs__: {
            $meth() {
                return new Sk.builtin.tuple(new Sk.builtin.bytes(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: null,
        },
        capitalize: {
            $meth() {
                const len = this.v.length;
                if (len === 0) {
                    return new Sk.builtin.bytes(this.v);
                }
                const final = new Uint8Array(len);
                let val = this.v[0];
                final[0] = islower(val) ? val - 32 : val;
                for (let i = 1; i < len; i++) {
                    val = this.v[i];
                    final[i] = isupper(val) ? val + 32 : val;
                }
                return new Sk.builtin.bytes(final);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "B.capitalize() -> copy of B\n\nReturn a copy of B with only its first character capitalized (ASCII)\nand the rest lower-cased.",
        },
        center: {
            $meth: mkJust("center", false, true),
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: null,
            $doc:
                "B.center(width[, fillchar]) -> copy of B\n\nReturn B centered in a string of length width.  Padding is\ndone using the specified fill character (default is a space).",
        },
        count: {
            $meth(tgt, start, end) {
                tgt = this.get$tgt(tgt);
                ({ start, end } = Sk.builtin.slice.startEnd$wrt(this, start, end));
                let count = 0;
                if (typeof tgt === "number") {
                    for (let i = start; i < end; i++) {
                        if (this.v[i] === tgt) {
                            count++;
                        }
                    }
                } else {
                    const upto = end - tgt.length + 1;
                    for (let i = start; i < upto; i++) {
                        if (tgt.every((val, j) => val === this.v[i + j])) {
                            count++;
                            i += tgt.length - 1;
                        }
                    }
                }
                return new Sk.builtin.int_(count);
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "B.count(sub[, start[, end]]) -> int\n\nReturn the number of non-overlapping occurrences of subsection sub in\nbytes B[start:end].  Optional arguments start and end are interpreted\nas in slice notation.",
        },
        decode: {
            $meth: bytesDecode,
            $flags: { NamedArgs: ["encoding", "errors"] },
            $textsig: "($self, /, encoding='utf-8', errors='strict')",
            $doc:
                "Decode the bytes using the codec registered for encoding.\n\n  encoding\n    The encoding with which to decode the bytes.\n  errors\n    The error handling scheme to use for the handling of decoding errors.\n    The default is 'strict' meaning that decoding errors raise a\n    UnicodeDecodeError. Other possible values are 'ignore' and 'replace'\n    as well as any other name registered with codecs.register_error that\n    can handle UnicodeDecodeErrors.",
        },
        endswith: {
            $meth: mkStartsEndsWith("endswith", (subarray, tgt) => {
                const start = subarray.length - tgt.length;
                return start >= 0 && tgt.every((val, i) => val === subarray[start + i]);
            }),
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "B.endswith(suffix[, start[, end]]) -> bool\n\nReturn True if B ends with the specified suffix, False otherwise.\nWith optional start, test B beginning at that position.\nWith optional end, stop comparing B at that position.\nsuffix can also be a tuple of bytes to try.",
        },
        expandtabs: {
            $meth(tabsize) {
                tabsize = Sk.misceval.asIndexSized(tabsize, Sk.builtin.OverflowError, "an integer is required (got type {tp$nam})");
                const final = [];
                let linepos = 0;
                for (let i = 0; i < this.v.length; i++) {
                    const val = this.v[i];
                    if (val === 9) {
                        const inc = tabsize - (linepos % tabsize);
                        final.push(...new Array(inc).fill(32));
                        linepos += inc;
                    } else if (val === 10 || val === 13) {
                        final.push(val);
                        linepos = 0;
                    } else {
                        final.push(val);
                        linepos++;
                    }
                }
                return new Sk.builtin.bytes(new Uint8Array(final));
            },
            $flags: { NamedArgs: ["tabsize"], Defaults: [8] },
            $textsig: null,
            $doc:
                "B.expandtabs(tabsize=8) -> copy of B\n\nReturn a copy of B where all tab characters are expanded using spaces.\nIf tabsize is not given, a tab size of 8 characters is assumed.",
        },
        find: {
            $meth: function find(tgt, start, end) {
                return new Sk.builtin.int_(this.find$left(tgt, start, end));
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "B.find(sub[, start[, end]]) -> int\n\nReturn the lowest index in B where subsection sub is found,\nsuch that sub is contained within B[start,end].  Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.",
        },
        hex: {
            $meth() {
                let final = "";
                for (let i = 0; i < this.v.length; i++) {
                    final += this.v[i].toString(16).padStart(2, "0");
                }
                return new Sk.builtin.str(final);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "B.hex() -> string\n\nCreate a string of hexadecimal numbers from a bytes object.\nExample: b'\\xb9\\x01\\xef'.hex() -> 'b901ef'.",
        },
        index: {
            $meth: function index(tgt, start, end) {
                const val = this.find$left(tgt, start, end);
                if (val === -1) {
                    throw new Sk.builtin.ValueError("subsection not found");
                } else {
                    return new Sk.builtin.int_(val);
                }
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "B.index(sub[, start[, end]]) -> int\n\nReturn the lowest index in B where subsection sub is found,\nsuch that sub is contained within B[start,end].  Optional\narguments start and end are interpreted as in slice notation.\n\nRaises ValueError when the subsection is not found.",
        },
        isalnum: {
            $meth: mkIsAll((val) => isdigit(val) || islower(val) || isupper(val)),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc:
                "B.isalnum() -> bool\n\nReturn True if all characters in B are alphanumeric\nand there is at least one character in B, False otherwise.",
        },
        isalpha: {
            $meth: mkIsAll((val) => (val >= 65 && val <= 90) || (val >= 97 && val <= 122)),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc:
                "B.isalpha() -> bool\n\nReturn True if all characters in B are alphabetic\nand there is at least one character in B, False otherwise.",
        },
        isascii: {
            $meth: mkIsAll((val) => val >= 0 && val <= 0x7f, true),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "B.isascii() -> bool\n\nReturn True if B is empty or all characters in B are ASCII,\nFalse otherwise.",
        },
        isdigit: {
            $meth: mkIsAll(isdigit),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "B.isdigit() -> bool\n\nReturn True if all characters in B are digits\nand there is at least one character in B, False otherwise.",
        },
        islower: {
            $meth: makeIsUpperLower(islower, isupper),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc:
                "B.islower() -> bool\n\nReturn True if all cased characters in B are lowercase and there is\nat least one cased character in B, False otherwise.",
        },
        isspace: {
            $meth: mkIsAll(isspace),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc:
                "B.isspace() -> bool\n\nReturn True if all characters in B are whitespace\nand there is at least one character in B, False otherwise.",
        },
        istitle: {
            $meth: function istitle() {
                let inword = false;
                let cased = false;
                for (let i = 0; i < this.v.length; i++) {
                    const val = this.v[i];
                    if (isupper(val)) {
                        if (inword) {
                            return Sk.builtin.bool.false$;
                        }
                        inword = true;
                        cased = true;
                    } else if (islower(val)) {
                        if (!inword) {
                            return Sk.builtin.bool.false$;
                        }
                        cased = true;
                    } else {
                        inword = false;
                    }
                }
                return cased ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$;
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc:
                "B.istitle() -> bool\n\nReturn True if B is a titlecased string and there is at least one\ncharacter in B, i.e. uppercase characters may only follow uncased\ncharacters and lowercase characters only cased ones. Return False\notherwise.",
        },
        isupper: {
            $meth: makeIsUpperLower(isupper, islower),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc:
                "B.isupper() -> bool\n\nReturn True if all cased characters in B are uppercase and there is\nat least one cased character in B, False otherwise.",
        },
        join: {
            $meth(iterable) {
                const final = [];
                let i = 0;
                return Sk.misceval.chain(
                    Sk.misceval.iterFor(Sk.abstr.iter(iterable), (item) => {
                        if (!(item instanceof Sk.builtin.bytes)) {
                            throw new Sk.builtin.TypeError(
                                "sequence item " + i + ": expected a bytes-like object, " + Sk.abstr.typeName(item) + " found"
                            );
                        }
                        i++;
                        if (final.length) {
                            final.push(...this.v);
                        }
                        final.push(...item.v);
                    }),
                    () => new Sk.builtin.bytes(new Uint8Array(final))
                );
            },
            $flags: { OneArg: true },
            $textsig: "($self, iterable_of_bytes, /)",
            $doc:
                "Concatenate any number of bytes objects.\n\nThe bytes whose method is called is inserted in between each pair.\n\nThe result is returned as a new bytes object.\n\nExample: b'.'.join([b'ab', b'pq', b'rs']) -> b'ab.pq.rs'.",
        },
        ljust: {
            $meth: mkJust("ljust", false, false),
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: null,
            $doc:
                "B.ljust(width[, fillchar]) -> copy of B\n\nReturn B left justified in a string of length width. Padding is\ndone using the specified fill character (default is a space).",
        },
        lower: {
            $meth: mkCaseSwitch((val) => (isupper(val) ? val + 32 : val)),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "B.lower() -> copy of B\n\nReturn a copy of B with all ASCII characters converted to lowercase.",
        },
        lstrip: {
            $meth: mkStrip(true, false),
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, bytes=None, /)",
            $doc: "Strip leading bytes contained in the argument.\n\nIf the argument is omitted or None, strip leading  ASCII whitespace.",
        },
        partition: {
            $meth: mkPartition(false),
            $flags: { OneArg: true },
            $textsig: "($self, sep, /)",
            $doc:
                "Partition the bytes into three parts using the given separator.\n\nThis will search for the separator sep in the bytes. If the separator is found,\nreturns a 3-tuple containing the part before the separator, the separator\nitself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing the original bytes\nobject and two empty bytes objects.",
        },
        replace: {
            $meth(oldB, newB, count) {
                oldB = this.get$raw(oldB);
                newB = this.get$raw(newB);
                count = count === undefined ? -1 : Sk.misceval.asIndexSized(count, Sk.builtin.OverflowError);
                count = count < 0 ? Infinity : count;
                const final = [];
                let found = 0,
                    i = 0;
                while (i < this.v.length && found < count) {
                    const next = this.find$subleft(oldB, i, this.v.length);
                    if (next === -1) {
                        break;
                    }
                    for (let j = i; j < next; j++) {
                        final.push(this.v[j]);
                    }
                    final.push(...newB);
                    i = next + oldB.length;
                    found++;
                }
                for (i; i < this.v.length; i++) {
                    final.push(this.v[i]);
                }
                return new Sk.builtin.bytes(new Uint8Array(final));
            },
            $flags: { MinArgs: 2, MaxArgs: 3 },
            $textsig: "($self, old, new, count=-1, /)",
            $doc:
                "Return a copy with all occurrences of substring old replaced by new.\n\n  count\n    Maximum number of occurrences to replace.\n    -1 (the default value) means replace all occurrences.\n\nIf the optional argument count is given, only the first count occurrences are\nreplaced.",
        },
        rfind: {
            $meth(tgt, start, end) {
                return new Sk.builtin.int_(this.find$right(tgt, start, end));
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "B.rfind(sub[, start[, end]]) -> int\n\nReturn the highest index in B where subsection sub is found,\nsuch that sub is contained within B[start,end].  Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.",
        },
        rindex: {
            $meth: function rindex(tgt, start, end) {
                const val = this.find$right(tgt, start, end);
                if (val === -1) {
                    throw new Sk.builtin.ValueError("subsection not found");
                } else {
                    return new Sk.builtin.int_(val);
                }
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "B.rindex(sub[, start[, end]]) -> int\n\nReturn the highest index in B where subsection sub is found,\nsuch that sub is contained within B[start,end].  Optional\narguments start and end are interpreted as in slice notation.\n\nRaise ValueError when the subsection is not found.",
        },
        rjust: {
            $meth: mkJust("rjust", true, false),
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: null,
            $doc:
                "B.rjust(width[, fillchar]) -> copy of B\n\nReturn B right justified in a string of length width. Padding is\ndone using the specified fill character (default is a space)",
        },
        rpartition: {
            $meth: mkPartition(true),
            $flags: { OneArg: true },
            $textsig: "($self, sep, /)",
            $doc:
                "Partition the bytes into three parts using the given separator.\n\nThis will search for the separator sep in the bytes, starting at the end. If\nthe separator is found, returns a 3-tuple containing the part before the\nseparator, the separator itself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing two empty bytes\nobjects and the original bytes object.",
        },
        rsplit: {
            $meth: function rSplit(sep, maxsplit) {
                ({ sep, maxsplit } = this.get$splitArgs(sep, maxsplit));

                const result = [];
                let splits = 0,
                    i = this.v.length;

                if (sep !== null) {
                    while (i >= 0 && splits < maxsplit) {
                        const next = this.find$subright(sep, 0, i);
                        if (next === -1) {
                            break;
                        }
                        result.push(new Sk.builtin.bytes(this.v.subarray(next + sep.length, i)));
                        i = next;
                        splits++;
                    }
                    result.push(new Sk.builtin.bytes(this.v.subarray(0, i)));
                } else {
                    i--;
                    while (splits < maxsplit) {
                        while (isspace(this.v[i])) {
                            i--;
                        }
                        if (i < 0) {
                            break;
                        }
                        const index = i + 1;
                        i--;
                        while (i >= 0 && !isspace(this.v[i])) {
                            i--;
                        }
                        result.push(new Sk.builtin.bytes(this.v.subarray(i + 1, index)));
                        splits++;
                    }
                    if (i >= 0) {
                        while (isspace(this.v[i])) {
                            i--;
                        }
                        if (i >= 0) {
                            result.push(new Sk.builtin.bytes(this.v.subarray(0, i + 1)));
                        }
                    }
                }
                return new Sk.builtin.list(result.reverse());
            },
            $flags: { NamedArgs: ["sep", "maxsplit"], Defaults: [Sk.builtin.none.none$, -1] },
            $textsig: "($self, /, sep=None, maxsplit=-1)",
            $doc:
                "Return a list of the sections in the bytes, using sep as the delimiter.\n\n  sep\n    The delimiter according which to split the bytes.\n    None (the default value) means split on ASCII whitespace characters\n    (space, tab, return, newline, formfeed, vertical tab).\n  maxsplit\n    Maximum number of splits to do.\n    -1 (the default value) means no limit.\n\nSplitting is done starting at the end of the bytes and working to the front.",
        },
        rstrip: {
            $meth: mkStrip(false, true),
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, bytes=None, /)",
            $doc: "Strip trailing bytes contained in the argument.\n\nIf the argument is omitted or None, strip trailing ASCII whitespace.",
        },
        split: {
            $meth: function Split(sep, maxsplit) {
                ({ sep, maxsplit } = this.get$splitArgs(sep, maxsplit));

                const result = [];
                const mylen = this.v.length;
                let splits = 0,
                    i = 0;

                if (sep !== null) {
                    while (i < mylen && splits < maxsplit) {
                        const next = this.find$subleft(sep, i, mylen);
                        if (next === -1) {
                            break;
                        }
                        result.push(new Sk.builtin.bytes(this.v.subarray(i, next)));
                        i = next + sep.length;
                        splits++;
                    }
                    result.push(new Sk.builtin.bytes(this.v.subarray(i, mylen)));
                } else {
                    while (splits < maxsplit) {
                        while (isspace(this.v[i])) {
                            i++;
                        }
                        if (i === mylen) {
                            break;
                        }
                        const index = i;
                        i++;
                        while (i < mylen && !isspace(this.v[i])) {
                            i++;
                        }
                        result.push(new Sk.builtin.bytes(this.v.subarray(index, i)));
                        splits++;
                    }
                    if (i < mylen) {
                        while (isspace(this.v[i])) {
                            i++;
                        }
                        if (i < mylen) {
                            result.push(new Sk.builtin.bytes(this.v.subarray(i, mylen)));
                        }
                    }
                }
                return new Sk.builtin.list(result);
            },
            $flags: { NamedArgs: ["sep", "maxsplit"], Defaults: [Sk.builtin.none.none$, -1] },
            $textsig: "($self, /, sep=None, maxsplit=-1)",
            $doc:
                "Return a list of the sections in the bytes, using sep as the delimiter.\n\n  sep\n    The delimiter according which to split the bytes.\n    None (the default value) means split on ASCII whitespace characters\n    (space, tab, return, newline, formfeed, vertical tab).\n  maxsplit\n    Maximum number of splits to do.\n    -1 (the default value) means no limit.",
        },
        splitlines: {
            $meth(keepends) {
                keepends = Sk.misceval.isTrue(keepends);
                const final = [];
                let sol = 0;
                let eol;
                let i = 0;
                const len = this.v.length;
                while (i < len) {
                    const val = this.v[i];
                    if (val === 13) {
                        // \r
                        const rn = this.v[i + 1] === 10; // \r\n
                        if (keepends) {
                            eol = rn ? i + 2 : i + 1;
                        } else {
                            eol = i;
                        }
                        final.push(new Sk.builtin.bytes(this.v.subarray(sol, eol)));
                        i = sol = rn ? i + 2 : i + 1;
                    } else if (val === 10) {
                        // \n
                        eol = keepends ? i + 1 : i;
                        final.push(new Sk.builtin.bytes(this.v.subarray(sol, eol)));
                        i = sol = i + 1;
                    } else {
                        i++;
                    }
                }
                if (sol < len) {
                    final.push(new Sk.builtin.bytes(this.v.subarray(sol, len)));
                }
                return new Sk.builtin.list(final);
            },
            $flags: { NamedArgs: ["keepends"], Defaults: [false] },
            $textsig: "($self, /, keepends=False)",
            $doc:
                "Return a list of the lines in the bytes, breaking at line boundaries.\n\nLine breaks are not included in the resulting list unless keepends is given and\ntrue.",
        },
        startswith: {
            $meth: mkStartsEndsWith("startswith", (subarray, tgt) => tgt.length <= subarray.length && tgt.every((val, i) => val === subarray[i])),
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "B.startswith(prefix[, start[, end]]) -> bool\n\nReturn True if B starts with the specified prefix, False otherwise.\nWith optional start, test B beginning at that position.\nWith optional end, stop comparing B at that position.\nprefix can also be a tuple of bytes to try.",
        },
        strip: {
            $meth: mkStrip(true, true),
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, bytes=None, /)",
            $doc:
                "Strip leading and trailing bytes contained in the argument.\n\nIf the argument is omitted or None, strip leading and trailing ASCII whitespace.",
        },
        swapcase: {
            $meth: mkCaseSwitch((val) => (isupper(val) ? val + 32 : islower(val) ? val - 32 : val)),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "B.swapcase() -> copy of B\n\nReturn a copy of B with uppercase ASCII characters converted\nto lowercase ASCII and vice versa.",
        },
        title: {
            $meth() {
                const len = this.v.length;
                const final = new Uint8Array(len);
                let inword = false;
                for (let i = 0; i < len; i++) {
                    const val = this.v[i];
                    if (isupper(val)) {
                        final[i] = inword ? val + 32 : val;
                        inword = true;
                    } else if (islower(val)) {
                        final[i] = inword ? val : val - 32;
                        inword = true;
                    } else {
                        final[i] = val;
                        inword = false;
                    }
                }
                return new Sk.builtin.bytes(final);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc:
                "B.title() -> copy of B\n\nReturn a titlecased version of B, i.e. ASCII words start with uppercase\ncharacters, all remaining cased characters have lowercase.",
        },
        // translate: {
        //     $meth() {
        //         throw new Sk.builtin.NotImplementedError("translate() bytes method not implemented in Skulpt");
        //     },
        //     $flags: { NoArgs: true },
        //     $textsig: "($self, table, /, delete=b'')",
        //     $doc:
        //         "Return a copy with each character mapped by the given translation table.\n\n  table\n    Translation table, which must be a bytes object of length 256.\n\nAll characters occurring in the optional argument delete are removed.\nThe remaining characters are mapped through the given translation table.",
        // },
        upper: {
            $meth: mkCaseSwitch((val) => (islower(val) ? val - 32 : val)),
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "B.upper() -> copy of B\n\nReturn a copy of B with all ASCII characters converted to uppercase.",
        },
        zfill: {
            $meth(width) {
                width = Sk.misceval.asIndexSized(width, Sk.builtin.IndexError);
                const fill_len = width - this.v.length;
                if (fill_len <= 0) {
                    return new Sk.builtin.bytes(this.v);
                }
                const final = new Uint8Array(width);
                let i = 0,
                    j;
                if (this.v[0] === 43 || this.v[0] === 45) {
                    final[0] = this.v[0];
                    i++;
                }
                final.fill(48, i, i + fill_len);
                for (j = i, i = i + fill_len; i < width; i++, j++) {
                    final[i] = this.v[j];
                }
                return new Sk.builtin.bytes(final);
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc:
                "B.zfill(width) -> copy of B\n\nPad a numeric string B with zeros on the left, to fill a field\nof the specified width.  B is never truncated.",
        },
    },
    classmethods: {
        fromhex: {
            $meth: function fromhex(string) {
                if (!Sk.builtin.checkString(string)) {
                    throw new Sk.builtin.TypeError("fromhex() argument must be str, not " + Sk.abstr.typeName(string));
                }
                string = string.$jsstr();
                const spaces = /\s+/g;
                const ishex = /^[abcdefABCDEF0123456789]{2}$/;
                const final = [];
                let index = 0;
                function pushOrThrow(upto) {
                    for (let i = index; i < upto; i += 2) {
                        let s = string.substr(i, 2);
                        if (!ishex.test(s)) {
                            throw new Sk.builtin.ValueError("non-hexadecimal number found in fromhex() arg at position " + (i + 1));
                        }
                        final.push(parseInt(s, 16));
                    }
                }
                let match;
                while ((match = spaces.exec(string)) !== null) {
                    pushOrThrow(match.index);
                    index = spaces.lastIndex;
                }
                pushOrThrow(string.length);
                return new this(final);
            },
            $flags: { OneArg: true },
            $textsig: "($type, string, /)",
            $doc:
                "Create a bytes object from a string of hexadecimal numbers.\n\nSpaces between two numbers are accepted.\nExample: bytes.fromhex('B9 01EF') -> b'\\\\xb9\\\\x01\\\\xef'.",
        },
    },
});

function checkGetEncodingErrors(funcname, encoding, errors) {
    // check the types of encoding and errors
    if (encoding === undefined) {
        encoding = "utf-8";
    } else if (!Sk.builtin.checkString(encoding)) {
        throw new Sk.builtin.TypeError(
            funcname + "() argument " + ("bytesstr".includes(funcname) ? 2 : 1) + " must be str not " + Sk.abstr.typeName(encoding)
        );
    } else {
        encoding = encoding.$jsstr();
    }
    if (errors === undefined) {
        errors = "strict";
    } else if (!Sk.builtin.checkString(errors)) {
        throw new Sk.builtin.TypeError(
            funcname + "() argument " + ("bytesstr".includes(funcname) ? 3 : 2) + " must be str not " + Sk.abstr.typeName(errors)
        );
    } else {
        errors = errors.$jsstr();
    }
    return { encoding: encoding, errors: errors };
}

function strEncode(pyStr, encoding, errors) {
    const source = pyStr.$jsstr();
    encoding = normalizeEncoding(encoding);
    if (!(errors === "strict" || errors === "ignore" || errors === "replace")) {
        throw new Sk.builtin.NotImplementedError("'" + errors + "' error handling not implemented in Skulpt");
    }
    let uint8;
    if (encoding === "ascii") {
        uint8 = encodeAscii(source, errors);
    } else if (encoding === "utf-8") {
        uint8 = Encoder.encode(source);
    } else {
        throw new Sk.builtin.LookupError("unknown encoding: " + encoding);
    }
    return new Sk.builtin.bytes(uint8);
}

function encodeAscii(source, errors) {
    const data = [];
    for (let i in source) {
        const val = source.charCodeAt(i);
        if (val > 0x7f) {
            if (errors === "strict") {
                const hexval = makehexform(val);
                throw new Sk.builtin.UnicodeEncodeError(
                    "'ascii' codec can't encode character '" + hexval + "' in position " + i + ": ordinal not in range(128)"
                );
            } else if (errors === "replace") {
                data.push(63); // "?"
            }
        } else {
            data.push(val);
        }
    }
    return new Uint8Array(data);
}

function makehexform(num) {
    var leading;
    if (num <= 265) {
        leading = "\\x";
    } else {
        leading = "\\u";
    }
    num = num.toString(16);
    if (num.length === 3) {
        num = num.slice(1, 3);
    }
    if (num.length === 1) {
        num = leading + "0" + num;
    } else {
        num = leading + num;
    }
    return num;
}

function decodeAscii(source, errors) {
    let final = "";
    for (let i = 0; i < source.length; i++) {
        const val = source[i];
        if (val > 0x7f) {
            if (errors === "strict") {
                throw new Sk.builtin.UnicodeDecodeError(
                    "'ascii' codec can't decode byte 0x" + val.toString(16) + " in position " + i + ": ordinal not in range(128)"
                );
            } else if (errors === "replace") {
                final += String.fromCharCode(65533);
            }
        } else {
            final += String.fromCharCode(val);
        }
    }
    return final;
}

function decodeUtf(source, errors) {
    const string = Decoder.decode(source);
    if (errors === "replace") {
        return string;
    } else if (errors === "strict") {
        const i = string.indexOf("�");
        if (i === -1) {
            return string;
        }
        throw new Sk.builtin.UnicodeDecodeError(
            "'utf-8' codec can't decode byte 0x" + source[i].toString(16) + " in position " + i + ": invalid start byte"
        );
    }
    return string.replace(/�/g, "");
}

function bytesDecode(encoding, errors) {
    ({ encoding, errors } = checkGetEncodingErrors("decode", encoding, errors));
    encoding = normalizeEncoding(encoding);

    if (!(errors === "strict" || errors === "ignore" || errors === "replace")) {
        throw new Sk.builtin.NotImplementedError("'" + errors + "' error handling not implemented in Skulpt");
    }

    let jsstr;
    if (encoding === "ascii") {
        jsstr = decodeAscii(this.v, errors);
    } else if (encoding === "utf-8") {
        jsstr = decodeUtf(this.v, errors);
    } else {
        throw new Sk.builtin.LookupError("unknown encoding: " + encoding);
    }
    return new Sk.builtin.str(jsstr);
}

function mkStartsEndsWith(funcname, is_match) {
    return function (prefix, start, end) {
        if (!(prefix instanceof Sk.builtin.bytes || prefix instanceof Sk.builtin.tuple)) {
            throw new Sk.builtin.TypeError(funcname + " first arg must be bytes or a tuple of bytes, not " + Sk.abstr.typeName(prefix));
        }
        ({ start, end } = Sk.builtin.slice.startEnd$wrt(this, start, end));
        if (end < start) {
            return Sk.builtin.bool.false$;
        }
        const slice = this.v.subarray(start, end);

        if (prefix instanceof Sk.builtin.tuple) {
            for (let iter = Sk.abstr.iter(prefix), item = iter.tp$iternext(); item !== undefined; item = iter.tp$iternext()) {
                item = this.get$raw(item);
                if (is_match(slice, item)) {
                    return Sk.builtin.bool.true$;
                }
            }
            return Sk.builtin.bool.false$;
        } else {
            return is_match(slice, prefix.v) ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$;
        }
    };
}

function mkFind(isReversed) {
    return function find(tgt, start, end) {
        tgt = this.get$tgt(tgt);
        ({ start, end } = Sk.builtin.slice.startEnd$wrt(this, start, end));
        if (end < start) {
            return -1;
        }
        let idx;
        if (typeof tgt === "number") {
            idx = isReversed ? this.v.lastIndexOf(tgt, end - 1) : this.v.indexOf(tgt, start);
            return idx >= start && idx < end ? idx : -1;
        }
        if (isReversed) {
            return this.find$subright(tgt, start, end);
        } else {
            return this.find$subleft(tgt, start, end);
        }
    };
}

function mkPartition(isReversed) {
    return function partition(sep) {
        sep = this.get$raw(sep);
        let pos;
        if (isReversed) {
            pos = this.find$subright(sep, 0, this.v.length);
            if (pos < 0) {
                return new Sk.builtin.tuple([new Sk.builtin.bytes(), new Sk.builtin.bytes(), this]);
            }
        } else {
            pos = this.find$subleft(sep, 0, this.v.length);
            if (pos < 0) {
                return new Sk.builtin.tuple([this, new Sk.builtin.bytes(), new Sk.builtin.bytes()]);
            }
        }
        return new Sk.builtin.tuple([
            new Sk.builtin.bytes(this.v.subarray(0, pos)),
            new Sk.builtin.bytes(sep),
            new Sk.builtin.bytes(this.v.subarray(pos + sep.length)),
        ]);
    };
}

function mkStrip(isLeft, isRight) {
    return function stripBytes(chars) {
        let strip_chrs;
        if (chars === undefined || chars === Sk.builtin.none.none$) {
            // default is to remove ASCII whitespace
            strip_chrs = new Uint8Array([9, 10, 11, 12, 13, 32, 133]);
        } else {
            strip_chrs = this.get$raw(chars);
        }
        let start = 0,
            end = this.v.length;
        if (isLeft) {
            while (start < end && strip_chrs.includes(this.v[start])) {
                start++;
            }
        }
        if (isRight) {
            while (end > start && strip_chrs.includes(this.v[end - 1])) {
                end--;
            }
        }
        const final = new Uint8Array(end - start);
        for (let i = 0; i < final.length; i++) {
            final[i] = this.v[i + start];
        }
        return new Sk.builtin.bytes(final);
    };
}

function mkJust(funcname, isRight, isCenter) {
    return function justify(width, fillbyte) {
        if (fillbyte === undefined) {
            fillbyte = 32;
        } else if (!(fillbyte instanceof Sk.builtin.bytes) || fillbyte.v.length != 1) {
            throw new Sk.builtin.TypeError(funcname + "() argument 2 must be a byte string of length 1, not " + Sk.abstr.typeName(fillbyte));
        } else {
            fillbyte = fillbyte.v[0];
        }
        const mylen = this.v.length;
        width = Sk.misceval.asIndexSized(width, Sk.builtin.OverflowError);
        if (width <= mylen) {
            return new Sk.builtin.bytes(this.v);
        }
        const final = new Uint8Array(width);
        let fill1, fill2;
        if (isCenter) {
            fill1 = Math.floor((width - mylen) / 2);
            fill2 = (width - mylen) % 2 ? fill1 + 1 : fill1;
        } else if (isRight) {
            fill1 = width - mylen;
            fill2 = 0;
        } else {
            fill1 = 0;
            fill2 = width - mylen;
        }
        final.fill(fillbyte, 0, fill1);
        for (let i = 0; i < mylen; i++) {
            final[i + fill1] = this.v[i];
        }
        final.fill(fillbyte, width - fill2);
        return new Sk.builtin.bytes(final);
    };
}

function isspace(val) {
    return (val >= 9 && val <= 13) || val === 32;
}
function islower(val) {
    return val >= 97 && val <= 122;
}
function isupper(val) {
    return val >= 65 && val <= 90;
}
function isdigit(val) {
    return val >= 48 && val <= 57;
}

function checkSepMaxSplit(sep, maxsplit) {
    maxsplit = Sk.misceval.asIndexSized(maxsplit, Sk.builtin.OverflowError);
    maxsplit = maxsplit < 0 ? Infinity : maxsplit;

    sep = Sk.builtin.checkNone(sep) ? null : this.get$raw(sep);
    if (sep !== null && !sep.length) {
        throw new Sk.builtin.ValueError("empty separator");
    }
    return { sep: sep, maxsplit: maxsplit };
}

function mkIsAll(passTest, passesZero) {
    return function isAll() {
        if (this.v.length === 0) {
            return passesZero ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$;
        }
        return this.v.every((val) => passTest(val)) ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$;
    };
}

function makeIsUpperLower(passTest, failTest) {
    return function () {
        let flag = false;
        for (let i = 0; i < this.v.length; i++) {
            if (failTest(this.v[i])) {
                return Sk.builtin.bool.false$;
            }
            if (!flag && passTest(this.v[i])) {
                flag = true;
            }
        }
        return flag ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$;
    };
}

function mkCaseSwitch(switchCase) {
    return function lowerUpperSwapCase() {
        const final = new Uint8Array(this.v.length);
        for (let i = 0; i < this.v.length; i++) {
            final[i] = switchCase(this.v[i]);
        }
        return new Sk.builtin.bytes(final);
    };
}

/**
 * @constructor
 * @param {Sk.builtin.bytes} bytes
 */
var bytes_iter_ = Sk.abstr.buildIteratorClass("bytes_iterator", {
    constructor: function bytes_iter_(bytes) {
        this.$index = 0;
        this.$seq = bytes.v;
    },
    iternext() {
        const next = this.$seq[this.$index++];
        if (next === undefined) {
            return undefined;
        }
        return new Sk.builtin.int_(next);
    },
    methods: {
        __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});

Sk.exportSymbol("Sk.builtin.bytes", Sk.builtin.bytes);


/***/ }),

/***/ "./src/check.js":
/*!**********************!*\
  !*** ./src/check.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Check arguments to Python functions to ensure the correct number of
 * arguments are passed.
 *
 * @param {string} name the name of the function
 * @param {Object} args the args passed to the function
 * @param {number} minargs the minimum number of allowable arguments
 * @param {number=} maxargs optional maximum number of allowable
 * arguments (default: Infinity)
 * @param {boolean=} kwargs optional true if kwargs, false otherwise
 * (default: false)
 * @param {boolean=} free optional true if free vars, false otherwise
 * (default: false)
 */
Sk.builtin.pyCheckArgs = function (name, args, minargs, maxargs, kwargs, free) {
    var nargs = args.length;
    var msg = "";

    if (maxargs === undefined) {
        maxargs = Infinity;
    }
    if (kwargs) {
        nargs -= 1;
    }
    if (free) {
        nargs -= 1;
    }
    if (nargs < minargs || nargs > maxargs) {
        if (minargs === maxargs) {
            msg = name + "() takes exactly " + minargs + " arguments";
        } else if (nargs < minargs) {
            msg = name + "() takes at least " + minargs + " arguments";
        } else if (minargs > 0) {
            msg = name + "() takes at most " + maxargs + " arguments";
        } else {
            msg = name + "() takes no arguments";
        }
        msg += " (" + nargs + " given)";
        throw new Sk.builtin.TypeError(msg);
    }
};
Sk.exportSymbol("Sk.builtin.pyCheckArgs", Sk.builtin.pyCheckArgs);

/**
 * Check arguments to Python functions to ensure the correct number of
 * arguments are passed.
 *
 * @param {string} name the name of the function
 * @param {number} nargs the args passed to the function
 * @param {number} minargs the minimum number of allowable arguments
 * @param {number=} maxargs optional maximum number of allowable
 * arguments (default: Infinity)
 * @param {boolean=} kwargs optional true if kwargs, false otherwise
 * (default: false)
 * @param {boolean=} free optional true if free vars, false otherwise
 * (default: false)
 */
Sk.builtin.pyCheckArgsLen = function (name, nargs, minargs, maxargs, kwargs, free) {
    var msg = "";

    if (maxargs === undefined) {
        maxargs = Infinity;
    }
    if (kwargs) {
        nargs -= 1;
    }
    if (free) {
        nargs -= 1;
    }
    if (nargs < minargs || nargs > maxargs) {
        if (minargs === maxargs) {
            msg = name + "() takes exactly " + minargs + " arguments";
        } else if (nargs < minargs) {
            msg = name + "() takes at least " + minargs + " arguments";
        } else {
            msg = name + "() takes at most " + maxargs + " arguments";
        }
        msg += " (" + nargs + " given)";
        throw new Sk.builtin.TypeError(msg);
    }
};

/**
 * Check type of argument to Python functions.
 *
 * @param {string} name the name of the argument
 * @param {string} exptype string of the expected type name
 * @param {boolean} check truthy if type check passes, falsy otherwise
 */
Sk.builtin.pyCheckType = function (name, exptype, check) {
    if (!check) {
        throw new Sk.builtin.TypeError(name + " must be a " + exptype);
    }
};
Sk.exportSymbol("Sk.builtin.pyCheckType", Sk.builtin.pyCheckType);

/**
 * @function
 * @param {*} arg
 * 
 * @description
 * Does the arg have a valid `__getitem__` method?
 */
Sk.builtin.checkSequence = function (arg) {
    return arg != null && arg.mp$subscript !== undefined;
};
Sk.exportSymbol("Sk.builtin.checkSequence", Sk.builtin.checkSequence);

/**
 * @description
 * Use this to test whether or not a Python object is iterable.  You should **not** rely
 * on the presence of tp$iter on the object as a good test, as it could be a user defined
 * class with `__iter__` defined or ``__getitem__``  This tests for all of those cases
 * 
 * Note in most cases it will be more pragmatic to simply call {@link Sk.abstr.iter} which will 
 * throw the appropriate error if the pyObject is not iterable. 
 *
 * @param arg {Object}   A Python object
 * @returns {boolean} true if the object is iterable
 */
Sk.builtin.checkIterable = function (arg) {
    if (arg === undefined) {
        return false;
    }
    if (arg.tp$iter) {
        const iter = arg.tp$iter();
        return iter.tp$iternext !== undefined;
    }
    return arg.mp$subscript !== undefined;
};
Sk.exportSymbol("Sk.builtin.checkIterable", Sk.builtin.checkIterable);

/**
 * @function
 * @param {*} obj 
 */
Sk.builtin.checkCallable = function (obj) {
    // takes care of builtin functions and methods, builtins
    return obj != null && obj.tp$call !== undefined;
};

/**
 * @function
 * @description
 * Is the object an instance of {@link Sk.builtin.int_} or {@link Sk.builtin.float_}
 * 
 * @param {*} arg 
 */
Sk.builtin.checkNumber = function (arg) {
    return typeof arg === "number" || arg instanceof Sk.builtin.int_ || arg instanceof Sk.builtin.float_ || arg instanceof Sk.builtin.lng;
};
Sk.exportSymbol("Sk.builtin.checkNumber", Sk.builtin.checkNumber);

/**
 * @description
 * Is the arg an instance of {@link Sk.builtin.complex}
 */
Sk.builtin.checkComplex = function (arg) {
    return arg instanceof Sk.builtin.complex;
};
Sk.exportSymbol("Sk.builtin.checkComplex", Sk.builtin.checkComplex);

/**
 * @description
 * Supports both JS Number and pyObject
 * @param {*} arg 
 */
Sk.builtin.checkInt = function (arg) {
    return arg instanceof Sk.builtin.int_ || (typeof arg === "number" && Number.isInteger(arg));
};
Sk.exportSymbol("Sk.builtin.checkInt", Sk.builtin.checkInt);

/**
 * @description
 * Is the arg an instance of {@link Sk.builtin.float_}
 * @param {*} arg 
 */
Sk.builtin.checkFloat = function (arg) {
    return arg instanceof Sk.builtin.float_;
};
Sk.exportSymbol("Sk.builtin.checkFloat", Sk.builtin.checkFloat);

/**
 * @description
 * Is the arg an instance of {@link Sk.builtin.str}
 * @param {*} arg 
 */
Sk.builtin.checkString = function (arg) {
    return arg instanceof Sk.builtin.str;
};
Sk.exportSymbol("Sk.builtin.checkString", Sk.builtin.checkString);

/**
 * @description
 * Is the arg an instance of {@link Sk.builtin.bytes}
 * @param {*} arg 
 */
Sk.builtin.checkBytes = function (arg) {
    return arg instanceof Sk.builtin.bytes;
};


/**
 * Is the arg an instance of {@link Sk.builtin.type}
 * @param {*} arg 
 */
Sk.builtin.checkClass = function (arg) {
    return arg instanceof Sk.builtin.type;
};
Sk.exportSymbol("Sk.builtin.checkClass", Sk.builtin.checkClass);

/**
 * @description
 * Is the arg an instance of {@link Sk.builtin.bool}
 * @param {*} arg 
 */
Sk.builtin.checkBool = function (arg) {
    return arg instanceof Sk.builtin.bool;
};
Sk.exportSymbol("Sk.builtin.checkBool", Sk.builtin.checkBool);

Sk.builtin.checkNone = function (arg) {
    return arg === Sk.builtin.none.none$;
};
Sk.exportSymbol("Sk.builtin.checkNone", Sk.builtin.checkNone);

/**
 * @description
 * Is the arg callable?
 * @param {*} arg 
 */
Sk.builtin.checkFunction = function (arg) {
    return arg != null && arg.tp$call !== undefined;
};
Sk.exportSymbol("Sk.builtin.checkFunction", Sk.builtin.checkFunction);

Sk.builtin.checkDataDescr = function (arg) {
    return arg && arg.tp$descr_set !== undefined;
};
Sk.exportSymbol("Sk.builtin.checkDataDescr", Sk.builtin.checkDataDescr);

/**
 * @description
 * Is the arg ain instance of {@link Sk.builtin.set} or {@link Sk.builtin.frozenset}
 * @param {*} arg 
 */
Sk.builtin.checkAnySet = function (arg) {
    return arg instanceof Sk.builtin.set || arg instanceof Sk.builtin.frozenset;
};

Sk.builtin.checkMapping = function (arg) {
    return (
        arg instanceof Sk.builtin.dict ||
        arg instanceof Sk.builtin.mappingproxy ||
        (arg != null && arg.mp$subscript !== undefined && Sk.abstr.lookupSpecial(arg, Sk.builtin.str.$keys) !== undefined)
    );
};

/***/ }),

/***/ "./src/compile.js":
/*!************************!*\
  !*** ./src/compile.js ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {

/** @param {...*} x */
var out;

Sk.gensymcount = 0;

/**
 * @constructor
 * @param {string} filename
 * @param {SymbolTable} st
 * @param {number} flags
 * @param {boolean=} canSuspend whether compiled code can suspend
 * @param {string=} sourceCodeForAnnotation used to add original source to listing if desired
 */
function Compiler (filename, st, flags, canSuspend, sourceCodeForAnnotation) {
    this.filename = filename;
    this.st = st;
    this.flags = flags;
    this.canSuspend = canSuspend;
    this.interactive = false;
    this.nestlevel = 0;

    this.u = null;
    this.stack = [];

    this.result = [];

    // this.gensymcount = 0;

    this.allUnits = [];

    this.source = sourceCodeForAnnotation ? sourceCodeForAnnotation.split("\n") : false;
}

/**
 * @constructor
 *
 * Stuff that changes on entry/exit of code blocks. must be saved and restored
 * when returning to a block.
 *
 * Corresponds to the body of a module, class, or function.
 */

function CompilerUnit () {
    this.ste = null;
    this.name = null;
    this.canSuspend = false;
    this.doesSuspend = false;

    this.private_ = null;
    this.firstlineno = 0;
    this.lineno = 0;
    this.linenoSet = false;
    this.localnames = [];

    this.localtemps = [];
    this.tempsToSave = [];

    this.blocknum = 0;
    this.blocks = [];
    this.curblock = 0;

    this.consts = {};

    this.scopename = null;

    this.prefixCode = "";
    this.varDeclsCode = "";
    this.switchCode = "";
    this.suffixCode = "";

    // stack of where to go on a break
    this.breakBlocks = [];
    // stack of where to go on a continue
    this.continueBlocks = [];
    this.exceptBlocks = [];
    // state of where to go on a return
    this.finallyBlocks = [];
}

CompilerUnit.prototype.activateScope = function () {
    var self = this;

    out = function () {
        var i;
        var b = self.blocks[self.curblock];
        if (b._next === null) {
            for (i = 0; i < arguments.length; ++i) {
                b.push(arguments[i]);
            }
        }
        // TODO: Warn about unreachable code after an unconditional jump?
    };
};

Compiler.prototype.getSourceLine = function (lineno) {
    Sk.asserts.assert(this.source);
    return this.source[lineno - 1];
};

Compiler.prototype.annotateSource = function (ast) {
    var i;
    var col_offset;
    var lineno;
    if (this.source) {
        lineno = ast.lineno;
        col_offset = ast.col_offset;
        out("\n//\n// line ", lineno, ":\n// ", this.getSourceLine(lineno), "\n// ");
        for (i = 0; i < col_offset; ++i) {
            out(" ");
        }
        out("^\n//\n");

        Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined);
        out("$currLineNo = ", lineno, ";\n$currColNo = ", col_offset, ";\n\n");
    }
};

Compiler.prototype.gensym = function (hint) {
    hint = hint || "";
    hint = "$" + hint;
    hint += Sk.gensymcount++;
    return hint;
};

Compiler.prototype.niceName = function (roughName) {
    return this.gensym(roughName.replace("<", "").replace(">", "").replace(" ", "_"));
};

var reservedWords_ = Sk.builtin.str.reservedWords_; // defined in str.js


function fixReserved(name) {
    if (reservedWords_[name] === undefined) {
        return name;
    }
    return name + "_$rw$";
}

function unfixReserved(name) {
    return name.replace(/_\$rw\$$/, "");
}

function mangleName (priv, ident) {
    var name = ident.v;
    var strpriv = null;


    if (priv === null || name === null || name.charAt(0) !== "_" || name.charAt(1) !== "_") {
        return ident;
    }
    // don't mangle __id__
    if (name.charAt(name.length - 1) === "_" && name.charAt(name.length - 2) === "_") {
        return ident;
    }
    // don't mangle classes that are all _ (obscure much?)
    strpriv = priv.v;
    strpriv.replace(/_/g, "");
    if (strpriv === "") {
        return ident;
    }

    strpriv = priv.v;
    strpriv.replace(/^_*/, "");
    strpriv = new Sk.builtin.str("_" + strpriv + name);
    return strpriv;
}

/**
 * @param {...*} rest
 */
Compiler.prototype.makeConstant = function (rest) {
    var i;
    var v;
    var val = "";
    var cval;

    // Construct constant value
    for (i = 0; i < arguments.length; ++i) {
        val += arguments[i];
    }

    // Check if we've already defined this exact constant
    for (var constant in this.u.consts) {
        if (this.u.consts.hasOwnProperty(constant)) {
            cval = this.u.consts[constant];
            if (cval == val) {
                // We have, just use it
                return constant;
            }
        }
    }

    // We have not, build new one
    v = this.u.scopename + "." + this.gensym("const");
    this.u.consts[v] = val;
    return v;
};

/**
 * @param {string} hint basename for gensym
 * @param {...*} rest
 */
Compiler.prototype._gr = function (hint, rest) {
    var i;
    var v = this.gensym(hint);
    this.u.localtemps.push(v);
    out("var ", v, "=");
    for (i = 1; i < arguments.length; ++i) {
        out(arguments[i]);
    }
    out(";");
    return v;
};

/**
 * Function to test if an interrupt should occur if the program has been running for too long.
 * This function is executed at every test/branch operation.
 */
Compiler.prototype.outputInterruptTest = function () { // Added by RNL
    var output = "";
    if (Sk.execLimit !== null || Sk.yieldLimit !== null && this.u.canSuspend) {
        output += "var $dateNow = Date.now();";
        if (Sk.execLimit !== null) {
            output += "if ($dateNow - Sk.execStart > Sk.execLimit) {throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}";
        }
        if (Sk.yieldLimit !== null && this.u.canSuspend) {
            output += "if ($dateNow - Sk.lastYield > Sk.yieldLimit) {";
            output += "var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '"+this.filename+"',$currLineNo,$currColNo);";
            output += "$susp.$blk = $blk;";
            output += "$susp.optional = true;";
            output += "return $susp;";
            output += "}";
            this.u.doesSuspend = true;
        }
    }
    return output;
};

Compiler.prototype._jumpfalse = function (test, block) {
    var cond = this._gr("jfalse", "(", test, "===false||!Sk.misceval.isTrue(", test, "))");
    out("if(", cond, "){/*test failed */$blk=", block, ";continue;}");
};

Compiler.prototype._jumpundef = function (test, block) {
    out("if(", test, "===undefined){$blk=", block, ";continue;}");
};

Compiler.prototype._jumpnotundef = function (test, block) {
    out("if(", test, "!==undefined){$blk=", block, ";continue;}");
};

Compiler.prototype._jumptrue = function (test, block) {
    var cond = this._gr("jtrue", "(", test, "===true||Sk.misceval.isTrue(", test, "))");
    out("if(", cond, "){/*test passed */$blk=", block, ";continue;}");
};

Compiler.prototype._jump = function (block) {
    if (this.u.blocks[this.u.curblock]._next === null) {
        out("$blk=", block, ";");
        this.u.blocks[this.u.curblock]._next = block;
    }
};

/**
 * @param {Object=} e Object with keys 'lineno' and 'col_offset'
 */
Compiler.prototype._checkSuspension = function(e) {
    var retblk;
    if (this.u.canSuspend) {

        retblk = this.newBlock("function return or resume suspension");
        this._jump(retblk);
        this.setBlock(retblk);

        e = e || {lineno: "$currLineNo", col_offset: "$currColNo"};

        out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+e.lineno+","+e.col_offset+"); }");

        this.u.doesSuspend = true;
        this.u.tempsToSave = this.u.tempsToSave.concat(this.u.localtemps);

    } else {
        out ("if ($ret && $ret.$isSuspension) { $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); }");
    }
};
Compiler.prototype.cunpackstarstoarray = function(elts, permitEndOnly) {
    if (!elts || elts.length == 0) {
        return "[]";
    }

    let hasStars = false;
    // If there are no stars, we have a nice fast path here
    for (let elt of elts) {
        if (permitEndOnly && hasStars) {
            throw new Sk.builtin.SyntaxError("Extended argument unpacking is not permitted in Python 2");
        }
        if (elt.constructor === Sk.astnodes.Starred) {
            hasStars = true;
        }
    }

    if (hasStars) {
        // Slow path
        let arr = this._gr("unpack", "[]");
        for (let elt of elts) {
            if (elt.constructor !== Sk.astnodes.Starred) {
                out(arr,".push(",this.vexpr(elt),");");
            } else {
                out("$ret = Sk.misceval.iterFor(Sk.abstr.iter(",this.vexpr(elt.value),"), function(e) { ",arr,".push(e); });");
                this._checkSuspension();
            }
        }
        return arr;
    } else {
        // Fast path
        return "[" + elts.map((expr) => this.vexpr(expr)).join(",") + "]";
    }
};

Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) {
    var i;
    var items;
    var item;
    var allconsts;
    Sk.asserts.assert(tuporlist === "tuple" || tuporlist === "list" || tuporlist === "set");

    let hasStars = false;
    let starIdx;
    for (i = 0; i < e.elts.length; i++) {
        if (e.elts[i].constructor === Sk.astnodes.Starred) {
            hasStars = true;
            starIdx = i;
            break;
        }
    }

    if (e.ctx === Sk.astnodes.Store) {
        if (hasStars) {
            if (!Sk.__future__.python3) {
                throw new Sk.builtin.SyntaxError("assignment unpacking with stars is not supported in Python 2", this.filename, e.lineno);
            }
            for (i = starIdx + 1; i < e.elts.length; i++) {
                if (e.elts[i].constructor === Sk.astnodes.Starred) {
                    throw new Sk.builtin.SyntaxError("multiple starred expressions in assignment", this.filename, e.lineno);
                }
            }
        }
        const breakIdx = hasStars ? starIdx : e.elts.length;
        const numvals = hasStars ? e.elts.length - 1 : breakIdx;
        out("$ret = Sk.abstr.sequenceUnpack(" + data + "," + breakIdx + "," + numvals + ", " + hasStars + ");");
        this._checkSuspension();
        items = this._gr("items", "$ret");
        
        for (i = 0; i < e.elts.length; ++i) {
            if (i === starIdx) {
                this.vexpr(e.elts[i].value, items + "[" + i + "]");
            } else {
                this.vexpr(e.elts[i], items + "[" + i + "]");
            }
        }
    } else if (e.ctx === Sk.astnodes.Load || tuporlist === "set") {
        //because set's can't be assigned to.

        if (hasStars) {
            if (!Sk.__future__.python3) {
                throw new Sk.builtin.SyntaxError("List packing with stars is not supported in Python 2");
            }
            return this._gr("load" + tuporlist, "new Sk.builtins['", tuporlist, "'](", this.cunpackstarstoarray(e.elts), ")");
        } else if (tuporlist === "tuple") {
            allconsts = true;
            items = [];
            for (i = 0; i < e.elts.length; ++i) {
                item = this.vexpr(e.elts[i]);

                // The following is an ugly check to see if item was
                // turned into a constant.  As vexpr returns a string,
                // this requires seeing if "$const" is contained
                // within it.  A better solution would require a
                // change to vexpr, which would be more invasive.
                if (allconsts && (item.indexOf("$const") == -1)) {
                    allconsts = false;
                }
                items.push(item);
            }

            if (allconsts) {
                return this.makeConstant("new Sk.builtin.tuple([" + items + "])");
            } else {
                for (i = 0; i < items.length; ++i) {
                    items[i] = this._gr("elem", items[i]);
                }
                return this._gr("load" + tuporlist, "new Sk.builtins['", tuporlist, "']([", items, "])");
            }
        } else {
            items = [];
            for (i = 0; i < e.elts.length; ++i) {
                items.push(this._gr("elem", this.vexpr(e.elts[i])));
            }
            return this._gr("load" + tuporlist, "new Sk.builtins['", tuporlist, "']([", items, "])");
        }
    }
};

Compiler.prototype.cdict = function (e) {
    var v;
    var i;
    var items;
    items = [];
    if (e.keys !== null) {
        Sk.asserts.assert(e.values.length === e.keys.length);
        for (i = 0; i < e.values.length; ++i) {
            v = this.vexpr(e.values[i]); // "backwards" to match order in cpy
            items.push(this.vexpr(e.keys[i]));
            items.push(v);
        }
    }
    return this._gr("loaddict", "new Sk.builtins['dict']([", items, "])");
};

Compiler.prototype.clistcomp = function(e) {
    Sk.asserts.assert(e instanceof Sk.astnodes.ListComp);
    var tmp = this._gr("_compr", "new Sk.builtins['list']([])"); // note: _ is impt. for hack in name mangling (same as cpy)
    return this.ccompgen("list", tmp, e.generators, 0, e.elt, null, e);
};

Compiler.prototype.cdictcomp = function(e) {
    Sk.asserts.assert(e instanceof Sk.astnodes.DictComp);
    var tmp = this._gr("_dcompr", "new Sk.builtins.dict([])");
    return this.ccompgen("dict", tmp, e.generators, 0, e.value, e.key, e);
};

Compiler.prototype.csetcomp = function(e) {
    Sk.asserts.assert(e instanceof Sk.astnodes.SetComp);
    var tmp = this._gr("_setcompr", "new Sk.builtins.set([])");
    return this.ccompgen("set", tmp, e.generators, 0, e.elt, null, e);
};

Compiler.prototype.ccompgen = function (type, tmpname, generators, genIndex, value, key, e) {
    var start = this.newBlock(type + " comp start");
    var skip = this.newBlock(type + " comp skip");
    var anchor = this.newBlock(type + " comp anchor");

    var l = generators[genIndex];
    var toiter = this.vexpr(l.iter);
    var iter = this._gr("iter", "Sk.abstr.iter(", toiter, ")");
    var lvalue;
    var lkey;
    var ifres;
    var i;
    var target;
    var nexti;
    var n;

    this._jump(start);
    this.setBlock(start);

    // load targets
    out("$ret = Sk.abstr.iternext(", iter, ", true);");

    this._checkSuspension(e);

    nexti = this._gr("next", "$ret");
    this._jumpundef(nexti, anchor); // todo; this should be handled by StopIteration
    target = this.vexpr(l.target, nexti);

    n = l.ifs ? l.ifs.length : 0;
    for (i = 0; i < n; ++i) {
        ifres = this.vexpr(l.ifs[i]);
        this._jumpfalse(ifres, start);
    }

    if (++genIndex < generators.length) {
        this.ccompgen(type, tmpname, generators, genIndex, value, key, e);
    }

    if (genIndex >= generators.length) {
        lvalue = this.vexpr(value);
        if (type === "dict") {
            lkey = this.vexpr(key);
            out(tmpname, ".mp$ass_subscript(", lkey, ",", lvalue, ");");
        } else if (type === "list") {
            out(tmpname, ".v.push(", lvalue, ");"); // todo;
        } else if (type === "set") {
            out(tmpname, ".v.mp$ass_subscript(", lvalue, ", true);");
        }
        this._jump(skip);
        this.setBlock(skip);
    }

    this._jump(start);

    this.setBlock(anchor);

    return tmpname;
};

Compiler.prototype.cyield = function(e) {
    if (this.u.ste.blockType !== Sk.SYMTAB_CONSTS.FunctionBlock) {
        throw new Sk.builtin.SyntaxError("'yield' outside function", this.filename, e.lineno);
    }
    var val = "Sk.builtin.none.none$",
        nextBlock;
    if (e.value) {
        val = this.vexpr(e.value);
    }
    nextBlock = this.newBlock("after yield");
    // return a pair: resume target block and yielded value
    out("return [/*resume*/", nextBlock, ",/*ret*/", val, "];");
    this.setBlock(nextBlock);
    return "$gen.gi$sentvalue"; // will either be none if none sent, or the value from gen.send(value)
};

Compiler.prototype.ccompare = function (e) {
    var res;
    var rhs;
    var i;
    var fres;
    var done;
    var n;
    var cur;
    Sk.asserts.assert(e.ops.length === e.comparators.length);
    cur = this.vexpr(e.left);
    n = e.ops.length;
    done = this.newBlock("done");
    fres = this._gr("compareres", "null");

    for (i = 0; i < n; ++i) {
        rhs = this.vexpr(e.comparators[i]);
        out("$ret = Sk.builtin.bool(Sk.misceval.richCompareBool(", cur, ",", rhs, ",'", e.ops[i].prototype._astname, "', true));");
        this._checkSuspension(e);
        out(fres, "=$ret;");
        this._jumpfalse("$ret", done);
        cur = rhs;
    }
    this._jump(done);
    this.setBlock(done);
    return fres;
};

Compiler.prototype.ccall = function (e) {
    var func = this.vexpr(e.func);
    var kwarray = null;
    // Okay, here's the deal. We have some set of positional args
    // and we need to unpack them. We have some set of keyword args
    // and we need to unpack those too. Then we make a call.
    // The existing Sk.misceval.call() and .apply() signatures do not
    // help us here; we do it by hand.

    let positionalArgs = this.cunpackstarstoarray(e.args, !Sk.__future__.python3);
    let keywordArgs = "undefined";

    if (e.keywords && e.keywords.length > 0) {
        let hasStars = false;
        kwarray = [];
        for (let kw of e.keywords) {
            if (hasStars && !Sk.__future__.python3) {
                throw new SyntaxError("Advanced unpacking of function arguments is not supported in Python 2");
            }
            if (kw.arg) {
                kwarray.push("'" + kw.arg.v + "'");
                kwarray.push(this.vexpr(kw.value));
            } else {
                hasStars = true;
            }
        }
        keywordArgs = "[" + kwarray.join(",") + "]";
        if (hasStars) {
            keywordArgs = this._gr("keywordArgs", keywordArgs);
            for (let kw of e.keywords) {
                if (!kw.arg) {
                    out("$ret = Sk.abstr.mappingUnpackIntoKeywordArray(",keywordArgs,",",this.vexpr(kw.value),",",func,");");
                    this._checkSuspension();
                }
            }
        }
    }

    if (Sk.__future__.super_args && e.func.id && e.func.id.v === "super" && positionalArgs === "[]") {
        // make sure there is a self variable
        // note that it's part of the js API spec: https://developer.mozilla.org/en/docs/Web/API/Window/self
        // so we should probably add self to the mangling
        // TODO: feel free to ignore the above
        out("if (typeof self === \"undefined\" || self.toString().indexOf(\"Window\") > 0) { throw new Sk.builtin.RuntimeError(\"super(): no arguments\") };");
        positionalArgs = "[$gbl.__class__,self]";
    }
    out ("$ret = (",func,".tp$call)?",func,".tp$call(",positionalArgs,",",keywordArgs,") : Sk.misceval.applyOrSuspend(",func,",undefined,undefined,",keywordArgs,",",positionalArgs,");");

    this._checkSuspension(e);

    return this._gr("call", "$ret");
};

Compiler.prototype.cslice = function (s) {
    var step;
    var high;
    var low;
    Sk.asserts.assert(s instanceof Sk.astnodes.Slice);
    if (Sk.__future__.python3) {
        low = s.lower ? this.vexpr(s.lower) : "Sk.builtin.none.none$";
        high = s.upper ? this.vexpr(s.upper) : "Sk.builtin.none.none$";
        step = s.step ? this.vexpr(s.step) : "Sk.builtin.none.none$";
    } else {
        // This implements Python 2's idea of slice literals, which is...idiosyncratic.
        // The rules for when you get None, and when you get an arbitrary integer (0 or maxint)
        // seem pretty arbitrary. Python 3's are much saner.
        low = s.lower ? this.vexpr(s.lower) : s.step ? "Sk.builtin.none.none$" : "new Sk.builtin.int_(0)"; // todo;ideally, these numbers would be constants
        high = s.upper ? this.vexpr(s.upper) : s.step ? "Sk.builtin.none.none$" : "new Sk.builtin.int_(2147483647)";
        step = s.step ? this.vexpr(s.step) : "Sk.builtin.none.none$";
    }
    return this._gr("slice", "new Sk.builtins['slice'](", low, ",", high, ",", step, ")");
};

Compiler.prototype.eslice = function (dims) {
    var i;
    var dimSubs, subs;
    Sk.asserts.assert(dims instanceof Array);
    dimSubs = [];
    for (i = 0; i < dims.length; i++) {
        dimSubs.push(this.vslicesub(dims[i]));
    }
    return this._gr("extslice", "new Sk.builtins['tuple']([", dimSubs, "])");
};

Compiler.prototype.vslicesub = function (s) {
    var subs;
    switch (s.constructor) {
        case Sk.astnodes.Index:
            subs = this.vexpr(s.value);
            break;
        case Sk.astnodes.Slice:
            subs = this.cslice(s);
            break;
        case Sk.astnodes.Ellipsis:
            Sk.asserts.fail("todo compile.js Ellipsis;");
            break;
        case Sk.astnodes.ExtSlice:
            subs = this.eslice(s.dims);
            break;
        default:
            Sk.asserts.fail("invalid subscript kind");
    }
    return subs;
};

Compiler.prototype.vslice = function (s, ctx, obj, dataToStore) {
    var subs = this.vslicesub(s);
    return this.chandlesubscr(ctx, obj, subs, dataToStore);
};

Compiler.prototype.chandlesubscr = function (ctx, obj, subs, data) {
    if (ctx === Sk.astnodes.Load || ctx === Sk.astnodes.AugLoad) {
        out("$ret = Sk.abstr.objectGetItem(", obj, ",", subs, ", true);");
        this._checkSuspension();
        return this._gr("lsubscr", "$ret");
    } else if (ctx === Sk.astnodes.Store || ctx === Sk.astnodes.AugStore) {
        out("$ret = Sk.abstr.objectSetItem(", obj, ",", subs, ",", data, ", true);");
        this._checkSuspension();
    } else if (ctx === Sk.astnodes.Del) {
        out("Sk.abstr.objectDelItem(", obj, ",", subs, ");");
    } else {
        Sk.asserts.fail("handlesubscr fail");
    }
};

Compiler.prototype.cboolop = function (e) {
    var expres;
    var i;
    var retval;
    var n;
    var s;
    var end;
    var ifFailed;
    var jtype;
    Sk.asserts.assert(e instanceof Sk.astnodes.BoolOp);
    if (e.op === Sk.astnodes.And) {
        jtype = this._jumpfalse;
    } else {
        jtype = this._jumptrue;
    }
    end = this.newBlock("end of boolop");
    s = e.values;
    n = s.length;
    for (i = 0; i < n; ++i) {
        expres = this.vexpr(s[i]);
        if (i === 0) {
            retval = this._gr("boolopsucc", expres);
        }
        out(retval, "=", expres, ";");
        jtype.call(this, expres, end);
    }
    this._jump(end);
    this.setBlock(end);
    return retval;
};


Compiler.prototype.cjoinedstr = function (e) {
    let ret;
    Sk.asserts.assert(e instanceof Sk.astnodes.JoinedStr);

    for (let s of e.values) {
        let v = this.vexpr(s);
        if (!ret) {
            ret = this._gr("joinedstr", v);
        } else {
            out(ret,"=",ret,".sq$concat(",v,");");
        }
    }

    if (!ret) {
        ret = "Sk.builtin.str.$emptystr";
    }

    return ret;
};

Compiler.prototype.cformattedvalue = function(e) {
    let value = this.vexpr(e.value);
    switch (e.conversion) {
        case "s":
            value = this._gr("value", "new Sk.builtin.str(",value,")");
            break;
        case "a":
            value = this._gr("value", "Sk.builtin.ascii(",value,")");
            break;
        case "r":
            value = this._gr("value", "Sk.builtin.repr(",value,")");
            break;
    }
    let formatSpec = (e.format_spec ? this.vexpr(e.format_spec) : "Sk.builtin.str.$emptystr");
    return this._gr("formatted", "Sk.abstr.objectFormat("+value+","+formatSpec+")");
};

function getJsLiteralForString(s) {
    let r = "\"";
    for (let i = 0; i < s.length; i++) {
        let c = s.charCodeAt(i);
        // Escape quotes, anything before space, and anything non-ASCII
        if (c == 0x0a) {
            r += "\\n";
        } else if (c == 92) {
            r += "\\\\";
        } else if (c == 34 || c < 32 || c >= 0x7f && c < 0x100) {
            r += "\\x" + ("0" + c.toString(16)).substr(-2);
        } else if (c >= 0x100) {
            r += "\\u" + ("000" + c.toString(16)).substr(-4);
        } else {
            r += s.charAt(i);
        }
    }
    r += "\"";
    return r;
}

/**
 *
 * compiles an expression. to 'return' something, it'll gensym a var and store
 * into that var so that the calling code doesn't have avoid just pasting the
 * returned name.
 *
 * @param {Object} e
 * @param {string=} data data to store in a store operation
 * @param {Object=} augvar var to load/store to for augmented assignments like '+='.
 *                  (already vexpr'ed, so we can evaluate it once and reuse for both load and store ops)
 * @param {Object=} augsubs precomputed subscript for augmented assignments like '+='.
 *                  (already vexpr'ed, so we can evaluate it once and reuse for both load and store ops)
 */
Compiler.prototype.vexpr = function (e, data, augvar, augsubs) {
    var mangled, mname;
    var val;
    var result;
    var nStr; // used for preserving signs for floats (zeros)
    if (e.lineno > this.u.lineno) {
        this.u.lineno = e.lineno;
        this.u.linenoSet = false;
    }
    //this.annotateSource(e);
    switch (e.constructor) {
        case Sk.astnodes.BoolOp:
            return this.cboolop(e);
        case Sk.astnodes.BinOp:
            return this._gr("binop", "Sk.abstr.numberBinOp(", this.vexpr(e.left), ",", this.vexpr(e.right), ",'", e.op.prototype._astname, "')");
        case Sk.astnodes.UnaryOp:
            return this._gr("unaryop", "Sk.abstr.numberUnaryOp(", this.vexpr(e.operand), ",'", e.op.prototype._astname, "')");
        case Sk.astnodes.Lambda:
            return this.clambda(e);
        case Sk.astnodes.IfExp:
            return this.cifexp(e);
        case Sk.astnodes.Dict:
            return this.cdict(e);
        case Sk.astnodes.ListComp:
            return this.clistcomp(e);
        case Sk.astnodes.DictComp:
            return this.cdictcomp(e);
        case Sk.astnodes.SetComp:
            return this.csetcomp(e);
        case Sk.astnodes.GeneratorExp:
            return this.cgenexp(e);
        case Sk.astnodes.Yield:
            return this.cyield(e);
        case Sk.astnodes.Compare:
            return this.ccompare(e);
        case Sk.astnodes.Call:
            result = this.ccall(e);
            // After the function call, we've returned to this line
            this.annotateSource(e);
            return result;
        case Sk.astnodes.Num:
            if (typeof e.n === "number") {
                return e.n;
            } else if (e.n instanceof Sk.builtin.lng) {
                return this.makeConstant("new Sk.builtin.lng('" + e.n.v.toString() + "')"); 
            } else if (e.n instanceof Sk.builtin.int_) {
                if (typeof e.n.v === "number") {
                    return this.makeConstant("new Sk.builtin.int_(" + e.n.v + ")");
                }
                return this.makeConstant("new Sk.builtin.int_('" + e.n.v.toString() + "')"); 
            } else if (e.n instanceof Sk.builtin.float_) {
                // Preserve sign of zero for floats
                nStr = e.n.v === 0 && 1/e.n.v === -Infinity ? "-0" : e.n.v;
                return this.makeConstant("new Sk.builtin.float_(" + nStr + ")");
            } else if (e.n instanceof Sk.builtin.complex) {
                // preserve sign of zero here too
                var real_val = e.n.real === 0 && 1/e.n.real === -Infinity ? "-0" : e.n.real;
                var imag_val = e.n.imag === 0 && 1/e.n.imag === -Infinity ? "-0" : e.n.imag;
                return this.makeConstant("new Sk.builtin.complex(" + real_val + ", " + imag_val + ")");
            }
            Sk.asserts.fail("unhandled Num type");
        case Sk.astnodes.Bytes:
            if (Sk.__future__.python3) {
                const source = [];
                const str = e.s.$jsstr();
                for (let i = 0; i < str.length; i++) {
                    source.push(str.charCodeAt(i));
                }
                return this.makeConstant("new Sk.builtin.bytes([", source.join(", "), "])");
            }
            // else fall through and make a string instead
        case Sk.astnodes.Str:
            return this.makeConstant("new Sk.builtin.str(", getJsLiteralForString(e.s.$jsstr()), ")");
        case Sk.astnodes.Attribute:
            if (e.ctx !== Sk.astnodes.AugLoad && e.ctx !== Sk.astnodes.AugStore) {
                val = this.vexpr(e.value);
            }
            mangled = e.attr["$r"]().v;
            mangled = mangled.substring(1, mangled.length - 1);
            mangled = mangleName(this.u.private_, new Sk.builtin.str(mangled)).v;
            mname = this.makeConstant("new Sk.builtin.str('" + mangled + "')");
            switch (e.ctx) {
                case Sk.astnodes.AugLoad:
                    out("$ret = ", augvar, ".tp$getattr(", mname, ", true);");
                    this._checkSuspension(e);
                    out("\nif ($ret === undefined) {");
                    out("\nthrow new Sk.builtin.AttributeError(", augvar, ".sk$attrError() + \" has no attribute '\" + ", mname,".$jsstr() + \"'\");");
                    out("\n};");
                    return this._gr("lattr", "$ret");
                case Sk.astnodes.Load:
                    out("$ret = ", val, ".tp$getattr(", mname, ", true);");
                    this._checkSuspension(e);
                    out("\nif ($ret === undefined) {");
                    out("\nthrow new Sk.builtin.AttributeError(", val, ".sk$attrError() + \" has no attribute '\" + ", mname,".$jsstr() + \"'\");");
                    out("\n};");
                    return this._gr("lattr", "$ret");
                case Sk.astnodes.AugStore:
                    // To be more correct, we shouldn't sattr() again if the in-place update worked.
                    // At the time of writing (26/Feb/2015), Sk.abstr.numberInplaceBinOp never returns undefined,
                    // so this will never *not* execute. But it could, if Sk.abstr.numberInplaceBinOp were fixed.
                    out("$ret = undefined;");
                    out("if(", data, "!==undefined){");
                    out("$ret = ",augvar, ".tp$setattr(", mname, ",", data, ", true);");
                    out("}");
                    this._checkSuspension(e);
                    break;
                case Sk.astnodes.Store:
                    out("$ret = ", val, ".tp$setattr(", mname, ",", data, ", true);");
                    this._checkSuspension(e);
                    break;
                case Sk.astnodes.Del:
                    out("$ret = ", val, ".tp$setattr(", mname, ", undefined, true);");
                    this._checkSuspension(e);
                    break;
                case Sk.astnodes.Param:
                default:
                    Sk.asserts.fail("invalid attribute expression");
            }
            break;
        case Sk.astnodes.Subscript:
            switch (e.ctx) {
                case Sk.astnodes.AugLoad:
                    out("$ret = Sk.abstr.objectGetItem(",augvar,",",augsubs,", true);");
                    this._checkSuspension(e);
                    return this._gr("gitem", "$ret");
                case Sk.astnodes.Load:
                case Sk.astnodes.Store:
                case Sk.astnodes.Del:
                    return this.vslice(e.slice, e.ctx, this.vexpr(e.value), data);
                case Sk.astnodes.AugStore:
                    // To be more correct, we shouldn't sattr() again if the in-place update worked.
                    // At the time of writing (26/Feb/2015), Sk.abstr.numberInplaceBinOp never returns undefined,
                    // so this will never *not* execute. But it could, if Sk.abstr.numberInplaceBinOp were fixed.

                    out("$ret=undefined;");
                    out("if(", data, "!==undefined){");
                    out("$ret=Sk.abstr.objectSetItem(",augvar,",",augsubs,",",data,", true)");
                    out("}");
                    this._checkSuspension(e);
                    break;
                case Sk.astnodes.Param:
                default:
                    Sk.asserts.fail("invalid subscript expression");
            }
            break;
        case Sk.astnodes.Name:
            return this.nameop(e.id, e.ctx, data);
        case Sk.astnodes.NameConstant:
            if (e.ctx === Sk.astnodes.Store || e.ctx === Sk.astnodes.AugStore || e.ctx === Sk.astnodes.Del) {
                throw new Sk.builtin.SyntaxError("can not assign to a constant name");
            }

            switch (e.value) {
                case Sk.builtin.none.none$:
                    return "Sk.builtin.none.none$";
                case Sk.builtin.bool.true$:
                    return "Sk.builtin.bool.true$";
                case Sk.builtin.bool.false$:
                    return "Sk.builtin.bool.false$";
                default:
                    Sk.asserts.fail("invalid named constant");
            }
            break;
        case Sk.astnodes.List:
            return this.ctuplelistorset(e, data, "list");
        case Sk.astnodes.Tuple:
            return this.ctuplelistorset(e, data, "tuple");
        case Sk.astnodes.Set:
            return this.ctuplelistorset(e, data, "set");
        case Sk.astnodes.Starred:
            switch (e.ctx) {
                case Sk.astnodes.Store:
                    /* In all legitimate cases, the Starred node was already replaced
                     * by compiler_list/compiler_tuple. XXX: is that okay? */
                    throw new Sk.builtin.SyntaxError("starred assignment target must be in a list or tuple", this.filename, e.lineno);
                default:
                    throw new Sk.builtin.SyntaxError("can't use starred expression here", this.filename, e.lineno);
            }
        case Sk.astnodes.JoinedStr:
            return this.cjoinedstr(e);
        case Sk.astnodes.FormattedValue:
            return this.cformattedvalue(e);
        default:
            Sk.asserts.fail("unhandled case " + e.constructor.name + " vexpr");
    }
};

/**
 * @param {Array.<Object>} exprs
 * @param {Array.<string>=} data
 */
Compiler.prototype.vseqexpr = function (exprs, data) {
    var i;
    var ret;
    Sk.asserts.assert(data === undefined || exprs.length === data.length);
    ret = [];

    // if (exprs.length === 1 && exprs[0].constructor === Sk.astnodes.Starred) {
    //     exprs = exprs[0].value;
    // }

    for (i = 0; i < exprs.length; ++i) {
        ret.push(this.vexpr(exprs[i], data === undefined ? undefined : data[i]));
    }
    return ret;
};

Compiler.prototype.caugassign = function (s) {
    var to;
    var augsub;
    var res;
    var val;
    var aug;
    var auge;
    var e;
    Sk.asserts.assert(s instanceof Sk.astnodes.AugAssign);
    e = s.target;
    switch (e.constructor) {
        case Sk.astnodes.Attribute:
            to = this.vexpr(e.value);
            auge = new Sk.astnodes.Attribute(e.value, e.attr, Sk.astnodes.AugLoad, e.lineno, e.col_offset);
            aug = this.vexpr(auge, undefined, to);
            val = this.vexpr(s.value);
            res = this._gr("inplbinopattr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')");
            auge.ctx = Sk.astnodes.AugStore;
            return this.vexpr(auge, res, to);
        case Sk.astnodes.Subscript:
            // Only compile the subscript value once
            to = this.vexpr(e.value);
            augsub = this.vslicesub(e.slice);
            auge = new Sk.astnodes.Subscript(e.value, augsub, Sk.astnodes.AugLoad, e.lineno, e.col_offset);
            aug = this.vexpr(auge, undefined, to, augsub);
            val = this.vexpr(s.value);
            res = this._gr("inplbinopsubscr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')");
            auge.ctx = Sk.astnodes.AugStore;
            return this.vexpr(auge, res, to, augsub);
        case Sk.astnodes.Name:
            to = this.nameop(e.id, Sk.astnodes.Load);
            val = this.vexpr(s.value);
            res = this._gr("inplbinop", "Sk.abstr.numberInplaceBinOp(", to, ",", val, ",'", s.op.prototype._astname, "')");
            return this.nameop(e.id, Sk.astnodes.Store, res);
        default:
            Sk.asserts.fail("unhandled case in augassign");
    }
};

/**
 * optimize some constant exprs. returns 0 if always false, 1 if always true or -1 otherwise.
 */
Compiler.prototype.exprConstant = function (e) {
    switch (e.constructor) {
        case Sk.astnodes.Num:
            return Sk.misceval.isTrue(e.n) ? 1 : 0;
        case Sk.astnodes.Str:
            return Sk.misceval.isTrue(e.s) ? 1 : 0;
        case Sk.astnodes.Name:
        // todo; do __debug__ test here if opt
        default:
            return -1;
    }
};

Compiler.prototype.newBlock = function (name) {
    var ret = this.u.blocknum++;
    this.u.blocks[ret] = [];
    this.u.blocks[ret]._name = name || "<unnamed>";
    this.u.blocks[ret]._next = null;
    return ret;
};
Compiler.prototype.setBlock = function (n) {
    Sk.asserts.assert(n >= 0 && n < this.u.blocknum);
    this.u.curblock = n;
};

Compiler.prototype.pushBreakBlock = function (n) {
    Sk.asserts.assert(n >= 0 && n < this.u.blocknum);
    this.u.breakBlocks.push(n);
};
Compiler.prototype.popBreakBlock = function () {
    this.u.breakBlocks.pop();
};

Compiler.prototype.pushContinueBlock = function (n) {
    Sk.asserts.assert(n >= 0 && n < this.u.blocknum);
    this.u.continueBlocks.push(n);
};
Compiler.prototype.popContinueBlock = function () {
    this.u.continueBlocks.pop();
};

Compiler.prototype.pushExceptBlock = function (n) {
    Sk.asserts.assert(n >= 0 && n < this.u.blocknum);
    this.u.exceptBlocks.push(n);
};
Compiler.prototype.popExceptBlock = function () {
    this.u.exceptBlocks.pop();
};

Compiler.prototype.pushFinallyBlock = function (n) {
    Sk.asserts.assert(n >= 0 && n < this.u.blocknum);
    Sk.asserts.assert(this.u.breakBlocks.length === this.u.continueBlocks.length);
    this.u.finallyBlocks.push({blk: n, breakDepth: this.u.breakBlocks.length});
};
Compiler.prototype.popFinallyBlock = function () {
    this.u.finallyBlocks.pop();
};
Compiler.prototype.peekFinallyBlock = function() {
    return (this.u.finallyBlocks.length > 0) ? this.u.finallyBlocks[this.u.finallyBlocks.length-1] : undefined;
};

Compiler.prototype.setupExcept = function (eb) {
    out("$exc.push(", eb, ");");
    //this.pushExceptBlock(eb);
};

Compiler.prototype.endExcept = function () {
    out("$exc.pop();");
};

Compiler.prototype.outputLocals = function (unit) {
    var name;
    var output;
    var i;
    var have = {};
    //print("args", unit.name.v, JSON.stringify(unit.argnames));
    for (i = 0; unit.argnames && i < unit.argnames.length; ++i) {
        have[unit.argnames[i]] = true;
    }
    unit.localnames.sort();
    output = [];
    for (i = 0; i < unit.localnames.length; ++i) {
        name = unit.localnames[i];
        if (have[name] === undefined) {
            output.push(name);
            have[name] = true;
        }
    }
    if (output.length > 0) {
        return "var " + output.join(",") + "; /* locals */";
    }
    return "";
};

Compiler.prototype.outputSuspensionHelpers = function (unit) {
    var i, t;
    var localSaveCode = [];
    var localsToSave = unit.localnames.concat(unit.tempsToSave);
    var seenTemps = {};
    var hasCell = unit.ste.blockType === Sk.SYMTAB_CONSTS.FunctionBlock && unit.ste.childHasFree;
    var output = (localsToSave.length > 0 ? ("var " + localsToSave.join(",") + ";") : "") +
                 "var $wakeFromSuspension = function() {" +
                    "var susp = "+unit.scopename+".$wakingSuspension; "+unit.scopename+".$wakingSuspension = undefined;" +
                    "$blk=susp.$blk; $loc=susp.$loc; $gbl=susp.$gbl; $exc=susp.$exc; $err=susp.$err; $postfinally=susp.$postfinally;" +
                    "$currLineNo=susp.$lineno; $currColNo=susp.$colno; Sk.lastYield=Date.now();" +
                    (hasCell?"$cell=susp.$cell;":"");

    for (i = 0; i < localsToSave.length; i++) {
        t = localsToSave[i];
        if (seenTemps[t]===undefined) {
            output += t + "=susp.$tmps." + t + ";";
            seenTemps[t] = true;
        }
    }

    output +=  "try { $ret=susp.child.resume(); } catch(err) { if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if($exc.length>0) { $err=err; $blk=$exc.pop(); } else { throw err; } }" +
                "};";

    output += "var $saveSuspension = function($child, $filename, $lineno, $colno) {" +
                "var susp = new Sk.misceval.Suspension(); susp.child=$child;" +
                "susp.resume=function(){"+unit.scopename+".$wakingSuspension=susp; return "+unit.scopename+"("+(unit.ste.generator?"$gen":"")+"); };" +
                "susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;" +
                "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;" +
                "susp.optional=susp.child.optional;" +
                (hasCell ? "susp.$cell=$cell;" : "");

    seenTemps = {};
    for (i = 0; i < localsToSave.length; i++) {
        t = localsToSave[i];
        if (seenTemps[t]===undefined) {
            localSaveCode.push("\"" + t + "\":" + t);
            seenTemps[t]=true;
        }
    }
    output +=   "susp.$tmps={" + localSaveCode.join(",") + "};" +
                "return susp;" +
              "};";

    return output;
};

Compiler.prototype.outputAllUnits = function () {
    var i;
    var blocks;
    var unit;
    var j;
    var ret = "";
    var block;
    var generatedBlocks;
    for (j = 0; j < this.allUnits.length; ++j) {
        unit = this.allUnits[j];
        ret += unit.prefixCode;
        ret += this.outputLocals(unit);
        if (unit.doesSuspend) {
            ret += this.outputSuspensionHelpers(unit);
        }
        ret += unit.varDeclsCode;
        ret += unit.switchCode;
        blocks = unit.blocks;
        generatedBlocks = Object.create(null);
        for (i = 0; i < blocks.length; ++i) {
            block = i;
            if (block in generatedBlocks) {continue;}
            while (true) {
                generatedBlocks[block] = true;

                ret += "case " + block + ": /* --- " + blocks[block]._name + " --- */";
                ret += blocks[block].join("");

                if (blocks[block]._next !== null) {
                    if (!(blocks[block]._next in generatedBlocks)) {
                        ret += "/* allowing case fallthrough */";
                        block = blocks[block]._next;
                    } else {
                        ret += "/* jump */ continue;";
                        break;
                    }
                } else {
                    ret += "throw new Sk.builtin.SystemError('internal error: unterminated block');";
                    break;
                }
            }
        }
        ret += unit.suffixCode;
    }
    return ret;
};

Compiler.prototype.cif = function (s) {
    var test;
    var next;
    var end;
    var constant;
    Sk.asserts.assert(s instanceof Sk.astnodes.If);
    constant = this.exprConstant(s.test);
    if (constant === 0) {
        if (s.orelse && s.orelse.length > 0) {
            this.vseqstmt(s.orelse);
        }
    } else if (constant === 1) {
        this.vseqstmt(s.body);
    } else {
        end = this.newBlock("end of if");
        if (s.orelse && s.orelse.length > 0) {
            next = this.newBlock("next branch of if");
        }

        test = this.vexpr(s.test);

        if (s.orelse && s.orelse.length > 0) {
            this._jumpfalse(test, next);
            this.vseqstmt(s.body);
            this._jump(end);

            this.setBlock(next);
            this.vseqstmt(s.orelse);
        } else {
            this._jumpfalse(test, end);
            this.vseqstmt(s.body);
        }
        this._jump(end);
        this.setBlock(end);
    }

};

Compiler.prototype.cwhile = function (s) {
    var body;
    var orelse;
    var next;
    var top;
    var constant = this.exprConstant(s.test);
    if (constant === 0) {
        if (s.orelse) {
            this.vseqstmt(s.orelse);
        }
    } else {
        top = this.newBlock("while test");
        this._jump(top);
        this.setBlock(top);

        next = this.newBlock("after while");
        orelse = s.orelse.length > 0 ? this.newBlock("while orelse") : null;
        body = this.newBlock("while body");

        this.annotateSource(s);
        this._jumpfalse(this.vexpr(s.test), orelse ? orelse : next);
        this._jump(body);

        this.pushBreakBlock(next);
        this.pushContinueBlock(top);

        this.setBlock(body);

        if ((Sk.debugging || Sk.killableWhile) && this.u.canSuspend) {
            var suspType = "Sk.delay";
            var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno);
            out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {",
                "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");",
                "$susp.$blk = "+debugBlock+";",
                "$susp.optional = true;",
                "return $susp;",
                "}");
            this._jump(debugBlock);
            this.setBlock(debugBlock);
            this.u.doesSuspend = true;
        }

        this.vseqstmt(s.body);

        this._jump(top);

        this.popContinueBlock();
        this.popBreakBlock();

        if (s.orelse.length > 0) {
            this.setBlock(orelse);
            this.vseqstmt(s.orelse);
            this._jump(next);
        }

        this.setBlock(next);
    }
};

Compiler.prototype.cfor = function (s) {
    var target;
    var nexti;
    var iter;
    var toiter;
    var start = this.newBlock("for start");
    var cleanup = this.newBlock("for cleanup");
    var end = this.newBlock("for end");

    this.pushBreakBlock(end);
    this.pushContinueBlock(start);

    // get the iterator
    toiter = this.vexpr(s.iter);
    if (this.u.ste.generator) {
        // if we're in a generator, we have to store the iterator to a local
        // so it's preserved (as we cross blocks here and assume it survives)
        iter = "$loc." + this.gensym("iter");
        out(iter, "=Sk.abstr.iter(", toiter, ");");
    } else {
        iter = this._gr("iter", "Sk.abstr.iter(", toiter, ")");
        this.u.tempsToSave.push(iter); // Save it across suspensions
    }

    this._jump(start);

    this.setBlock(start);

    // load targets
    out ("$ret = Sk.abstr.iternext(", iter,(this.u.canSuspend?", true":", false"),");");

    this._checkSuspension(s);

    nexti = this._gr("next", "$ret");
    this._jumpundef(nexti, cleanup); // todo; this should be handled by StopIteration
    target = this.vexpr(s.target, nexti);

    if ((Sk.debugging || Sk.killableFor) && this.u.canSuspend) {
        var suspType = "Sk.delay";
        var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno);
        out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {",
            "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");",
            "$susp.$blk = "+debugBlock+";",
            "$susp.optional = true;",
            "return $susp;",
            "}");
        this._jump(debugBlock);
        this.setBlock(debugBlock);
        this.u.doesSuspend = true;
    }

    // execute body
    this.vseqstmt(s.body);

    // jump to top of loop
    this._jump(start);

    this.setBlock(cleanup);
    this.popContinueBlock();
    this.popBreakBlock();

    this.vseqstmt(s.orelse);
    this._jump(end);

    this.setBlock(end);
};

Compiler.prototype.craise = function (s) {
    if (s.exc) {
        var exc = this._gr("exc", this.vexpr(s.exc));
        // This is tricky - we're supporting both the weird-ass semantics
        // of the Python 2 "raise (exc), (inst), (tback)" version,
        // plus the sensible Python "raise (exc) from (cause)".
        // ast.js takes care of ensuring that you can only use the right one
        // for the Python version you're using.

        var instantiatedException = this.newBlock("exception now instantiated");
        var isClass = this._gr("isclass", exc + ".prototype instanceof Sk.builtin.BaseException");
        this._jumpfalse(isClass, instantiatedException);
        //this._jumpfalse(instantiatedException, isClass);

        // Instantiate exc with inst
        if (s.inst) {
            var inst = this._gr("inst", this.vexpr(s.inst));
            out("if(!(",inst," instanceof Sk.builtin.tuple)) {",
                inst,"= new Sk.builtin.tuple([",inst,"]);",
                "}");
            out("$ret = Sk.misceval.callsimOrSuspendArray(",exc,",",inst,".v);");
        } else {
            out("$ret = Sk.misceval.callsimOrSuspend(",exc,");");
        }
        this._checkSuspension(s);
        out(exc,"=$ret;");

        this._jump(instantiatedException);

        this.setBlock(instantiatedException);

        // TODO TODO TODO set cause appropriately
        // (and perhaps traceback for py2 if we care before it gets fully deprecated)

        out("if (", exc, " instanceof Sk.builtin.BaseException) {throw ",exc,";} else {throw new Sk.builtin.TypeError('exceptions must derive from BaseException');};");
    } else {
        // re-raise
        out("throw $err;");
    }
};

Compiler.prototype.outputFinallyCascade = function (thisFinally) {
    var nextFinally;

    // What do we do when we're done executing a 'finally' block?
    // Normally you just fall off the end. If we're 'return'ing,
    // 'continue'ing or 'break'ing, $postfinally tells us what to do.
    //
    // But we might be in a nested pair of 'finally' blocks. If so, we need
    // to work out whether to jump to the outer finally block.
    //
    // (NB we do NOT deal with re-raising exceptions here. That's handled
    // elsewhere, because 'with' does special things with exceptions.)

    if (this.u.finallyBlocks.length == 0) {
        // No nested 'finally' block. Easy.
        out("if($postfinally!==undefined) { if ($postfinally.returning) { return $postfinally.returning; } else { $blk=$postfinally.gotoBlock; $postfinally=undefined; continue; } }");
    } else {

        // OK, we're nested. Do we jump straight to the outer 'finally' block?
        // Depends on how we got here here.

        // Normal execution ($postfinally===undefined)? No, we're done here.

        // Returning ($postfinally.returning)? Yes, we want to execute all the
        // 'finally' blocks on the way out.

        // Breaking ($postfinally.isBreak)? It depends. Is the outer 'finally'
        // block inside or outside the loop we're breaking out of? We compare
        // its breakDepth to ours to find out. If we're at the same breakDepth,
        // we're both inside the innermost loop, so we both need to execute.
        // ('continue' is the same thing as 'break' for us)

        nextFinally = this.peekFinallyBlock();

        out("if($postfinally!==undefined) {",
            "if ($postfinally.returning",
            (nextFinally.breakDepth == thisFinally.breakDepth) ? "|| $postfinally.isBreak" : "", ") {",

            "$blk=",nextFinally.blk,";continue;",
            "} else {",
            "$blk=$postfinally.gotoBlock;$postfinally=undefined;continue;",
            "}",
            "}");
    }
};

Compiler.prototype.ctry = function (s) {
    var check;
    var next;
    var handlertype;
    var handler;
    var end;
    var orelse;
    var unhandled;
    var i;
    var n = s.handlers.length;

    var finalBody, finalExceptionHandler, finalExceptionToReRaise;
    var thisFinally;

    if (s.finalbody) {
        finalBody = this.newBlock("finalbody");
        finalExceptionHandler = this.newBlock("finalexh");
        finalExceptionToReRaise = this._gr("finally_reraise", "undefined");

        this.u.tempsToSave.push(finalExceptionToReRaise);
        this.pushFinallyBlock(finalBody);
        thisFinally = this.peekFinallyBlock();
        this.setupExcept(finalExceptionHandler);
    }

    // Create a block for each except clause
    var handlers = [];
    for (i = 0; i < n; ++i) {
        handlers.push(this.newBlock("except_" + i + "_"));
    }

    unhandled = this.newBlock("unhandled");
    orelse = this.newBlock("orelse");
    end = this.newBlock("end");

    if (handlers.length != 0) {
        this.setupExcept(handlers[0]);
    }
    this.vseqstmt(s.body);
    if (handlers.length != 0) {
        this.endExcept();
    }
    this._jump(orelse);

    for (i = 0; i < n; ++i) {
        this.setBlock(handlers[i]);
        handler = s.handlers[i];
        if (!handler.type && i < n - 1) {
            throw new Sk.builtin.SyntaxError("default 'except:' must be last", this.filename, handler.lineno);
        }

        if (handler.type) {
            // should jump to next handler if err not isinstance of handler.type
            handlertype = this.vexpr(handler.type);
            next = (i == n - 1) ? unhandled : handlers[i + 1];

            // var isinstance = this.nameop(new Sk.builtin.str("isinstance"), Load));
            // var check = this._gr('call', "Sk.misceval.callsimArray(", isinstance, ", [$err, ", handlertype, "])");

            check = this._gr("instance", "Sk.misceval.isTrue(Sk.builtin.isinstance($err, ", handlertype, "))");
            this._jumpfalse(check, next);
        }

        if (handler.name) {
            this.vexpr(handler.name, "$err");
        }

        this.vseqstmt(handler.body);

        this._jump(end);
    }

    // If no except clause catches exception, throw it again
    this.setBlock(unhandled);
    out("throw $err;");

    this.setBlock(orelse);
    this.vseqstmt(s.orelse);
    this._jump(end);

    this.setBlock(end);
    // End of the try/catch/else segment
    if (s.finalbody) {
        this.endExcept();

        this._jump(finalBody);

        this.setBlock(finalExceptionHandler);
        // Exception handling also goes to the finally body,
        // stashing the original exception to re-raise
        out(finalExceptionToReRaise,"=$err;");
        this._jump(finalBody);

        this.setBlock(finalBody);
        this.popFinallyBlock();
        this.vseqstmt(s.finalbody);
        // If finalbody executes normally, AND we have an exception
        // to re-raise, we raise it.
        out("if(",finalExceptionToReRaise,"!==undefined) { throw ",finalExceptionToReRaise,";}");

        this.outputFinallyCascade(thisFinally);
        // Else, we continue from here.
    }
};

Compiler.prototype.cwith = function (s, itemIdx) {
    var mgr, exit, value, exception;
    var exceptionHandler = this.newBlock("withexh"), tidyUp = this.newBlock("withtidyup");
    var carryOn = this.newBlock("withcarryon");
    var thisFinallyBlock;

    // NB this does not *quite* match the semantics in PEP 343, which
    // specifies "exit = type(mgr).__exit__" rather than getattr()ing,
    // presumably for performance reasons.

    mgr = this._gr("mgr", this.vexpr(s.items[itemIdx].context_expr));

    // exit = mgr.__exit__
    out("$ret = Sk.abstr.lookupSpecial(",mgr,",Sk.builtin.str.$exit);");
    this._checkSuspension(s);
    exit = this._gr("exit", "$ret");
    this.u.tempsToSave.push(exit);

    // value = mgr.__enter__()
    out("$ret = Sk.abstr.lookupSpecial(",mgr,",Sk.builtin.str.$enter);");
    this._checkSuspension(s);
    out("$ret = Sk.misceval.callsimOrSuspendArray($ret);");
    this._checkSuspension(s);
    value = this._gr("value", "$ret");

    // try:
    this.pushFinallyBlock(tidyUp);
    thisFinallyBlock = this.u.finallyBlocks[this.u.finallyBlocks.length-1];
    this.setupExcept(exceptionHandler);

    //    VAR = value
    if (s.items[itemIdx].optional_vars) {
        this.nameop(s.items[itemIdx].optional_vars.id, Sk.astnodes.Store, value);
    }

    //    (try body)

    if (itemIdx +1 < s.items.length) {
        // "with" statements with multiple items (context managers) are
        // treated as nested "with" statements
        this.cwith(s, itemIdx + 1);
    } else {
        this.vseqstmt(s.body);
    }

    this.endExcept();
    this._jump(tidyUp);

    // except:
    this.setBlock(exceptionHandler);

    //   if not exit(*sys.exc_info()):
    //     raise
    out("$ret = Sk.misceval.applyOrSuspend(",exit,",undefined,Sk.builtin.getExcInfo($err),undefined,[]);");
    this._checkSuspension(s);
    this._jumptrue("$ret", carryOn);
    out("throw $err;");

    // finally: (kinda. NB that this is a "finally" that doesn't run in the
    //           exception case!)
    this.setBlock(tidyUp);
    this.popFinallyBlock();

    //   exit(None, None, None)
    out("$ret = Sk.misceval.callsimOrSuspendArray(",exit,",[Sk.builtin.none.none$,Sk.builtin.none.none$,Sk.builtin.none.none$]);");
    this._checkSuspension(s);
    // Ignore $ret.

    this.outputFinallyCascade(thisFinallyBlock);

    this._jump(carryOn);

    this.setBlock(carryOn);
};

Compiler.prototype.cassert = function (s) {
    /* todo; warnings method
     if (s.test instanceof Tuple && s.test.elts.length > 0)
     Sk.warn("assertion is always true, perhaps remove parentheses?");
     */

    var test = this.vexpr(s.test);
    var end = this.newBlock("end");
    this._jumptrue(test, end);
    // todo; exception handling
    // maybe replace with Sk.asserts.fail?? or just an alert?
    out("throw new Sk.builtin.AssertionError(", s.msg ? this.vexpr(s.msg) : "", ");");
    this.setBlock(end);
};

Compiler.prototype.cimportas = function (name, asname, mod) {
    var attr;
    var src = name.v;
    var dotLoc = src.indexOf(".");
    //print("src", src);
    //print("dotLoc", dotLoc);
    var cur = mod;
    if (dotLoc !== -1) {
        // if there's dots in the module name, __import__ will have returned
        // the top-level module. so, we need to extract the actual module by
        // getattr'ing up through the names, and then storing the leaf under
        // the name it was to be imported as.
        src = src.substr(dotLoc + 1);
        //print("src now", src);
        while (dotLoc !== -1) {
            dotLoc = src.indexOf(".");
            attr = dotLoc !== -1 ? src.substr(0, dotLoc) : src;
            cur = this._gr("lattr", "Sk.abstr.gattr(", cur, ", new Sk.builtin.str('", attr, "'))");
            src = src.substr(dotLoc + 1);
        }
    }
    return this.nameop(asname, Sk.astnodes.Store, cur);
};

Compiler.prototype.cimport = function (s) {
    var lastDot;
    var tmp;
    var mod;
    var alias;
    var i;
    var n = s.names.length;
    for (i = 0; i < n; ++i) {
        alias = s.names[i];
        out("$ret = Sk.builtin.__import__(", alias.name["$r"]().v, ",$gbl,$loc,[],",(Sk.__future__.absolute_import?0:-1),");");

        this._checkSuspension(s);

        mod = this._gr("module", "$ret");

        if (alias.asname) {
            this.cimportas(alias.name, alias.asname, mod);
        } else {
            tmp = alias.name;
            lastDot = tmp.v.indexOf(".");
            if (lastDot !== -1) {
                tmp = new Sk.builtin.str(tmp.v.substr(0, lastDot));
            }
            this.nameop(tmp, Sk.astnodes.Store, mod);
        }
    }
};

Compiler.prototype.cfromimport = function (s) {
    var storeName;
    var got;
    var alias;
    var aliasOut;
    var mod;
    var i;
    var n = s.names.length;
    var names = [];
    var level = s.level;
    if (level == 0 && !Sk.__future__.absolute_import) {
        level = -1;
    }
    for (i = 0; i < n; ++i) {
        names[i] = "'" + fixReserved(s.names[i].name.v) + "'";
    }
    out("$ret = Sk.builtin.__import__(", s.module["$r"]().v, ",$gbl,$loc,[", names, "],",level,");");

    this._checkSuspension(s);

    //out("print('__import__ returned ' + $ret);");
    //out("for (var x in $ret) { print(x); }");
    mod = this._gr("module", "$ret");
    for (i = 0; i < n; ++i) {
        alias = s.names[i];
        aliasOut = "'" + alias.name.v + "'";
        if (i === 0 && alias.name.v === "*") {
            Sk.asserts.assert(n === 1);
            out("Sk.importStar(", mod, ",$loc, $gbl);");
            return;
        }

        //out("print(\"getting Sk.abstr.gattr(", mod, ",", alias.name["$r"]().v, ")\");");
        got = this._gr("item", "Sk.abstr.gattr(", mod, ", new Sk.builtin.str(", aliasOut, "), undefined)");
        //out("print('got');");
        storeName = alias.name;
        if (alias.asname) {
            storeName = alias.asname;
        }
        this.nameop(storeName, Sk.astnodes.Store, got);
    }
};

/**
 * builds a code object (js function) for various constructs. used by def,
 * lambda, generator expressions. it isn't used for class because it seemed
 * different enough.
 *
 * handles:
 * - setting up a new scope
 * - decorators (if any)
 * - defaults setup
 * - setup for cell and free vars
 * - setup and modification for generators
 *
 * @param {Object} n ast node to build for
 * @param {Sk.builtin.str} coname name of code object to build
 * @param {Array} decorator_list ast of decorators if any
 * @param {Sk.astnodes.arguments_} args arguments to function, if any
 * @param {Function} callback called after setup to do actual work of function
 * @param {Sk.builtin.str=} class_for_super
 *
 * @returns the name of the newly created function or generator object.
 *
 */
Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, callback, class_for_super) {
    var containingHasFree;
    var frees;
    var argnamesarr = [];
    var start;
    var kw;
    var maxargs;
    var minargs;
    var id;
    var argname;
    var offset;
    var cells;
    var locals;
    var i;
    var funcArgs;
    var entryBlock;
    var hasCell;
    var hasFree;
    var isGenerator;
    var scopename;
    var decos = [];
    var defaults = [];
    var kw_defaults = [];
    var vararg = null;
    var kwarg = null;

    // decorators and defaults have to be evaluated out here before we enter
    // the new scope. we output the defaults and attach them to this code
    // object, but only once we know the name of it (so we do it after we've
    // exited the scope near the end of this function).
    if (decorator_list) {
        decos = this.vseqexpr(decorator_list);
    }
    if (args && args.defaults) {
        defaults = this.vseqexpr(args.defaults);
    }
    if (args && args.kw_defaults) {
        kw_defaults = args.kw_defaults.map(e => e ? this.vexpr(e) : "undefined");
    }
    if (args && args.vararg) {
        vararg = args.vararg;
    }
    if (args && args.kwarg) {
        kwarg = args.kwarg;
    }
    if (!Sk.__future__.python3 && args && args.kwonlyargs && args.kwonlyargs.length != 0) {
        throw new Sk.builtin.SyntaxError("Keyword-only arguments are not supported in Python 2");
    }

    //
    // enter the new scope, and create the first block
    //
    scopename = this.enterScope(coname, n, n.lineno, this.canSuspend);

    isGenerator = this.u.ste.generator;
    hasFree = this.u.ste.hasFree;
    hasCell = this.u.ste.childHasFree;

    entryBlock = this.newBlock("codeobj entry");

    //
    // the header of the function, and arguments
    //
    this.u.prefixCode = "var " + scopename + "=(function " + this.niceName(coname.v) + "$(";

    funcArgs = [];
    if (isGenerator) {
        // TODO make generators deal with arguments properly
        if (kwarg) {
            throw new Sk.builtin.SyntaxError(coname.v + "(): keyword arguments in generators not supported",
                                             this.filename, n.lineno);
        }
        if (vararg) {
            throw new Sk.builtin.SyntaxError(coname.v + "(): variable number of arguments in generators not supported",
                                             this.filename, n.lineno);
        }
        funcArgs.push("$gen");
    } else {
        if (kwarg) {
            funcArgs.push("$kwa");
            this.u.tempsToSave.push("$kwa");
        }
        for (i = 0; args && i < args.args.length; ++i) {
            funcArgs.push(this.nameop(args.args[i].arg, Sk.astnodes.Param));
        }
        for (i = 0; args && args.kwonlyargs && i < args.kwonlyargs.length; ++i) {
            funcArgs.push(this.nameop(args.kwonlyargs[i].arg, Sk.astnodes.Param));
        }
        if (vararg) {
            funcArgs.push(this.nameop(args.vararg.arg, Sk.astnodes.Param));
        }
    }
    // Are we using the new fast-call mechanism, where the
    // function we define implements the tp$call interface?
    // (Right now we haven't migrated generators because they're
    // a mess, but if this works we can move everything over)
    let fastCall = !isGenerator;

    if (hasFree) {
        if (!fastCall) {
            funcArgs.push("$free");
        }
        this.u.tempsToSave.push("$free");
    }

    if (fastCall) {
        this.u.prefixCode += "$posargs,$kwargs";
    } else {
        this.u.prefixCode += funcArgs.join(",");
    }

    this.u.prefixCode += "){";

    if (isGenerator) {
        this.u.prefixCode += "\n// generator\n";
    }
    if (hasFree) {
        this.u.prefixCode += "\n// has free\n";
    }
    if (hasCell) {
        this.u.prefixCode += "\n// has cell\n";
    }

    if (fastCall) {
        this.u.prefixCode += "\n// fast call\n";
    }

    //
    // set up standard dicts/variables
    //
    locals = "{}";
    if (isGenerator) {
        entryBlock = "$gen.gi$resumeat";
        locals = "$gen.gi$locals";
    }
    cells = ",$cell={}";
    if (hasCell) {
        if (isGenerator) {
            cells = ",$cell=$gen.gi$cells";
        }
    }

    // note special usage of 'this' to avoid having to slice globals into
    // all function invocations in call
    // (fastcall doesn't need to do this, as 'this' is the func object)
    this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=" +(fastCall?"this && this.func_globals":"this") + ((fastCall&&hasFree)?",$free=this && this.func_closure":"") + ",$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";
    if (Sk.execLimit !== null) {
        this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}";
    }
    if (Sk.yieldLimit !== null && this.u.canSuspend) {
        this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}";
    }

    //
    // If there is a suspension, resume from it. Otherwise, initialise
    // parameters appropriately.
    //
    this.u.varDeclsCode += "if ("+scopename+".$wakingSuspension!==undefined) { $wakeFromSuspension(); } else {";

    if (fastCall) {
        // Resolve our arguments from $posargs+$kwargs.
        // If we're posargs-only, we can handle the fast path
        // without even calling out
        if (!kwarg && !vararg && (!args || !args.kwonlyargs || args.kwonlyargs.length === 0)) {
            this.u.varDeclsCode += "var $args = ((!$kwargs || $kwargs.length===0) && $posargs.length===" + funcArgs.length + ") ? $posargs : this.$resolveArgs($posargs,$kwargs)";
        } else {
            this.u.varDeclsCode += "\nvar $args = this.$resolveArgs($posargs,$kwargs)\n";
        }
        for (let i=0; i < funcArgs.length; i++) {
            this.u.varDeclsCode += ","+funcArgs[i]+"=$args["+i+"]";
        }
        this.u.varDeclsCode += ";\n";
    }


    // TODO update generators to do their arg checks in outside generated code,
    // like functions do
    //
    // this could potentially get removed if generators would learn to deal with args, kw, kwargs, varargs
    // initialize default arguments. we store the values of the defaults to
    // this code object as .$defaults just below after we exit this scope.
    //
    if (isGenerator && defaults.length > 0) {
        // defaults have to be "right justified" so if there's less defaults
        // than args we offset to make them match up (we don't need another
        // correlation in the ast)
        offset = args.args.length - defaults.length;
        for (i = 0; i < defaults.length; ++i) {
            argname = this.nameop(args.args[i + offset].arg, Sk.astnodes.Param);
            this.u.varDeclsCode += "if(" + argname + "===undefined)" + argname + "=" + scopename + ".$defaults[" + i + "];";
        }
    }

    //
    // copy all parameters that are also cells into the cells dict. this is so
    // they can be accessed correctly by nested scopes.
    //
    for (i = 0; args && i < args.args.length; ++i) {
        id = args.args[i].arg;
        if (this.isCell(id)) {
            let mangled = fixReserved(mangleName(this.u.private_, id).v);
            this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";";
        }
    }
    for (i = 0; args && args.kwonlyargs && i < args.kwonlyargs.length; ++i) {
        id = args.kwonlyargs[i].arg;
        if (this.isCell(id)) {
            let mangled = fixReserved(mangleName(this.u.private_, id).v);
            this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";";
        }
    }
    if (vararg && this.isCell(vararg.arg)) {
        let mangled = fixReserved(mangleName(this.u.private_, vararg.arg).v);
        this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";";
    }

    //
    // initialize kwarg, if any
    //
    if (kwarg) {
        this.u.localnames.push(kwarg.arg.v);
        this.u.varDeclsCode += kwarg.arg.v + "=new Sk.builtins['dict']($kwa);";
        if (this.isCell(kwarg.arg)) {
            let mangled = fixReserved(mangleName(this.u.private_, kwarg.arg).v);
            this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";";
        }
    }

    //
    // close the else{} block from the wakingSuspension check
    //
    this.u.varDeclsCode += "}";

    // inject __class__ cell when running python3
    if (Sk.__future__.python3 && class_for_super) {
        this.u.varDeclsCode += "$gbl.__class__=$gbl." + class_for_super.v + ";";
    }

    // finally, set up the block switch that the jump code expects
    //
    // Old switch code
    // this.u.switchCode += "while(true){switch($blk){";
    // this.u.suffixCode = "}break;}});";

    // New switch code to catch exceptions
    this.u.switchCode = "while(true){try{";
    this.u.switchCode += this.outputInterruptTest();
    this.u.switchCode += "switch($blk){";
    this.u.suffixCode = "} }catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});";

    //
    // jump back to the handler so it can do the main actual work of the
    // function
    //
    callback.call(this, scopename);

    //
    // get a list of all the argument names (used to attach to the code
    // object, and also to allow us to declare only locals that aren't also
    // parameters).
    if (args) {
        for (let arg of args.args) {
            argnamesarr.push(arg.arg.v);
        }
        for (let arg of args.kwonlyargs || []) {
            argnamesarr.push(arg.arg.v);
        }

        // store to unit so we know what local variables not to declare
        this.u.argnames = argnamesarr;
    }

    //
    // and exit the code object scope
    //
    this.exitScope();

    //
    // attach the default values we evaluated at the beginning to the code
    // object so that it can get at them to set any arguments that are left
    // unset.
    //
    if (defaults.length > 0) {
        out(scopename, ".$defaults=[", defaults.join(","), "];");
    }
    if (args && args.kwonlyargs && args.kwonlyargs.length > 0) {
        out(scopename, ".co_argcount=", args.args.length, ";");
        out(scopename, ".co_kwonlyargcount=", args.kwonlyargs.length, ";");
        out(scopename, ".$kwdefs=[", kw_defaults.join(","), "];");
    }

    //
    // attach co_varnames (only the argument names) for keyword argument
    // binding.
    //
    if (argnamesarr.length > 0) {
        out(scopename, ".co_varnames=['", argnamesarr.join("','"), "'];");
    } else {
        out(scopename, ".co_varnames=[];");
    }

    //
    // Skulpt doesn't have "co_consts", so record the docstring (or
    // None) in the "co_docstring" property of the code object, ready
    // for use by the Sk.builtin.func constructor.
    //
    out(scopename, ".co_docstring=", this.cDocstringOfCode(n), ";");

    //
    // attach flags
    //
    if (kwarg) {
        out(scopename, ".co_kwargs=1;");
    }
    if (vararg) {
        out(scopename, ".co_varargs=1;");
    }
    if (!isGenerator) {
        out(scopename, ".co_fastcall=1;");
    }

    //
    // build either a 'function' or 'generator'. the function is just a simple
    // constructor call. the generator is more complicated. it needs to make a
    // new generator every time it's called, so the thing that's returned is
    // actually a function that makes the generator (and passes arguments to
    // the function onwards to the generator). this should probably actually
    // be a function object, rather than a js function like it is now. we also
    // have to build the argument names to pass to the generator because it
    // needs to store all locals into itself so that they're maintained across
    // yields.
    //
    // todo; possibly this should be outside?
    //
    frees = "";
    if (hasFree) {
        frees = ",$cell";
        // if the scope we're in where we're defining this one has free
        // vars, they may also be cell vars, so we pass those to the
        // closure too.
        containingHasFree = this.u.ste.hasFree;
        if (containingHasFree) {
            frees += ",$free";
        }
    }
    if (isGenerator) {
    // Keyword and variable arguments are not currently supported in generators.
    // The call to pyCheckArgs assumes they can't be true.
        if (args && args.args.length > 0) {
            return this._gr("gener", "new Sk.builtins['function']((function(){var $origargs=Array.prototype.slice.call(arguments);Sk.builtin.pyCheckArgsLen(\"",
                            coname.v, "\",arguments.length,", args.args.length - defaults.length, ",", args.args.length,
                            ");return new Sk.builtins['generator'](", scopename, ",$gbl,$origargs", frees, ");}))");
        } else {
            return this._gr("gener", "new Sk.builtins['function']((function(){Sk.builtin.pyCheckArgsLen(\"", coname.v,
                            "\",arguments.length,0,0);return new Sk.builtins['generator'](", scopename, ",$gbl,[]", frees, ");}))");
        }
    } else {
        if (decos.length > 0) {
            out("$ret = new Sk.builtins['function'](", scopename, ",$gbl", frees, ");");
            for (let decorator of decos.reverse()) {
                out("$ret = Sk.misceval.callsimOrSuspendArray(", decorator, ",[$ret]);");
                this._checkSuspension();
            }
            return this._gr("funcobj", "$ret");
        }

        return this._gr("funcobj", "new Sk.builtins['function'](", scopename, ",$gbl", frees, ")");
    }
};

/** JavaScript for the docstring of the given body, or null if the
 * body has no docstring.
 */
Compiler.prototype.maybeCDocstringOfBody = function(body) {
    if (body.length === 0)  // Don't think this can happen?
        return null;

    const stmt_0 = body[0];
    if (stmt_0.constructor !== Sk.astnodes.Expr)
        return null;

    const expr = stmt_0.value;
    if (expr.constructor !== Sk.astnodes.Str)
        return null;

    return this.vexpr(expr);
};

/** JavaScript for the docstring of the given node.  Only called from
 * buildcodeobj(), and expects a FunctionDef, Lambda, or GeneratorExp
 * node.  We give a "None" docstring to a GeneratorExp node, although
 * it is not carried over to the final generator; this is harmless.
 */
Compiler.prototype.cDocstringOfCode = function(node) {
    switch (node.constructor) {
    case Sk.astnodes.AsyncFunctionDef:  // For when it's supported
    case Sk.astnodes.FunctionDef:
        return (
            this.maybeCDocstringOfBody(node.body)
            || "Sk.builtin.none.none$"
        );

    case Sk.astnodes.Lambda:
    case Sk.astnodes.GeneratorExp:
        return "Sk.builtin.none.none$";

    default:
        Sk.asserts.fail(`unexpected node kind ${node.constructor.name}`);
    }
}

Compiler.prototype.cfunction = function (s, class_for_super) {
    var funcorgen;
    Sk.asserts.assert(s instanceof Sk.astnodes.FunctionDef);
    funcorgen = this.buildcodeobj(s, s.name, s.decorator_list, s.args, function (scopename) {
        this.vseqstmt(s.body);
        out("return Sk.builtin.none.none$;"); // if we fall off the bottom, we want the ret to be None
    }, class_for_super);
    this.nameop(s.name, Sk.astnodes.Store, funcorgen);
};

Compiler.prototype.clambda = function (e) {
    var func;
    Sk.asserts.assert(e instanceof Sk.astnodes.Lambda);
    func = this.buildcodeobj(e, new Sk.builtin.str("<lambda>"), null, e.args, function (scopename) {
        var val = this.vexpr(e.body);
        out("return ", val, ";");
    });
    return func;
};

Compiler.prototype.cifexp = function (e) {
    var next = this.newBlock("next of ifexp");
    var end = this.newBlock("end of ifexp");
    var ret = this._gr("res", "null");

    var test = this.vexpr(e.test);
    this._jumpfalse(test, next);

    out(ret, "=", this.vexpr(e.body), ";");
    this._jump(end);

    this.setBlock(next);
    out(ret, "=", this.vexpr(e.orelse), ";");
    this._jump(end);

    this.setBlock(end);
    return ret;
};

Compiler.prototype.cgenexpgen = function (generators, genIndex, elt) {
    var velt;
    var ifres;
    var i;
    var n;
    var target;
    var nexti;
    var toiter;
    var start = this.newBlock("start for " + genIndex);
    var skip = this.newBlock("skip for " + genIndex);
    var ifCleanup = this.newBlock("if cleanup for " + genIndex);
    var end = this.newBlock("end for " + genIndex);

    var ge = generators[genIndex];

    var iter;
    if (genIndex === 0) {
        // the outer most iterator is evaluated in the scope outside so we
        // have to evaluate it outside and store it into the generator as a
        // local, which we retrieve here.
        iter = "$loc.$iter0";
    } else {
        toiter = this.vexpr(ge.iter);
        iter = "$loc." + this.gensym("iter");
        out(iter, "=", "Sk.abstr.iter(", toiter, ");");
    }
    this._jump(start);
    this.setBlock(start);

    this.annotateSource(elt);

    // load targets
    out ("$ret = Sk.abstr.iternext(", iter,(this.u.canSuspend?", true":", false"),");");

    this._checkSuspension(elt);

    nexti = this._gr("next", "$ret");
    this._jumpundef(nexti, end); // todo; this should be handled by StopIteration
    target = this.vexpr(ge.target, nexti);

    n = ge.ifs ? ge.ifs.length : 0;
    for (i = 0; i < n; ++i) {
        this.annotateSource(ge.ifs[i]);

        ifres = this.vexpr(ge.ifs[i]);
        this._jumpfalse(ifres, start);
    }

    if (++genIndex < generators.length) {
        this.cgenexpgen(generators, genIndex, elt);
    }

    if (genIndex >= generators.length) {
        this.annotateSource(elt);

        velt = this.vexpr(elt);
        out("return [", skip, "/*resume*/,", velt, "/*ret*/];");
        this.setBlock(skip);
    }

    this._jump(start);

    this.setBlock(end);

    if (genIndex === 1) {
        out("return Sk.builtin.none.none$;");
    }
};

Compiler.prototype.cgenexp = function (e) {
    var gen = this.buildcodeobj(e, new Sk.builtin.str("<genexpr>"), null, null, function (scopename) {
        this.cgenexpgen(e.generators, 0, e.elt);
    });

    // call the generator maker to get the generator. this is kind of dumb,
    // but the code builder builds a wrapper that makes generators for normal
    // function generators, so we just do it outside (even just new'ing it
    // inline would be fine).
    var gener = this._gr("gener", "Sk.misceval.callsimArray(", gen, ");");
    // stuff the outermost iterator into the generator after evaluating it
    // outside of the function. it's retrieved by the fixed name above.
    out(gener, ".gi$locals.$iter0=Sk.abstr.iter(", this.vexpr(e.generators[0].iter), ");");
    return gener;
};


Compiler.prototype.cclass = function (s) {
    var wrapped;
    var entryBlock;
    var scopename;
    var bases;
    var decos;
    Sk.asserts.assert(s instanceof Sk.astnodes.ClassDef);

    decos = this.vseqexpr(s.decorator_list);

    bases = this.vseqexpr(s.bases);

    scopename = this.enterScope(s.name, s, s.lineno);
    entryBlock = this.newBlock("class entry");

    this.u.prefixCode = "var " + scopename + "=(function $" + s.name.v + "$class_outer($globals,$locals,$cell){var $gbl=$globals,$loc=$locals;$free=$globals;";
    this.u.switchCode += "(function $" + s.name.v + "$_closure($cell){";
    this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";

    if (Sk.execLimit !== null) {
        this.u.switchCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}";
    }
    if (Sk.yieldLimit !== null && this.u.canSuspend) {
        this.u.switchCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}";
    }

    this.u.switchCode += "while(true){try{";
    this.u.switchCode += this.outputInterruptTest();
    this.u.switchCode += "switch($blk){";
    this.u.suffixCode = "}}catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}";
    this.u.suffixCode += "}).call(null, $cell);});";

    this.u.private_ = s.name;

    this.cbody(s.body, s.name);
    out("return;");

    // build class

    this.exitScope();

    // todo; metaclass
    out("$ret = Sk.misceval.buildClass($gbl,", scopename, ",", s.name["$r"]().v, ",[", bases, "], $cell);");

    // apply decorators

    for (let decorator of decos) {
        out("$ret = Sk.misceval.callsimOrSuspendArray(", decorator, ", [$ret]);");
        this._checkSuspension();
    }

    // store our new class under the right name
    this.nameop(s.name, Sk.astnodes.Store, "$ret");
};

Compiler.prototype.ccontinue = function (s) {
    var nextFinally = this.peekFinallyBlock(), gotoBlock;
    if (this.u.continueBlocks.length == 0) {
        throw new Sk.builtin.SyntaxError("'continue' outside loop", this.filename, s.lineno);
    }
    // todo; continue out of exception blocks
    gotoBlock = this.u.continueBlocks[this.u.continueBlocks.length - 1];
    Sk.asserts.assert(this.u.breakBlocks.length === this.u.continueBlocks.length);
    if (nextFinally && nextFinally.breakDepth == this.u.continueBlocks.length) {
        out("$postfinally={isBreak:true,gotoBlock:",gotoBlock,"};");
    } else {
        this._jump(gotoBlock);
    }
};

Compiler.prototype.cbreak = function (s) {
    var nextFinally = this.peekFinallyBlock(), gotoBlock;

    if (this.u.breakBlocks.length === 0) {
        throw new Sk.builtin.SyntaxError("'break' outside loop", this.filename, s.lineno);
    }
    gotoBlock = this.u.breakBlocks[this.u.breakBlocks.length - 1];
    if (nextFinally && nextFinally.breakDepth == this.u.breakBlocks.length) {
        out("$postfinally={isBreak:true,gotoBlock:",gotoBlock,"};");
    } else {
        this._jump(gotoBlock);
    }
};

/**
 * compiles a statement
 * @param {Object} s
 * @param {Sk.builtin.str=} class_for_super
 */
Compiler.prototype.vstmt = function (s, class_for_super) {
    var i;
    var val;
    var n;
    var debugBlock;
    this.u.lineno = s.lineno;
    this.u.linenoSet = false;
    this.u.localtemps = [];

    if (Sk.debugging && this.u.canSuspend) {
        debugBlock = this.newBlock("debug breakpoint for line "+s.lineno);
        out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {",
            "var $susp = $saveSuspension({data: {type: 'Sk.debug'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");",
            "$susp.$blk = " + debugBlock + ";",
            "$susp.optional = true;",
            "return $susp;",
            "}");
        this._jump(debugBlock);
        this.setBlock(debugBlock);
        this.u.doesSuspend = true;
    }

    this.annotateSource(s);

    switch (s.constructor) {
        case Sk.astnodes.FunctionDef:
            this.cfunction(s, class_for_super);
            break;
        case Sk.astnodes.ClassDef:
            this.cclass(s);
            break;
        case Sk.astnodes.Return:
            if (this.u.ste.blockType !== Sk.SYMTAB_CONSTS.FunctionBlock) {
                throw new Sk.builtin.SyntaxError("'return' outside function", this.filename, s.lineno);
            }
            val = s.value ? this.vexpr(s.value) : "Sk.builtin.none.none$";
            if (this.u.finallyBlocks.length == 0) {
                out("return ", val, ";");
            } else {
                out("$postfinally={returning:",val,"};");
                this._jump(this.peekFinallyBlock().blk);
            }
            break;
        case Sk.astnodes.Delete:
            this.vseqexpr(s.targets);
            break;
        case Sk.astnodes.Assign:
            n = s.targets.length;
            val = this.vexpr(s.value);
            for (i = 0; i < n; ++i) {
                this.vexpr(s.targets[i], val);
            }
            break;
        case Sk.astnodes.AnnAssign:
            val = this.vexpr(s.value);
            this.vexpr(s.target, val);
            this.vexpr(s.annotation);
            break;
        case Sk.astnodes.AugAssign:
            return this.caugassign(s);
        case Sk.astnodes.Print:
            this.cprint(s);
            break;
        case Sk.astnodes.For:
            return this.cfor(s);
        case Sk.astnodes.While:
            return this.cwhile(s);
        case Sk.astnodes.If:
            return this.cif(s);
        case Sk.astnodes.Raise:
            return this.craise(s);
        case Sk.astnodes.Try:
            return this.ctry(s);
        case Sk.astnodes.With:
            return this.cwith(s, 0);
        case Sk.astnodes.Assert:
            return this.cassert(s);
        case Sk.astnodes.Import:
            return this.cimport(s);
        case Sk.astnodes.ImportFrom:
            return this.cfromimport(s);
        case Sk.astnodes.Global:
            break;
        case Sk.astnodes.Expr:
            this.vexpr(s.value);
            break;
        case Sk.astnodes.Pass:
            break;
        case Sk.astnodes.Break:
            this.cbreak(s);
            break;
        case Sk.astnodes.Continue:
            this.ccontinue(s);
            break;
        case Sk.astnodes.Debugger:
            out("debugger;");
            break;
        default:
            Sk.asserts.fail("unhandled case in vstmt: " + JSON.stringify(s));
    }
};

Compiler.prototype.vseqstmt = function (stmts) {
    var i;
    for (i = 0; i < stmts.length; ++i) {
        this.vstmt(stmts[i]);
    }
};

var OP_FAST = 0;
var OP_GLOBAL = 1;
var OP_DEREF = 2;
var OP_NAME = 3;
var D_NAMES = 0;
var D_FREEVARS = 1;
var D_CELLVARS = 2;

Compiler.prototype.isCell = function (name) {
    var mangled = fixReserved(mangleName(this.u.private_, name).v);
    var scope = this.u.ste.getScope(mangled);
    var dict = null;
    return scope === Sk.SYMTAB_CONSTS.CELL;

};

/**
 * @param {Sk.builtin.str} name
 * @param {Object} ctx
 * @param {string=} dataToStore
 */
Compiler.prototype.nameop = function (name, ctx, dataToStore) {
    var v;
    var mangledNoPre;
    var dict;
    var scope;
    var optype;
    var op;
    var mangled;
    if ((ctx === Sk.astnodes.Store || ctx === Sk.astnodes.AugStore || ctx === Sk.astnodes.Del) && name.v === "__debug__") {
        throw new Sk.builtin.SyntaxError("can not assign to __debug__", this.filename, this.u.lineno);
    }
    Sk.asserts.assert(name.v !== "None");

    if (name.v === "NotImplemented") {
        return "Sk.builtin.NotImplemented.NotImplemented$";
    }

    mangled = mangleName(this.u.private_, name).v;
    // Have to do this before looking it up in the scope
    mangled = fixReserved(mangled);
    op = 0;
    optype = OP_NAME;
    scope = this.u.ste.getScope(mangled);
    dict = null;
    switch (scope) {
        case Sk.SYMTAB_CONSTS.FREE:
            dict = "$free";
            optype = OP_DEREF;
            break;
        case Sk.SYMTAB_CONSTS.CELL:
            dict = "$cell";
            optype = OP_DEREF;
            break;
        case Sk.SYMTAB_CONSTS.LOCAL:
            // can't do FAST in generators or at module/class scope
            if (this.u.ste.blockType === Sk.SYMTAB_CONSTS.FunctionBlock && !this.u.ste.generator) {
                optype = OP_FAST;
            }
            break;
        case Sk.SYMTAB_CONSTS.GLOBAL_IMPLICIT:
            if (this.u.ste.blockType === Sk.SYMTAB_CONSTS.FunctionBlock) {
                optype = OP_GLOBAL;
            }
            break;
        case Sk.SYMTAB_CONSTS.GLOBAL_EXPLICIT:
            optype = OP_GLOBAL;
        default:
            break;
    }


    //print("mangled", mangled);
    // TODO TODO TODO todo; import * at global scope failing here
    Sk.asserts.assert(scope || name.v.charAt(1) === "_");

    // in generator or at module scope, we need to store to $loc, rather that
    // to actual JS stack variables.
    mangledNoPre = mangled;
    if (this.u.ste.generator || this.u.ste.blockType !== Sk.SYMTAB_CONSTS.FunctionBlock) {
        mangled = "$loc." + mangled;
    } else if (optype === OP_FAST || optype === OP_NAME) {
        this.u.localnames.push(mangled);
    }

    switch (optype) {
        case OP_FAST:
            switch (ctx) {
                case Sk.astnodes.Load:
                case Sk.astnodes.Param:
                    // Need to check that it is bound!
                    out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n");
                    return mangled;
                case Sk.astnodes.Store:
                    out(mangled, "=", dataToStore, ";");
                    break;
                case Sk.astnodes.Del:
                    out("delete ", mangled, ";");
                    break;
                default:
                    Sk.asserts.fail("unhandled");
            }
            break;
        case OP_NAME:
            switch (ctx) {
                case Sk.astnodes.Load:
                    // can't be || for loc.x = 0 or null
                    return this._gr("loadname", mangled, "!==undefined?", mangled, ":Sk.misceval.loadname('", mangledNoPre, "',$gbl);");
                case Sk.astnodes.Store:
                    out(mangled, "=", dataToStore, ";");
                    break;
                case Sk.astnodes.Del:
                    out("delete ", mangled, ";");
                    break;
                case Sk.astnodes.Param:
                    return mangled;
                default:
                    Sk.asserts.fail("unhandled");
            }
            break;
        case OP_GLOBAL:
            switch (ctx) {
                case Sk.astnodes.Load:
                    return this._gr("loadgbl", "Sk.misceval.loadname('", mangledNoPre, "',$gbl)");
                case Sk.astnodes.Store:
                    out("$gbl.", mangledNoPre, "=", dataToStore, ";");
                    break;
                case Sk.astnodes.Del:
                    out("delete $gbl.", mangledNoPre);
                    break;
                default:
                    Sk.asserts.fail("unhandled case in name op_global");
            }
            break;
        case OP_DEREF:
            switch (ctx) {
                case Sk.astnodes.Load:
                    return dict + "." + mangledNoPre;
                case Sk.astnodes.Store:
                    out(dict, ".", mangledNoPre, "=", dataToStore, ";");
                    break;
                case Sk.astnodes.Param:
                    return mangledNoPre;
                default:
                    Sk.asserts.fail("unhandled case in name op_deref");
            }
            break;
        default:
            Sk.asserts.fail("unhandled case");
    }
};

/**
 * @param {Sk.builtin.str} name
 * @param {Object} key
 * @param {number} lineno
 * @param {boolean=} canSuspend
 */
Compiler.prototype.enterScope = function (name, key, lineno, canSuspend) {
    var scopeName;
    var u = new CompilerUnit();
    u.ste = this.st.getStsForAst(key);
    u.name = name;
    u.firstlineno = lineno;
    u.canSuspend = canSuspend || false;

    if (this.u && this.u.private_) {
        u.private_ = this.u.private_;
    }

    this.stack.push(this.u);
    this.allUnits.push(u);
    scopeName = this.gensym("scope");
    u.scopename = scopeName;

    this.u = u;
    this.u.activateScope();

    this.nestlevel++;

    return scopeName;
};

Compiler.prototype.exitScope = function () {
    var mangled;
    var prev = this.u;
    this.nestlevel--;
    if (this.stack.length - 1 >= 0) {
        this.u = this.stack.pop();
    } else {
        this.u = null;
    }
    if (this.u) {
        this.u.activateScope();
    }

    if (prev.name.v !== "<module>") {// todo; hacky
        mangled = prev.name["$r"]().v;
        mangled = mangled.substring(1, mangled.length - 1);
        out(prev.scopename, ".co_name=new Sk.builtins['str']('", mangled, "');");
        if (this.stack.length && this.u.ste.blockType == "class") {
            const classname = this.u.name.v;
            out(prev.scopename, ".co_qualname=new Sk.builtins['str']('"+classname+ "." + mangled + "');");
        }
    }
    for (var constant in prev.consts) {
        if (prev.consts.hasOwnProperty(constant)) {
            prev.suffixCode += constant + " = " + prev.consts[constant] + ";";
        }
    }
};

/**
 * @param {Array} stmts
 * @param {Sk.builtin.str=} class_for_super
 */
Compiler.prototype.cbody = function (stmts, class_for_super) {
    var i = 0;

    // If we have a docstring, then assign it to __doc__, and skip over
    // the expression when properly compiling the rest of the body.  This
    // happens for class and module bodies.
    //
    const maybeDocstring = this.maybeCDocstringOfBody(stmts);
    if (maybeDocstring !== null) {
        out("$loc.__doc__ = ", maybeDocstring, ";");
        i = 1;
    }

    for (; i < stmts.length; ++i) {
        this.vstmt(stmts[i], class_for_super);
    }
};

Compiler.prototype.cprint = function (s) {
    var i;
    var n;
    var dest;
    Sk.asserts.assert(s instanceof Sk.astnodes.Print);
    dest = "null";
    if (s.dest) {
        dest = this.vexpr(s.dest);
    }

    n = s.values.length;
    // todo; dest disabled
    for (i = 0; i < n; ++i) {
        out("$ret = Sk.misceval.print_(", /*dest, ',',*/ "new Sk.builtins['str'](", this.vexpr(s.values[i]), ").v);");
        this._checkSuspension(s);
    }
    if (s.nl) {
        out("$ret = Sk.misceval.print_(", /*dest, ',*/ "\"\\n\");");
        this._checkSuspension(s);
    }

};

Compiler.prototype.cmod = function (mod) {
    //print("-----");
    //print(Sk.astDump(mod));
    var modf = this.enterScope(new Sk.builtin.str("<module>"), mod, 0, this.canSuspend);

    var entryBlock = this.newBlock("module entry");
    this.u.prefixCode = "var " + modf + "=(function($forcegbl){";
    this.u.varDeclsCode =
        "var $gbl = $forcegbl || {}, $blk=" + entryBlock +
        ",$exc=[],$loc=$gbl,$cell={},$err=undefined;" +
        "$loc.__file__=new Sk.builtins.str('" + this.filename +
        "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";

    if (Sk.execLimit !== null) {
        this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}";
    }

    if (Sk.yieldLimit !== null && this.u.canSuspend) {
        this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}";
    }

    this.u.varDeclsCode += "if ("+modf+".$wakingSuspension!==undefined) { $wakeFromSuspension(); }" +
        "if (Sk.retainGlobals) {" +
        "    if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; }" +
        "    if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; $loc.__file__=new Sk.builtins.str('" + this.filename + "');}" +
        "    else { Sk.globals = $gbl; }" +
        "} else { Sk.globals = $gbl; }";

    // Add the try block that pops the try/except stack if one exists
    // Github Issue #38
    // Google Code Issue: 109 / 114

    // Old code:
    //this.u.switchCode = "while(true){switch($blk){";
    //this.u.suffixCode = "}}});";

    // New Code:
    this.u.switchCode = "while(true){try{";
    this.u.switchCode += this.outputInterruptTest();
    this.u.switchCode += "switch($blk){";
    this.u.suffixCode = "}";
    this.u.suffixCode += "}catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });";

    // Note - this change may need to be adjusted for all the other instances of
    // switchCode and suffixCode in this file.  Not knowing how to test those
    // other cases I left them alone.   At least the changes to
    // setupExcept and endExcept will insure that the generated JavaScript
    // will be syntactically correct.  The worst that will happen is that when
    // code in a try block blows up, we will not know to run the except block.
    // The other problem is that we might catch something that is really an internal
    // error - it might be nice to add code in the above catch block that looked at
    // the kind of exception and only popped the stack for exceptions that are
    // from the original code rather than artifacts of some code generation or
    // exeution environment error.  We at least err on the side of exceptions
    // being revealed to the user.  drchuck - Wed Jan 23 19:20:18 EST 2013

    switch (mod.constructor) {
        case Sk.astnodes.Module:
            this.cbody(mod.body);
            out("return $loc;");
            break;
        default:
            Sk.asserts.fail("todo; unhandled case in compilerMod");
    }
    this.exitScope();

    this.result.push(this.outputAllUnits());
    return modf;
};

/**
 * @param {string} source the code
 * @param {string} filename where it came from
 * @param {string} mode one of 'exec', 'eval', or 'single'
 * @param {boolean=} canSuspend if the generated code supports suspension
 */
Sk.compile = function (source, filename, mode, canSuspend) {
    //print("FILE:", filename);
    // __future__ flags can be set from code
    // (with "from __future__ import ..." statements),
    // so make a temporary object that can be edited.
    var savedFlags = Sk.__future__;
    Sk.__future__ = Object.create(Sk.__future__);

    var parse = Sk.parse(filename, source);
    var ast = Sk.astFromParse(parse.cst, filename, parse.flags);
    // console.log(JSON.stringify(ast, undefined, 2));

    // compilers flags, later we can add other ones too
    var flags = {};
    flags.cf_flags = parse.flags;

    var st = Sk.symboltable(ast, filename);
    var c = new Compiler(filename, st, flags.cf_flags, canSuspend, source); // todo; CO_xxx
    var funcname = c.cmod(ast);

    // Restore the global __future__ flags
    Sk.__future__ = savedFlags;

    var ret = "$compiledmod = function() {" + c.result.join("") + "\nreturn " + funcname + ";}();";
    return {
        funcname: "$compiledmod",
        code    : ret
    };
};

Sk.exportSymbol("Sk.compile", Sk.compile);

Sk.resetCompiler = function () {
    Sk.gensymcount = 0;
};

Sk.exportSymbol("Sk.resetCompiler", Sk.resetCompiler);

Sk.fixReserved = fixReserved;
Sk.exportSymbol("Sk.fixReserved", Sk.fixReserved);

Sk.unfixReserved = unfixReserved;
Sk.exportSymbol("Sk.unfixReserved", Sk.unfixReserved);

Sk.mangleName = mangleName;
Sk.exportSymbol("Sk.mangleName", Sk.mangleName);

Sk.reservedWords_ = reservedWords_;
Sk.exportSymbol("Sk.reservedWords_", Sk.reservedWords_);


/***/ }),

/***/ "./src/complex.js":
/*!************************!*\
  !*** ./src/complex.js ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @description
 * see [Cpython complex_new](https://hg.python.org/cpython/file/f0e2caad4200/Objects/complexobject.c#l911)
 * @constructor
 * @param {number} real part of the complex number
 * @param {number} imag part of the complex number
 *
 * Prefering here == instead of ===, otherwise also undefined has to be matched explicitly
 * @extends {Sk.builtin.object}
 *
 */
Sk.builtin.complex = Sk.abstr.buildNativeClass("complex", {
    constructor: function complex(real, imag) {
        Sk.asserts.assert(this instanceof Sk.builtin.complex, "bad call to complex constructor, use 'new'");
        this.real = real;
        this.imag = imag;
    },
    slots: /**@lends {Sk.builtin.complex.prototype}*/ {
        tp$as_number: true,
        tp$doc:
            "Create a complex number from a real part and an optional imaginary part.\n\nThis is equivalent to (real + imag*1j) where imag defaults to 0.",
        tp$hash() {
            // _PyHASH_IMAG refers to _PyHASH_MULTIPLIER which refers to 1000003
            const real_hash = new Sk.builtin.float_(this.real).tp$hash();
            const imag_hash = new Sk.builtin.float_(this.imag).tp$hash();
            const v = imag_hash * 1003 + real_hash;
            if (Sk.builtin.int_.withinThreshold(v)) {
                return v;
            }
            return new Sk.builtin.int_(JSBI.BigInt(v)).tp$hash();
        },
        tp$getattr: Sk.generic.getAttr,
        tp$new(args, kwargs) {
            args = Sk.abstr.copyKeywordsToNamedArgs("complex", ["real", "imag"], args, kwargs, [null, null]);
            return complex_from_py.call(this, args[0], args[1]);
        },
        tp$richcompare(w, op) {
            if (op !== "Eq" && op !== "NotEq") {
                if (Sk.builtin.checkNumber(w) || _complex_check(w)) {
                    throw new Sk.builtin.TypeError("no ordering relation is defined for complex numbers");
                }
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            return complexNumberSlot(function (a_real, a_imag, b_real, b_imag) {
                const equal = a_real == b_real && a_imag == b_imag;
                return op === "Eq" ? equal : !equal;
            }, true).call(this, w);
        },
        $r() {
            return complex_format(this, null, "g");
        },

        // number slots
        nb$int() {
            throw new Sk.builtin.TypeError("can't convert complex to int");
        },
        nb$long() {
            throw new Sk.builtin.TypeError("can't convert complex to long");
        },
        nb$float() {
            throw new Sk.builtin.TypeError("can't convert complex to float");
        },
        nb$positive() {
            return new Sk.builtin.complex(this.real, this.imag);
        },
        nb$negative() {
            return new Sk.builtin.complex(-this.real, -this.imag);
        },
        nb$bool() {
            return this.real || this.imag;
        },
        nb$add: complexNumberSlot((a_real, a_imag, b_real, b_imag) => {
            return new Sk.builtin.complex(a_real + b_real, a_imag + b_imag);
        }),
        nb$subtract: complexNumberSlot((a_real, a_imag, b_real, b_imag) => {
            return new Sk.builtin.complex(a_real - b_real, a_imag - b_imag);
        }),
        nb$reflected_subtract: complexNumberSlot((a_real, a_imag, b_real, b_imag) => {
            return new Sk.builtin.complex(b_real - a_real, b_imag - a_imag);
        }),
        nb$multiply: complexNumberSlot((a_real, a_imag, b_real, b_imag) => {
            return new Sk.builtin.complex(b_real * a_real - b_imag * a_imag, a_real * b_imag + a_imag * b_real);
        }),
        nb$divide: complexNumberSlot(divide),
        nb$reflected_divide: complexNumberSlot((a_real, a_imag, b_real, b_imag) => {
            return divide(b_real, b_imag, a_real, a_imag);
        }),
        nb$floor_divide(other) {
            throw new Sk.builtin.TypeError("can't take floor of complex number.");
        },
        nb$reflected_floor_divide(other) {
            throw new Sk.builtin.TypeError("can't take floor of complex number.");
        },
        nb$remainder(other) {
            throw new Sk.builtin.TypeError("can't mod complex numbers.");
        },
        nb$reflected_remainder(other) {
            throw new Sk.builtin.TypeError("can't mod complex numbers.");
        },
        nb$divmod(other) {
            throw new Sk.builtin.TypeError("can't take floor or mod of complex number.");
        },
        nb$power(other, z) {
            if (z != null && !Sk.builtin.checkNone(z)) {
                throw new Sk.builtin.ValueError("complex modulo");
            }
            return power.call(this, other);
        },

        nb$abs() {
            const _real = this.real;
            const _imag = this.imag;
            if (!_is_finite(_real) || !_is_finite(_imag)) {
                /* C99 rules: if either the real or the imaginary part is an
                   infinity, return infinity, even if the other part is a
                   NaN.
                */
                if (_is_infinity(_real)) {
                    return new Sk.builtin.float_(Math.abs(_real));
                } else if (_is_infinity(_imag)) {
                    return new Sk.builtin.float_(Math.abs(_imag));
                }
                /* either the real or imaginary part is a NaN,
                   and neither is infinite. Result should be NaN. */
                return new Sk.builtin.float_(NaN);
            }
            const result = Math.hypot(_real, _imag);
            if (!_is_finite(result)) {
                throw new Sk.builtin.OverflowError("absolute value too large");
            }
            return new Sk.builtin.float_(result);
        },
    },
    getsets: {
        real: {
            $get() {
                return new Sk.builtin.float_(this.real);
            },
            $doc: "the real part of a complex number",
        },
        imag: {
            $get() {
                return new Sk.builtin.float_(this.imag);
            },
            $doc: "the imaginary part of a complex number",
        },
    },
    methods: /**@lends {Sk.builtin.complex.prototype}*/ {
        conjugate: {
            $meth() {
                return new Sk.builtin.complex(this.real, -this.imag);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "complex.conjugate() -> complex\n\nReturn the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.",
        },
        __getnewargs__: {
            $meth() {
                return new Sk.builtin.tuple([new Sk.builtin.float_(this.real), new Sk.builtin.float_(this.imag)]);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: Sk.builtin.none.none$,
        },
        __format__: {
            $meth(format_spec) {
                if (Sk.builtin.checkString(format_spec)) {
                    // currently just returns not implemented.
                    return _PyComplex_FormatAdvanced(this, format_spec);
                }
                throw new Sk.builtin.TypeError("__format__ requires str");
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "complex.__format__() -> str\n\nConvert to a string according to format_spec.",
        },
    },
});

Sk.exportSymbol("Sk.builtin.complex", Sk.builtin.complex);

/**
 * @function
 *
 * @description
 * returns a Number if the object passed as a __float__ method
 * Otherwise throws an error
 *
 * @param {Sk.builtin.object} op
 *
 * @ignore
 */
function PyFloat_AsDouble(op) {
    let v = op.v;
    if (typeof v === "number") {
        return v;
    } else if (op.nb$float) {
        v = op.nb$float();
    }
    if (v === undefined) {
        throw new Sk.builtin.TypeError("a float is required");
    }
    return v.v;
}

/**
 * @function
 *
 * @description
 * checks and tries the __complex__ method
 * throws an error if this returns a non complex object
 * returns null if that function does not exist
 *
 *
 * @param {Sk.builtin.object} op
 *
 * @ignore
 */
function try_complex_special_method(op) {
    // the lookup special method does already all the magic
    if (op == null) {
        return null;
    }
    const f = Sk.abstr.lookupSpecial(op, Sk.builtin.str.$complex);
    if (f !== undefined) {
        // method on builtin, provide this arg
        return Sk.misceval.callsimArray(f, []);
    }
    return null;
}

/**
 * @function
 *
 * @description
 * copied here for easy access
 * checks whether the argument is an instance of Sk.builtin.complex
 *
 * @return {boolean}
 *
 * @param {Sk.builtin.object} op
 * @ignore
 */
const _complex_check = Sk.builtin.checkComplex;

/**
 * @function
 *
 * @description
 * this is the logic for tp$new
 *
 * @param {Sk.builtin.object} real
 * @param {Sk.builtin.object} imag
 *
 * @ignore
 */
function complex_from_py(real, imag) {
    let tmp; // pyObject
    // var nbr, nbi; // real, imag as numbers
    const cr = {}; // PyComplexObject
    const ci = {}; // PyComplexObject
    let cr_is_complex = false;
    let ci_is_complex = false;

    let r = real;
    let i = imag;

    // handle case if passed in arguments are of type complex
    if (r != null && r.constructor === Sk.builtin.complex && i == null) {
        // subtypes are handled later;
        return r;
    }

    if (Sk.builtin.checkString(r)) {
        if (i != null) {
            throw new Sk.builtin.TypeError("complex() can't take second arg if first is a string");
        }
        return Sk.builtin.complex.complex_subtype_from_string(r, this);
    }

    if (i != null && Sk.builtin.checkString(i)) {
        throw new Sk.builtin.TypeError("complex() second arg can't be a string");
    }

    // try_complex_special_method
    tmp = try_complex_special_method(r);
    if (tmp != null && tmp !== Sk.builtin.NotImplemented.NotImplemented$) {
        if (!_complex_check(tmp)) {
            throw new Sk.builtin.TypeError("__complex__ should return a complex object");
        }
        r = tmp;
    }

    // just a temporary function to match cpython
    function check_number(nb) {
        return nb.nb$float !== undefined;
    }

    if (r != null) {
        if (!check_number(r)) {
            throw new Sk.builtin.TypeError("complex() first argument must be a string or a number, not '" + Sk.abstr.typeName(r) + "'");
        }
    }

    if (i != null) {
        if (!check_number(i)) {
            throw new Sk.builtin.TypeError("complex() second argument must be a number, not '" + Sk.abstr.typeName(r) + "'");
        }
    }

    /* If we get this far, then the "real" and "imag" parts should
       both be treated as numbers, and the constructor should return a
       complex number equal to (real + imag*1j).

       Note that we do NOT assume the input to already be in canonical
       form; the "real" and "imag" parts might themselves be complex
       numbers, which slightly complicates the code below. */
    if (r == null) {
        cr.real = 0.0;
        cr.imag = 0.0;
    } else if (_complex_check(r)) {
        /* Note that if r is of a complex subtype, we're only
        retaining its real & imag parts here, and the return
        value is (properly) of the builtin complex type. */
        cr.real = r.real;
        cr.imag = r.imag;
        cr_is_complex = true;
    } else {
        /* The "real" part really is entirely real, and contributes
        nothing in the imaginary direction.
        Just treat it as a double. */
        cr.real = PyFloat_AsDouble(r);
        cr.imag = 0.0;
    }

    if (i == null) {
        ci.real = 0.0;
        ci.imag = 0.0;
    } else if (_complex_check(i)) {
        ci.real = i.real;
        ci.imag = i.imag;
        ci_is_complex = true;
    } else {
        /* The "imag" part really is entirely imaginary, and
        contributes nothing in the real direction.
        Just treat it as a double. */
        ci.real = PyFloat_AsDouble(i);
        ci.imag = 0.0;
    }

    /*  If the input was in canonical form, then the "real" and "imag"
    parts are real numbers, so that ci.imag and cr.imag are zero.
    We need this correction in case they were not real numbers. */

    if (ci_is_complex === true) {
        cr.real -= ci.imag;
    }

    if (cr_is_complex === true) {
        ci.real += cr.imag;
    }
    return complex_subtype_from_doubles(cr.real, ci.real, this);
}

/**
 * @function
 *
 * @return {Sk.builtin.complex} an instance of complex - could be a subtype's instance
 *
 * @param {number} real
 * @param {number} imag
 * @param {Object} type_prototype Sk.builtin.complex.prototype
 * @ignore
 */
function complex_subtype_from_doubles(real, imag, type_prototype) {
    if (type_prototype === Sk.builtin.complex.prototype) {
        return new Sk.builtin.complex(real, imag);
    } else {
        const instance = new type_prototype.constructor();
        Sk.builtin.complex.call(instance, real, imag);
        return instance;
    }
}

const invalidUnderscores = /_[eE]|[eE]_|\._|_\.|[+-]_|_j|j_/;
const validUnderscores = /_(?=[^_])/g;
/**
 *
 * @function
 * @description Parses a string repr of a complex number
 * @param {*} val
 * @param {Object=} type_prototype
 * We leave this as Sk.builtin.complex since it is called by the compiler
 * @ignore
 */
Sk.builtin.complex.complex_subtype_from_string = function (val, type_prototype) {
    type_prototype = type_prototype || Sk.builtin.complex.prototype;
    var index;
    var start;
    var val_wws; // val with removed beginning ws and (
    var x = 0.0,
        y = 0.0; // real, imag parts
    var got_bracket = false; // flag for braces
    var len; // total length of val
    var match; // regex result

    // first check if val is javascript string or python string
    if (Sk.builtin.checkString(val)) {
        val = Sk.ffi.remapToJs(val);
    } else if (typeof val !== "string") {
        throw new TypeError("provided unsupported string-alike argument");
    }

    /* This is an python specific error, this does not do any harm in js, but we want
     * to be as close to the orginial impl. as possible.
     *
     * Check also for empty strings. They are not allowed.
     */
    if (val.indexOf("\0") !== -1 || val.length === 0 || val === "") {
        throw new Sk.builtin.ValueError("complex() arg is a malformed string");
    }

    // transform to unicode
    // ToDo: do we need this?
    index = 0; // first char

    // do some replacements for javascript floats
    val = val.replace(/inf|infinity/gi, "Infinity");
    val = val.replace(/nan/gi, "NaN");

    /* position on first nonblank */
    start = 0;
    while (val[index] === " ") {
        index++;
    }

    if (val[index] === "(") {
        /* skip over possible bracket from repr(). */
        got_bracket = true;
        index++;
        while (val[index] === " ") {
            index++;
        }
    }

    if (val.indexOf("_") !== -1) {
        if (invalidUnderscores.test(val)) {
            throw new Sk.builtin.ValueError("could not convert string to complex: '" + val + "'");
        }

        val = val.charAt(0) + val.substring(1).replace(validUnderscores, "");
    }

    /* a valid complex string usually takes one of the three forms:

        <float>                - real part only
        <float>j               - imaginary part only
        <float><signed-float>j - real and imaginary parts

        where <float> represents any numeric string that's accepted by the
        float constructor (including 'nan', 'inf', 'infinity', etc.), and
        <signed-float> is any string of the form <float> whose first character
        is '+' or '-'.

        For backwards compatibility, the extra forms

          <float><sign>j
          <sign>j
          j

        are also accepted, though support for these forms my be removed from
        a future version of Python.
     *      This is a complete regular expression for matching any valid python floats, e.g.:
     *          - 1.0
     *          - 0.
     *          - .1
     *          - nan/inf/infinity
     *          - +-1.0
     *          - +3.E-3
     *
     *      In order to work, this pattern requires only lower case characters
     *      There is case insensitive group option in js.
     *
     *      the [eE] could be refactored to soley e
     */
    var float_regex2 = /^(?:[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[eE][+-]?\d+)?|NaN|Infinity))/;
    val_wws = val.substr(index); // val with removed whitespace and "("

    /* first try to match a float at the beginning */
    match = val_wws.match(float_regex2);
    if (match !== null) {
        // one of the first 4 cases
        index += match[0].length;

        /* <float>j */
        if (val[index] === "j" || val[index] === "J") {
            y = parseFloat(match[0]);
            index++;
        } else if (val[index] === "+" || val[index] === "-") {
            /* <float><signed-float>j | <float><sign>j */
            x = parseFloat(match[0]);

            match = val.substr(index).match(float_regex2);
            if (match !== null) {
                /* <float><signed-float>j */
                y = parseFloat(match[0]);
                index += match[0].length;
            } else {
                /* <float><sign>j */
                y = val[index] === "+" ? 1.0 : -1.0;
                index++;
            }

            if (val[index] !== "j" && val[index] !== "J") {
                throw new Sk.builtin.ValueError("complex() arg is malformed string");
            }

            index++;
        } else {
            /* <float> */
            x = parseFloat(match[0]);
        }
    } else {
        // maybe <sign>j or j
        match = match = val_wws.match(/^([+-]?[jJ])/);
        if (match !== null) {
            if (match[0].length === 1) {
                y = 1.0; // must be j
            } else {
                y = match[0][0] === "+" ? 1.0 : -1.0;
            }

            index += match[0].length;
        }
    }

    while (val[index] === " ") {
        index++;
    }

    if (got_bracket) {
        /* if there was an opening parenthesis, then the corresponding
           closing parenthesis should be right here */
        if (val[index] !== ")") {
            throw new Sk.builtin.ValueError("complex() arg is malformed string");
        }

        index++;

        while (val[index] === " ") {
            index++;
        }
    }

    /* we should now be at the end of the string */
    if (val.length !== index) {
        throw new Sk.builtin.ValueError("complex() arg is malformed string");
    }

    // return here complex number parts
    return complex_subtype_from_doubles(x, y, type_prototype);
};

/**
 *
 * @function
 * @description
 *
 * A helper function for converting a big int to a number or throwing OverFlow
 * @ignore
 */
function fromBigIntToNumberOrOverflow(big) {
    const x = parseFloat(JSBI.toNumber(big));
    if (x == Infinity || x == -Infinity) {
        //trying to convert a large js string to a float
        throw new Sk.builtin.OverflowError("int too large to convert to float");
    }
    return x;
}

/**
 *
 * @function
 * @description
 * A wrapper to do the checks before passing the this.real, this.imag, other.real, other.imag
 * to the number function
 * @ignore
 * @param {function(number, number, number, number)} f
 * @param {boolean=} suppressOverflow
 */
function complexNumberSlot(f, suppressOverflow) {
    return function (other) {
        const a_real = this.real;
        const a_imag = this.imag;
        let b_real = other.real;
        let b_imag;
        const other_v = other.v;
        if (typeof b_real === "number") {
            b_imag = other.imag;
        } else if (typeof other_v === "number") {
            b_real = other_v;
            b_imag = 0.0;
        } else if (JSBI.__isBigInt(other_v)) {
            if (suppressOverflow === undefined) {
                b_real = fromBigIntToNumberOrOverflow(other_v);
            } else {
                b_real = other_v.toString(); // weird case for tp_richcompare
            }
            b_imag = 0.0;
        } else {
            return Sk.builtin.NotImplemented.NotImplemented$;
        }

        return f(a_real, a_imag, b_real, b_imag);
    };
}

function divide(a_real, a_imag, b_real, b_imag) {
    let ratio, denom, real, imag;
    const abs_b_real = Math.abs(b_real);
    const abs_b_imag = Math.abs(b_imag);
    if (abs_b_real >= abs_b_imag) {
        // divide tops and bottom by b_real
        if (abs_b_real === 0.0) {
            throw new Sk.builtin.ZeroDivisionError("complex division by zero");
        } else {
            ratio = b_imag / b_real;
            denom = b_real + b_imag * ratio;
            real = (a_real + a_imag * ratio) / denom;
            imag = (a_imag - a_real * ratio) / denom;
        }
    } else if (abs_b_imag >= abs_b_real) {
        // divide tops and bottom by b.imag
        ratio = b_real / b_imag;
        denom = b_real * ratio + b_imag;
        Sk.asserts.assert(b_imag !== 0.0);
        real = (a_real * ratio + a_imag) / denom;
        imag = (a_imag * ratio - a_real) / denom;
    } else {
        // At least one of b.real or b.imag is a NaN
        real = NaN;
        imag = NaN;
    }

    return new Sk.builtin.complex(real, imag);
}

const power = complexNumberSlot((a_real, a_imag, b_real, b_imag) => {
    const int_exponent = b_real | 0; // js convert to int
    if (b_imag === 0.0 && b_real === int_exponent) {
        return c_powi(a_real, a_imag, int_exponent);
    } else {
        return c_pow(a_real, a_imag, b_real, b_imag);
    }
});

// power of complex a and complex exponent b
function c_pow(a_real, a_imag, b_real, b_imag) {
    let len, phase, real, imag;

    if (b_real === 0.0 && b_imag === 0.0) {
        real = 1.0;
        imag = 0.0;
    } else if (a_real === 0.0 && a_imag === 0.0) {
        if (b_imag !== 0.0 || b_real < 0.0) {
            throw new Sk.builtin.ZeroDivisionError("complex division by zero");
        }

        real = 0.0;
        imag = 0.0;
    } else {
        const vabs = Math.hypot(a_real, a_imag);
        len = Math.pow(vabs, b_real);
        const at = Math.atan2(a_imag, a_real);
        phase = at * b_real;

        if (b_imag !== 0.0) {
            len /= Math.exp(at * b_imag);
            phase += b_imag * Math.log(vabs);
        }

        real = len * Math.cos(phase);
        imag = len * Math.sin(phase);
    }
    return new Sk.builtin.complex(real, imag);
}

// power of complex x and integer exponent n
function c_powi(a_real, a_imag, n) {
    if (n > 100 || n < -100) {
        return c_pow(a_real, a_imag, n, 0.0);
    } else if (n > 0) {
        return c_powu(a_real, a_imag, n);
    } else {
        //  return c_quot(c_1,c_powu(x,-n));
        const r = c_powu(a_real, a_imag, -n);
        return divide(1.0, 0.0, r.real, r.imag);
    }
}

function c_powu(a_real, a_imag, n) {
    var r, p; // Py_complex
    let mask = 1;
    r = new Sk.builtin.complex(1.0, 0.0);
    p = new Sk.builtin.complex(a_real, a_imag);

    while (mask > 0 && n >= mask) {
        if (n & mask) {
            r = new Sk.builtin.complex(r.real * p.real - r.imag * p.imag, r.real * p.imag + p.real * r.imag);
        }

        mask <<= 1;
        p = new Sk.builtin.complex(p.real * p.real - p.imag * p.imag, 2 * p.real * p.imag);
    }

    return r;
}

/**
 * Internal format function for repr and str
 * It is not intended for __format__ calls
 *
 * This functions assumes, that v is always instance of Sk.builtin.complex
 * @ignore
 */
function complex_format(v, precision, format_code) {
    function copysign(a, b) {
        let sign;
        if (b) {
            sign = b < 0 ? -1 : 1;
        } else {
            sign = 1 / b < 0 ? -1 : 1;
        }
        return sign * Math.abs(a);
    }

    let result; // pyObject

    let pre = "";
    let im = "";
    let re = null;
    let lead = "";
    let tail = "";
    const real = v.real;
    const imag = v.imag;

    if (real === 0.0 && copysign(1.0, real) == 1.0) {
        re = "";
        im = PyOS_double_to_string(imag, format_code, precision, 0, null);
        // im = imag;
    } else {
        /* Format imaginary part with sign, real part without */
        pre = PyOS_double_to_string(real, format_code, precision, 0, null);
        re = pre;

        im = PyOS_double_to_string(imag, format_code, precision, PyOS_double_to_string.Py_DTSF_SIGN, null);

        if (imag === 0 && 1 / imag === -Infinity && im && im[0] !== "-") {
            im = "-" + im; // force negative zero sign
        }

        lead = "(";
        tail = ")";
    }

    result = "" + lead + re + im + "j" + tail; // concat all parts
    return new Sk.builtin.str(result);
}

/**
 * https://hg.python.org/cpython/file/3cf2990d19ab/Objects/complexobject.c#l907
 * also see _PyComplex_FormatAdvanced
 * @ignore
 */
function _PyComplex_FormatAdvanced(self, format_spec) {
    throw new Sk.builtin.NotImplementedError("__format__ is not implemented for complex type.");
}

/**
    Return true if float or double are is neither infinite nor NAN, else false
    Value is already a Javascript object
    @ignore
 */
function _is_finite(val) {
    return Number.isFinite(val);
}

function _is_infinity(val) {
    return val === Infinity || val === -Infinity;
}

/**
 * Convert a double val to a string using supplied format_code, precision, and flags.
 *
 * format_code must be one of 'e', 'E', 'f', 'F', 'g', 'G' or 'r'. For 'r', the supplied precision must be 0 and is ignored. The 'r' format code specifies the standard repr() format.
 *
 * flags can be zero or more of the values Py_DTSF_SIGN, Py_DTSF_ADD_DOT_0, or Py_DTSF_ALT, or-ed together:
 *
 * Py_DTSF_SIGN means to always precede the returned string with a sign character, even if val is non-negative.
 * Py_DTSF_ADD_DOT_0 means to ensure that the returned string will not look like an integer.
 * Py_DTSF_ALT means to apply “alternate” formatting rules. See the documentation for the PyOS_snprintf() '#' specifier for details.
 * If ptype is non-NULL, then the value it points to will be set to one of Py_DTST_FINITE, Py_DTST_INFINITE, or Py_DTST_NAN, signifying that val is a finite number, an
 * infinite number, or not a number, respectively.
 * @ignore
 */
function PyOS_double_to_string(val, format_code, precision, flags, type) {
    let buf,
        t,
        upper = false;
    // Validate format code, and map upper and lower case
    switch (format_code) {
        case "e": /* exponent */
        case "f": /* fixed */
        case "g" /* general */:
            break;
        case "E":
            upper = true;
            format_code = "e";
            break;
        case "F":
            upper = true;
            format_code = "f";
            break;
        case "r" /* repr format */:
            // Supplied precision is unused, must be 0.
            if (precision !== 0) {
                throw new Error("Bad internall call"); // only happens when somebody messes up calling this in js
            }
            // repr() precision is 17 significant decimal digits
            precision = 17;
            format_code = "g";
            break;
        default:
            throw new Error("Bad internall call");
    }
    // no need for buffer size calculation like in cpython
    // Handle nan and inf
    if (isNaN(val)) {
        buf = "nan";
        t = PyOS_double_to_string.Py_DTST_NAN;
    } else if (val === Infinity) {
        buf = "inf";
        t = PyOS_double_to_string.Py_DTST_INFINITE;
    } else if (val === -Infinity) {
        buf = "-inf";
        t = PyOS_double_to_string.Py_DTST_INFINITE;
    } else {
        t = PyOS_double_to_string.Py_DTST_FINITE;
        if (flags & PyOS_double_to_string.Py_DTSF_ADD_DOT_0) {
            format_code = "g"; // "Z"; _PyOS_ascii_formatd converts "Z" to "g"
        }
        // ToDo: call snprintf here
        // ToDo: call ascii_formatd
        var format_str = "%";
        format_str += flags & PyOS_double_to_string.Py_DTSF_ALT ? "#" : "";

        if (precision != null) {
            format_str += ".";
            format_str += precision;
        }

        format_str += format_code;
        format_str = new Sk.builtin.str(format_str);
        /**
         * We can call nb$remainder with val, because it gets unwrapped and it doesn't matter if it is
         * already a javascript number. If we do not pass a float, we can't distinguish between ints and floats
         * and therefore we can't adjust the sign of the zero accordingly
         */
        buf = format_str.nb$remainder(new Sk.builtin.float_(val));
        buf = buf.v; // get javascript string
    }
    /**
     * Add sign when requested. It's convenient (esp. when formatting complex numbers) to
     * include sign even for inf and nan.
     */
    if (flags & PyOS_double_to_string.Py_DTSF_SIGN && buf[0] !== "-") {
        buf = "+" + buf;
    }
    if (upper) {
        // Convert to upper case
        buf = buf.toUpperCase();
    }
    return buf;
}

/* PyOS_double_to_string's "flags" parameter can be set to 0 or more of: */
PyOS_double_to_string.Py_DTSF_SIGN = 0x01; // always add the sign
PyOS_double_to_string.Py_DTSF_ADD_DOT_0 = 0x02; // if the result is an integer add ".0"
PyOS_double_to_string.Py_DTSF_ALT = 0x04; // "alternate" formatting. it's format_code specific

/* PyOS_double_to_string's "type", if non-NULL, will be set to one of: */
PyOS_double_to_string.Py_DTST_FINITE = 0;
PyOS_double_to_string.Py_DTST_INFINITE = 1;
PyOS_double_to_string.Py_DTST_NAN = 2;


/***/ }),

/***/ "./src/constants.js":
/*!**************************!*\
  !*** ./src/constants.js ***!
  \**************************/
/*! no static exports found */
/***/ (function(module, exports) {


/* Constants used for kwargs */

// String constants
Sk.builtin.str.$empty = new Sk.builtin.str("");
Sk.builtin.str.$emptystr = Sk.builtin.str.$empty;

Sk.builtin.str.$utf8 = new Sk.builtin.str("utf-8");
Sk.builtin.str.$ascii = new Sk.builtin.str("ascii");

Sk.builtin.str.$default_factory = new Sk.builtin.str("default_factory");
Sk.builtin.str.$imag = new Sk.builtin.str("imag");
Sk.builtin.str.$real = new Sk.builtin.str("real");

Sk.builtin.str.$abs = new Sk.builtin.str("__abs__");
Sk.builtin.str.$bytes = new Sk.builtin.str("__bytes__");
Sk.builtin.str.$call = new Sk.builtin.str("__call__");
Sk.builtin.str.$class = new Sk.builtin.str("__class__");
Sk.builtin.str.$cmp = new Sk.builtin.str("__cmp__");
Sk.builtin.str.$complex = new Sk.builtin.str("__complex__");
Sk.builtin.str.$contains = new Sk.builtin.str("__contains__");
Sk.builtin.str.$copy = new Sk.builtin.str("__copy__");
Sk.builtin.str.$dict = new Sk.builtin.str("__dict__");
Sk.builtin.str.$dir = new Sk.builtin.str("__dir__");
Sk.builtin.str.$doc = new Sk.builtin.str("__doc__");        
Sk.builtin.str.$enter = new Sk.builtin.str("__enter__");
Sk.builtin.str.$eq = new Sk.builtin.str("__eq__");
Sk.builtin.str.$exit = new Sk.builtin.str("__exit__");
Sk.builtin.str.$index = new Sk.builtin.str("__index__");
Sk.builtin.str.$init = new Sk.builtin.str("__init__");
Sk.builtin.str.$int_ = new Sk.builtin.str("__int__");
Sk.builtin.str.$iter = new Sk.builtin.str("__iter__");
Sk.builtin.str.$file = new Sk.builtin.str("__file__");
Sk.builtin.str.$float_ = new Sk.builtin.str("__float__");
Sk.builtin.str.$format = new Sk.builtin.str("__format__");
Sk.builtin.str.$ge = new Sk.builtin.str("__ge__");
Sk.builtin.str.$getattr = new Sk.builtin.str("__getattr__");
Sk.builtin.str.$getattribute = new Sk.builtin.str("__getattribute__");
Sk.builtin.str.$getitem = new Sk.builtin.str("__getitem__");
Sk.builtin.str.$gt = new Sk.builtin.str("__gt__");
Sk.builtin.str.$keys = new Sk.builtin.str("keys");
Sk.builtin.str.$le = new Sk.builtin.str("__le__");
Sk.builtin.str.$len = new Sk.builtin.str("__len__");
Sk.builtin.str.$length_hint = new Sk.builtin.str("__length_hint__");
Sk.builtin.str.$loader = new Sk.builtin.str("__loader__");
Sk.builtin.str.$lt = new Sk.builtin.str("__lt__");
Sk.builtin.str.$module = new Sk.builtin.str("__module__");
Sk.builtin.str.$missing = new Sk.builtin.str("__missing__");
Sk.builtin.str.$name = new Sk.builtin.str("__name__");
Sk.builtin.str.$ne = new Sk.builtin.str("__ne__");
Sk.builtin.str.$new = new Sk.builtin.str("__new__");
Sk.builtin.str.$next = new Sk.builtin.str("__next__");
Sk.builtin.str.$path = new Sk.builtin.str("__path__");
Sk.builtin.str.$qualname = new Sk.builtin.str("__qualname__");
Sk.builtin.str.$repr = new Sk.builtin.str("__repr__");
Sk.builtin.str.$reversed = new Sk.builtin.str("__reversed__");
Sk.builtin.str.$round = new Sk.builtin.str("__round__");
Sk.builtin.str.$setattr = new Sk.builtin.str("__setattr__");
Sk.builtin.str.$setitem = new Sk.builtin.str("__setitem__");
Sk.builtin.str.$str = new Sk.builtin.str("__str__");
Sk.builtin.str.$trunc = new Sk.builtin.str("__trunc__");
Sk.builtin.str.$write = new Sk.builtin.str("write");

Sk.misceval.op2method_ = {
    "Eq"   : Sk.builtin.str.$eq,
    "NotEq": Sk.builtin.str.$ne,
    "Gt"   : Sk.builtin.str.$gt,
    "GtE"  : Sk.builtin.str.$ge,
    "Lt"   : Sk.builtin.str.$lt,
    "LtE"  : Sk.builtin.str.$le
};



/***/ }),

/***/ "./src/descr.js":
/*!**********************!*\
  !*** ./src/descr.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @function
 * @param {string} type_name
 * @param {string|undefined} repr_name
 * @param {Function} descr_constructor
 *
 * @returns {FunctionConstructor}
 */
function buildDescriptor(type_name, repr_name, descr_options) {
    const descr = Sk.abstr.buildNativeClass(type_name, {
        constructor: descr_options.constructor,
        slots: Object.assign({
            tp$getattr: Sk.generic.getAttr,
            $r: descriptorRepr,
        }, descr_options.slots),
        getsets: Object.assign(descr_options.getsets || {}, descriptorGetsets),
        proto: /**@lends {descr_object.prototype}*/ Object.assign(descr_options.proto || {}, {
            d$repr_name: repr_name || type_name,
            d$check: descriptorCheck,
            d$set_check: descriptorSetCheck,
        }),
        flags: { sk$acceptable_as_base_class: false },
    });
    return descr;
}

function descriptorCheck(obj) {
    if (obj == null) {
        return this;
    } else if (!obj.ob$type.$isSubType(this.d$type)) {
        throw new Sk.builtin.TypeError(
            "descriptor '" +
                this.d$name +
                "' requires a '" +
                this.d$type.prototype.tp$name +
                "' object but received a '" +
                Sk.abstr.typeName(obj) +
                "' object"
        );
    }
    return;
}

function descriptorSetCheck(obj) {
    if (!obj.ob$type.$isSubType(this.d$type)) {
        throw new Sk.builtin.TypeError(
            "descriptor '" +
                this.d$name +
                "' requires a '" +
                this.d$type.prototype.tp$name +
                "' object but received a '" +
                Sk.abstr.typeName(obj) +
                "' object"
        );
    }
}

function descriptorRepr() {
    return new Sk.builtin.str("<" + this.d$repr_name + " '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' objects>");
}

const descriptorGetsets = {
    __doc__: {
        $get() {
            return this.d$def.$doc ? new Sk.builtin.str(this.d$def.$doc) : Sk.builtin.none.none$;
        },
    },
    __objclass__: {
        $get() {
            return this.d$type;
        },
    },
    __name__: {
        $get() {
            return new Sk.builtin.str(this.d$name);
        },
    },
};

const descrTextSig = {
    __text_signature__: {
        $get() {
            return this.d$def.$textsig ? new Sk.builtin.str(this.d$def.$textsig) : Sk.builtin.none.none$;
        },
    },
};

/**
 * @constructor
 * @param {typeObject} type_obj
 * @param {Object} gsd
 * @extends {Sk.builtin.object}
 */
Sk.builtin.getset_descriptor = buildDescriptor("getset_descriptor", undefined, {
    constructor: function getset_descr(typeobj, getset_def) {
        this.d$def = getset_def;
        this.$get = getset_def.$get;
        this.$set = getset_def.$set;
        this.d$type = typeobj;
        this.d$name = getset_def.$name;
    },
    slots: {
        tp$descr_get(obj, type) {
            let ret;
            if ((ret = this.d$check(obj))) {
                return ret;
            }
            if (this.$get !== undefined) {
                return this.$get.call(obj);
            }

            throw new Sk.builtin.AttributeError(
                "getset_descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' objects is not readable"
            );
        },
        tp$descr_set(obj, value) {
            this.d$set_check(obj);

            if (this.$set !== undefined) {
                return this.$set.call(obj, value);
            }
            throw new Sk.builtin.AttributeError("attribute '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' objects is readonly");
        },
    },
});

/**
 * @param {typeObject} type_obj
 * @param {Object} method
 * @extends {descr_object}
 */
Sk.builtin.method_descriptor = buildDescriptor("method_descriptor", "method", {
    constructor: function method_descriptor(typeobj, method_def) {
        this.d$def = method_def;
        this.$meth = method_def.$meth; //useful for internal fast calls
        this.d$type = typeobj;
        this.d$name = method_def.$name || "<native JS>";
        const flags = method_def.$flags || {};
        this.$flags = flags;
        if (flags.FastCall && flags.NoKwargs) {
            this.tp$call = this.$methodFastCallNoKwargs;
        } else if (flags.FastCall) {
            this.tp$call = this.$methodFastCall;
        } else if (flags.NoArgs) {
            this.tp$call = this.$methodCallNoArgs;
        } else if (flags.OneArg) {
            this.tp$call = this.$methodCallOneArg;
        } else if (flags.NamedArgs) {
            this.tp$call = this.$methodCallNamedArgs;
        } else if (flags.MinArgs !== undefined) {
            this.tp$call = this.$methodCallMinArgs;
        } else {
            // for legacy methods that haven't defined flags yet
            this.func_code = method_def.$meth;
            this.tp$call = this.$defaultCall;
            this.$memoiseFlags = Sk.builtin.func.prototype.$memoiseFlags;
            this.$resolveArgs = Sk.builtin.func.prototype.$resolveArgs;
        }
    },
    slots: {
        tp$call(args, kwargs) {
            return this.tp$call(args, kwargs);
        },
        tp$descr_get(obj, type) {
            let ret;
            if ((ret = this.d$check(obj))) {
                return ret;
            }
            return new Sk.builtin.sk_method(this.d$def, obj);
        },
    },
    getsets: descrTextSig,
    proto: {
        $methodFastCall(args, kwargs) {
            const self = args.shift();
            this.m$checkself(self);
            return this.$meth.call(self, args, kwargs);
        },
        $methodFastCallNoKwargs(args, kwargs) {
            const self = args.shift();
            this.m$checkself(self);
            Sk.abstr.checkNoKwargs(this.d$name, kwargs);
            return this.$meth.call(self, args);
        },
        $methodCallNoArgs(args, kwargs) {
            const self = args.shift();
            this.m$checkself(self);
            Sk.abstr.checkNoArgs(this.d$name, args, kwargs);
            return this.$meth.call(self);
        },
        $methodCallOneArg(args, kwargs) {
            const self = args.shift();
            this.m$checkself(self);
            Sk.abstr.checkOneArg(this.d$name, args, kwargs);
            return this.$meth.call(self, args[0]);
        },
        $methodCallNamedArgs(args, kwargs) {
            const self = args.shift();
            this.m$checkself(self);
            args = Sk.abstr.copyKeywordsToNamedArgs(this.d$name, this.$flags.NamedArgs, args, kwargs, this.$flags.Defaults);
            return this.$meth.call(self, ...args);
        },
        $methodCallMinArgs(args, kwargs) {
            const self = args.shift();
            this.m$checkself(self);
            Sk.abstr.checkNoKwargs(this.d$name, kwargs);
            Sk.abstr.checkArgsLen(this.d$name, args, this.$flags.MinArgs, this.$flags.MaxArgs);
            return this.$meth.call(self, ...args);
        },
        $defaultCall(args, kwargs) {
            this.m$checkself(args[0]);
            return Sk.builtin.func.prototype.tp$call.call(this, args, kwargs);
        },
        m$checkself(self) {
            if (self === undefined) {
                throw new Sk.builtin.TypeError(
                    "descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' object needs an argument"
                );
            }
            this.d$check(self);
        },
    },
});

/**
 * @constructor
 * @extends {descr_object}
 *
 * @param {typeObject} type_obj
 * @param {Object} wrapper_base
 * @param {Function} wrapped
 */
Sk.builtin.wrapper_descriptor = buildDescriptor("wrapper_descriptor", "slot wrapper", {
    constructor: function wrapper_descriptor(typeobj, slot_def, wrapped) {
        this.d$def = slot_def;
        this.d$type = typeobj;
        this.d$name = wrapped.$name = slot_def.$name;
        this.d$wrapped = wrapped;
    },
    slots: {
        tp$descr_get(obj, type) {
            let ret;
            if ((ret = this.d$check(obj))) {
                return ret;
            }
            return new Sk.builtin.method_wrapper(this, obj);
        },
        tp$call(args, kwargs) {
            // make sure the first argument is acceptable as self
            if (args.length < 1) {
                throw new Sk.builtin.TypeError(
                    "descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' object needs an argument"
                );
            }
            const self = args.shift();
            if (!self.ob$type.$isSubType(this.d$type)) {
                throw new Sk.builtin.TypeError(
                    "descriptor '" +
                        this.d$name +
                        "' requires a '" +
                        this.d$type.prototype.tp$name +
                        "' object but received a '" +
                        Sk.abstr.typeName(self) +
                        "'"
                );
            }
            return this.raw$call(self, args, kwargs);
        },
    },
    proto: {
        raw$call(self, args, kwargs) {
            // the base might have some flags I guess... see cpython version in descr.c
            this.d$wrapped.$name = this.d$name; // hack since some slots use the same function (__setattr__, __delattr__)
            return this.d$def.$wrapper.call(this.d$wrapped, self, args, kwargs);
        },
    },
});

/**
 * @constructor
 * @extends {descr_object}
 * @param {Sk.builtin.wrapper_descriptor} type_obj
 * @param wrapper_base
 */
Sk.builtin.method_wrapper = buildDescriptor("method_wrapper", undefined, {
    constructor: function method_wrapper(wrapper_descr, self) {
        this.m$descr = wrapper_descr;
        this.m$self = self;
        this.d$def = wrapper_descr.d$def;
        this.d$name = wrapper_descr.d$name;
        this.d$type = wrapper_descr.d$type;
    },
    slots: {
        tp$call(args, kwargs) {
            return this.m$descr.raw$call(this.m$self, args, kwargs);
        },
        tp$richcompare(other, op) {
            if ((op !== "Eq" && op !== "NotEq") || !(other instanceof Sk.builtin.method_wrapper)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            let eq = this.m$self === other.m$self && this.m$descr === other.m$descr;
            return op === "Eq" ? eq : !eq;
        },
        $r() {
            return new Sk.builtin.str("<method-wrapper '" + this.d$name + "' of " + Sk.abstr.typeName(this.m$self) + " object>");
        },
    },
    getsets: {
        __self__: {
            $get() {
                return this.m$self;
            },
        },
    },
});

/**
 *
 * @constructor
 * @extends {descr_object}
 * @param {typeObject} typeobj
 * @param {Object} method_def
 *
 * @description
 * This is for classmethods in Native Js Classes, not for "f = classmethod(f)" in Python
 * See dict.fromkeys for a native example
 *
 */
Sk.builtin.classmethod_descriptor = buildDescriptor("classmethod_descriptor", "method", {
    constructor: function classmethod_descriptor(typeobj, method_def) {
        this.d$def = method_def;
        this.$meth = method_def.$meth; //useful for internal fast calls
        this.d$type = typeobj;
        this.d$name = method_def.$name || "<native JS>";
    },
    slots: {
        tp$call(args, kwargs) {
            if (args.length < 1) {
                throw new Sk.builtin.TypeError(
                    "descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' object needs an argument"
                );
            }
            const self = args.shift();
            const bound = this.tp$descr_get(null, self);
            return bound.tp$call(args, kwargs);
        },
        tp$descr_get(obj, type, canSuspend) {
            if (type === undefined) {
                if (obj !== null) {
                    type = type || obj.ob$type;
                } else {
                    throw new Sk.builtin.TypeError(
                        "descriptor '" + this.d$name + "' for type '" + this.d$type.prototype.tp$name + "' needs an object or a type"
                    );
                }
            }
            if (type.ob$type !== Sk.builtin.type) {
                throw new Sk.builtin.TypeError(
                    "descriptor '" +
                        this.d$name +
                        "' for type '" +
                        this.d$type.prototype.tp$name +
                        "' needs a type not a '" +
                        Sk.abstr.typeName(type) +
                        "' as arg 2"
                );
            }

            if (!type.$isSubType(this.d$type)) {
                throw new Sk.builtin.TypeError(
                    "descriptor '" +
                        this.d$name +
                        "' requires a '" +
                        this.d$type.prototype.tp$name +
                        "' object but received a '" +
                        Sk.abstr.typeName(type) +
                        "' object"
                );
            }
            return new Sk.builtin.sk_method(this.d$def, type);
        },
    },
    getsets: descrTextSig,
});


[
    Sk.builtin.method_descriptor,
    Sk.builtin.getset_descriptor,
    Sk.builtin.wrapper_descriptor,
    Sk.builtin.method_wrapper,
    Sk.builtin.classmethod_descriptor,
].forEach((cls) => {
    Sk.abstr.setUpSlots(cls);
    Sk.abstr.setUpMethods(cls);
    Sk.abstr.setUpGetSets(cls);
});

/***/ }),

/***/ "./src/dict.js":
/*!*********************!*\
  !*** ./src/dict.js ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports) {

/** @typedef {Sk.builtin.object} */ var pyObject;
/** @typedef {Sk.builtin.type|Function} */ var typeObject;

/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {Array=} L A javascript array of key value pairs - All elements should be pyObjects
 *
 * @description
 * call with an array of key value pairs
 * Do not use this function to convert a JS object to a dict
 * Instead use {@link Sk.ffi.remapToPy}
 *
 *
 */
Sk.builtin.dict = Sk.abstr.buildNativeClass("dict", {
    constructor: function dict(L) {
        // calling new Sk.builtin.dict is an internal method that requires an array of key value pairs
        if (L === undefined) {
            L = [];
        }
        Sk.asserts.assert(Array.isArray(L) && L.length % 2 === 0 && this instanceof Sk.builtin.dict, "bad call to dict constructor");

        this.size = 0;
        this.entries = Object.create(null);
        this.buckets = {};
        for (let i = 0; i < L.length; i += 2) {
            this.set$item(L[i], L[i + 1]);
        }
        this.in$repr = false;
        this.$version = 0; // change version number anytime the keys change
    },
    slots: /**@lends {Sk.builtin.dict.prototype}*/ {
        tp$getattr: Sk.generic.getAttr,
        tp$as_sequence_or_mapping: true,
        tp$as_number: true,
        tp$hash: Sk.builtin.none.none$,
        tp$doc:
            "dict() -> new empty dictionary\ndict(mapping) -> new dictionary initialized from a mapping object's\n    (key, value) pairs\ndict(iterable) -> new dictionary initialized as if via:\n    d = {}\n    for k, v in iterable:\n        d[k] = v\ndict(**kwargs) -> new dictionary initialized with the name=value pairs\n    in the keyword argument list.  For example:  dict(one=1, two=2)",
        $r() {
            if (this.in$repr) {
                // prevents recursively calling repr;
                return new Sk.builtin.str("{...}");
            }
            this.in$repr = true;
            // iterate over the keys - we don't use the dict iterator or mp$subscript here
            const ret = this.$items().map(([key, val]) => Sk.misceval.objectRepr(key) + ": " + Sk.misceval.objectRepr(val));
            this.in$repr = false;
            return new Sk.builtin.str("{" + ret.join(", ") + "}");
        },
        tp$new: Sk.generic.new,
        tp$init(args, kwargs) {
            return this.update$common(args, kwargs, "dict");
        },
        tp$iter() {
            return new dict_iter_(this);
        },
        tp$richcompare(other, op) {
            let res;
            if (!(other instanceof Sk.builtin.dict) || (op !== "Eq" && op !== "NotEq")) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            if (other === this) {
                res = true;
            } else if (this.size !== other.size) {
                res = false;
            } else {
                let otherv;
                res = this.$items().every(([key, val]) => {
                    otherv = other.mp$lookup(key);
                    return otherv !== undefined && (otherv === val || Sk.misceval.richCompareBool(val, otherv, "Eq"));
                });
            }
            return op === "Eq" ? res : !res;
        },
        // as number slot
        nb$or(other) {
            if (!(other instanceof Sk.builtin.dict)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            const dict = this.dict$copy();
            dict.dict$merge(other);
            return dict;
        },
        nb$reflected_or(other) {
            if (!(other instanceof Sk.builtin.dict)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            // dict or is not commutative so must define reflected slot here.
            const dict = other.dict$copy();
            dict.dict$merge(this);
            return dict;
        },
        nb$inplace_or(other) {
            return Sk.misceval.chain(this.update$onearg(other), () => this);
        },
        // sequence or mapping slots
        sq$length() {
            return this.get$size();
        },
        sq$contains(ob) {
            return this.mp$lookup(ob) !== undefined;
        },
        mp$subscript(key, canSuspend) {
            const res = this.mp$lookup(key);
            if (res !== undefined) {
                // Found in dictionary
                return res;
            }
            let missing = Sk.abstr.lookupSpecial(this, Sk.builtin.str.$missing);
            if (missing !== undefined) {
                const ret = Sk.misceval.callsimOrSuspendArray(missing, [key]);
                return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
            }
            throw new Sk.builtin.KeyError(key);
        },
        mp$ass_subscript(key, value) {
            if (value === undefined) {
                const item = this.pop$item(key);
                if (item === undefined) {
                    throw new Sk.builtin.KeyError(key);
                }
            } else {
                this.set$item(key, value);
            }
        },
    },
    methods: /**@lends {Sk.builtin.dict.prototype}*/ {
        __reversed__: {
            $meth() {
                return new dict_reverse_iter_(this);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Return a reverse iterator over the dict keys.",
        },
        get: {
            $meth(key, d) {
                return this.mp$lookup(key) || d || Sk.builtin.none.none$;
            },
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: "($self, key, default=None, /)",
            $doc: "Return the value for key if key is in the dictionary, else default.",
        },
        setdefault: {
            $meth(key, default_) {
                // logic could be simpler here but some tests dictate we can't do too many lookups
                let item;
                const hash = getHash(key);
                item = typeof hash === "string" ? this.entries[hash] : this.get$bucket_item(key, hash);
                if (item !== undefined) {
                    return item[1];
                }
                default_ = default_ || Sk.builtin.none.none$;
                if (typeof hash === "string") {
                    this.entries[hash] = [key, default_];
                } else {
                    this.set$bucket_item(key, default_, hash);
                }
                this.size++;
                this.$version++;
                return default_;
            },
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: "($self, key, default=None, /)",
            $doc:
                "Insert key with a value of default if key is not in the dictionary.\n\nReturn the value for key if key is in the dictionary, else default.",
        },
        pop: {
            $meth(key, d) {
                const item = this.pop$item(key);
                if (item !== undefined) {
                    return item[1];
                }
                // Not found in dictionary
                if (d !== undefined) {
                    return d;
                }
                throw new Sk.builtin.KeyError(key);
            },
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: null,
            $doc:
                "D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\nIf key is not found, d is returned if given, otherwise KeyError is raised",
        },
        popitem: {
            $meth() {
                // not particularly efficent but we get allkeys as an array to iter anyway
                const size = this.get$size();
                if (size === 0) {
                    throw new Sk.builtin.KeyError("popitem(): dictionary is empty");
                }
                const [key, val] = this.$items()[size - 1];
                this.pop$item(key);
                return new Sk.builtin.tuple([key, val]);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.popitem() -> (k, v), remove and return some (key, value) pair as a\n2-tuple; but raise KeyError if D is empty.",
        },
        keys: {
            $meth() {
                return new dict_keys(this);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.keys() -> a set-like object providing a view on D's keys",
        },
        items: {
            $meth() {
                return new dict_items(this);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.items() -> a set-like object providing a view on D's items",
        },
        values: {
            $meth() {
                return new dict_values(this);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.values() -> an object providing a view on D's values",
        },
        update: {
            $meth(args, kwargs) {
                return Sk.misceval.chain(this.update$common(args, kwargs, "update"), () => Sk.builtin.none.none$);
            },
            $flags: { FastCall: true },
            $textsig: null,
            $doc:
                "D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.\nIf E is present and has a .keys() method, then does:  for k in E: D[k] = E[k]\nIf E is present and lacks a .keys() method, then does:  for k, v in E: D[k] = v\nIn either case, this is followed by: for k in F:  D[k] = F[k]",
        },
        clear: {
            $meth() {
                this.size = 0;
                this.$version++;
                this.entries = Object.create(null);
                this.buckets = {};
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.clear() -> None.  Remove all items from D.",
        },
        copy: {
            $meth() {
                return this.dict$copy();
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.copy() -> a shallow copy of D",
        },
    },
    classmethods: /**@lends {Sk.builtin.dict.prototype}*/ {
        fromkeys: {
            $meth: function fromkeys(seq, value) {
                value = value || Sk.builtin.none.none$;
                let dict = this === Sk.builtin.dict ? new this() : this.tp$call([], []);
                return Sk.misceval.chain(
                    dict,
                    (d) => {
                        dict = d;
                        return Sk.misceval.iterFor(Sk.abstr.iter(seq), (key) => {
                            return dict.mp$ass_subscript(key, value, true);
                        });
                    },
                    () => dict
                );
            },
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: "($type, iterable, value=None, /)",
            $doc: "Create a new dictionary with keys from iterable and values set to value.",
        },
    },
    proto: /**@lends {Sk.builtin.dict.prototype}*/ {
        quick$lookup,
        mp$lookup,
        get$size() {
            // can't be overridden by subclasses so we use this for the dict key iterator
            return this.size;
        },
        sk$asarray() {
            return Object.values(this.entries).map((item) => item[0]);
        },
        update$common,
        update$onearg(arg) {
            if (arg instanceof Sk.builtin.dict || Sk.abstr.lookupSpecial(arg, Sk.builtin.str.$keys) !== undefined) {
                return this.dict$merge(arg);
            } else {
                return this.dict$merge_seq(arg);
            }
        },
        dict$copy() {
            const newCopy = new Sk.builtin.dict([]);
            newCopy.size = this.size;
            const entries = Object.entries(this.entries); // do it this way for mappingproxy
            for (let i in entries) {
                const key = entries[i][0];
                const item = entries[i][1];
                newCopy.entries[key] = [item[0], item[1]];
            }
            let bucket, this_bucket;
            for (let i in this.buckets) {
                this_bucket = this.buckets[i];
                newCopy.buckets[i] = bucket = [];
                for (let j = 0; j < this_bucket.length; j++) {
                    bucket.push(newCopy.entries["#" + i + "_" + j]);
                }
            }
            return newCopy;
        },
        $items() {
            return Object.values(this.entries);
        },
        set$item,
        get$bucket_item,
        pop$bucket_item,
        set$bucket_item,
        pop$item,
        dict$merge,
        dict$merge_seq,
    },
});

function getHash(key) {
    let key_hash = key.$savedKeyHash;
    if (key_hash !== undefined) {
        return key_hash;
    }
    key_hash = Sk.abstr.objectHash(key);
    return key_hash;
}

/**
 * @private
 * @param {Sk.builtin.str} pyName
 * @this {Sk.builtin.dict}
 * 
 * this is hot code!
 */
function quick$lookup(pyName) {
    /**@type {string} */
    var key_hash = pyName.$savedKeyHash;
    var item = this.entries[key_hash];
    if (item !== undefined) {
        return item[1];
    }
    return;
};

/**
 * NB:
 * We could put the following methods on the proto in the above object literal
 * but they're quite long so we keep them below for readability
 * @ignore
 */

/**
 * @function
 * @param {pyObject} key - key to get item for
 * @param {string} hash_value - hash_value from the key
 *
 * @description
 * fast call - if we have a str then we can guarantee that it's in the bucket
 * so we compare strings quickly rather than reaching out to richcompareBool
 *
 * @return {pyObject|undefined} the item if found or undefined if not found
 * @private
 */
function get$bucket_item(key, hash_value) {
    const bucket = this.buckets[hash_value];
    if (bucket === undefined) {
        return;
    }
    let item;
    for (let i = 0; i < bucket.length; i++) {
        item = bucket[i];
        if (item === undefined) {
            continue;
        }
        if (item[0] === key || Sk.misceval.richCompareBool(key, item[0], "Eq")) {
            return item;
        }
    }
    return;
};

/**
 * @function
 * @param {pyObject} key
 * @param {string} hash_value
 *
 * @return undefined if no key was found
 * or the item if the key was in the bucket
 * also removes the item from entries
 * @private
 */
function pop$bucket_item(key, hash_value) {
    const bucket = this.buckets[hash_value];
    let bucket_key, item;
    if (bucket === undefined) {
        return undefined;
    }
    for (let i = 0; i < bucket.length; i++) {
        item = bucket[i];
        if (item === undefined) {
            continue;
        }
        if (item[0] === key || Sk.misceval.richCompareBool(key, item[0], "Eq")) {
            const key_hash = "#" + hash_value + "_" + i;
            delete this.entries[key_hash];
            bucket[i] = undefined;
            if (bucket.every((x) => x === undefined)) {
                delete this.buckets[hash_value];
            }
            return item;
        }
    }
    return;
};

/**
 * @function
 * @param {Sk.builtin.object} key
 * @param {Sk.builtin.object} value
 * @param {string} hash_value
 *
 * @description
 * given a key and a hash_value will find a free slot or append to the list of slots for a given hash_value
 * then will set the item in the entries and return the item
 * Note this should only be called and immediately preceded by assigning the value to the rhs
 *
 * @private
 */
function set$bucket_item(key, value, hash_value) {
    let key_hash,
        bucket = this.buckets[hash_value];
    const item = [key, value];
    if (bucket === undefined) {
        this.buckets[hash_value] = [item];
        key_hash = "#" + hash_value + "_" + 0;
    } else {
        // we might have a freeslot from deleting an item
        const free_slot_idx = bucket.indexOf(undefined);
        if (free_slot_idx !== -1) {
            key_hash = "#" + hash_value + "_" + free_slot_idx;
            bucket[free_slot_idx] = item;
        } else {
            key_hash = "#" + hash_value + "_" + bucket.length;
            bucket.push(item);
        }
    }
    this.entries[key_hash] = item;
};

/**
 * @function
 * @param {Sk.builtin.object} key - want to check if the key is inside the dict
 *
 * @return undefined if no key was found
 * or the item[1] (value) if the key was found
 * @private
 */
function mp$lookup(key) {
    let item;
    const hash = getHash(key);
    if (typeof hash === "string") {
        item = this.entries[hash];
    } else {
        // then we have a base hash so this is non string;
        item = this.get$bucket_item(key, hash);
    }
    if (item !== undefined) {
        return item[1];
    }
    // Not found in dictionary
    return undefined;
};

/**
 * @function
 *
 * @param {Sk.builtin.dict} b or dictlike object (anything with a keys method)
 *
 * @description
 * this function mimics the cpython implementation, which is also the reason for the
 * almost similar code, this may be changed in future
 *
 * Note we don't use mp$ass_subscript since that slot might be overridden by a subclass
 * Instead we use this.set$item which is the dict implementation of mp$ass_subscript
 * @private
 */
function dict$merge(b) {
    // we don't use mp$ass_subscript incase a subclass overrides __setitem__ we just ignore that like Cpython does
    // so use this.set$item instead which can't be overridden by a subclass
    let keys;
    if (b.tp$iter === Sk.builtin.dict.prototype.tp$iter) {
        // fast way used
        keys = b.tp$iter();
        for (let key = keys.tp$iternext(); key !== undefined; key = keys.tp$iternext()) {
            const v = b.mp$subscript(key);
            this.set$item(key, v);
        }
    } else {
        // generic slower way for a subclass that has overriden the tp$iter method
        // or other mapping types like mapping proxy
        const keyfunc = Sk.abstr.lookupSpecial(b, Sk.builtin.str.$keys);

        return Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(keyfunc, []), (keys) =>
            Sk.misceval.iterFor(Sk.abstr.iter(keys), (key) =>
                Sk.misceval.chain(b.mp$subscript(key, true), (v) => {
                    this.set$item(key, v);
                })
            )
        );
    }
};

/**
 * @function
 *
 * @param {Array} args
 * @param {Array} kwargs
 * @param {string} func_name for error messages
 *
 * @description
 *
 * update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two).
 * If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2).
 * https://hg.python.org/cpython/file/4ff865976bb9/Objects/dictobject.c
 *
 * this function is called by both __init__ and update
 * We check that there is only 1 arg
 *
 * if arg is a dict like object we call dict$merge (must have a keys attribute)
 * otherwise call dict$merge_seq
 *
 * finally put the kwargs in the dict.
 * @private
 *
 */
function update$common(args, kwargs, func_name) {
    Sk.abstr.checkArgsLen(func_name, args, 0, 1);
    const arg = args[0];
    let ret;
    if (arg !== undefined) {
        ret = this.update$onearg(arg);
    }
    return Sk.misceval.chain(ret, () => {
        if (kwargs) {
            for (let i = 0; i < kwargs.length; i += 2) {
                this.set$item(new Sk.builtin.str(kwargs[i]), kwargs[i + 1]);
            }
        }
        return;
    });
};

/**
 * @function
 *
 * @param {pyObject} arg
 *
 * @description
 * iterate over a sequence like object
 * check the next value has length 2
 * and then set the key value pair in
 * @private
 *
 */
function dict$merge_seq(arg) {
    let idx = 0;
    return Sk.misceval.iterFor(Sk.abstr.iter(arg), (i) => {
        if (!Sk.builtin.checkIterable(i)) {
            throw new Sk.builtin.TypeError("cannot convert dictionary update sequence element #" + idx + " to a sequence");
        }
        const seq = Sk.misceval.arrayFromIterable(i);
        if (seq.length !== 2) {
            throw new Sk.builtin.ValueError("dictionary update sequence element #" + idx + " has length " + seq.length + "; 2 is required");
        }
        this.set$item(seq[0], seq[1]);
        idx++;
    });
};

/**
 * @function
 *
 * @param {pyObject} key should be a python object
 * @param {pyObject} value
 *
 * @description
 * sets the item from a key, value
 * @private
 *
 */
function set$item(key, value) {
    const hash = getHash(key);
    let item;
    if (typeof hash === "string") {
        // we have a string so pass it to the dictionary
        item = this.entries[hash];
        if (item === undefined) {
            this.entries[hash] = [key, value];
            this.size++;
            this.$version++;
        } else {
            item[1] = value;
        }
    } else {
        item = this.get$bucket_item(key, hash);
        if (item === undefined) {
            this.set$bucket_item(key, value, hash);
            this.size++;
            this.$version++;
        } else {
            item[1] = value;
        }
    }
};

/**
 * @function
 *
 * @param {Sk.builtin.object} key
 *
 * @description
 * deletes an item in the dictionary
 * @private
 *
 */
function pop$item(key) {
    const hash = getHash(key);
    let item;
    if (typeof hash === "string") {
        item = this.entries[hash];
        delete this.entries[hash];
    } else {
        item = this.pop$bucket_item(key, hash);
    }
    if (item !== undefined) {
        this.size--;
        this.$version++;
        return item;
    }
    // Not found in dictionary
    return undefined;
};


/******** Start of Dict Views ********/

function as_set(self) {
    return new Sk.builtin.set(Sk.misceval.arrayFromIterable(self));
}
function checkAnyView(view) {
    return view instanceof dict_keys || view instanceof dict_items;
}
function all_contained_in(self, other) {
    for (let it = Sk.abstr.iter(self), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
        if (!Sk.abstr.sequenceContains(other, i)) {
            return false;
        }
    }
    return true;
}

// compile shared slots
const dict_view_slots = {
    tp$getattr: Sk.generic.getAttr,
    tp$as_number: true,
    tp$as_sequence_or_mapping: true,
    tp$hash: Sk.builtin.none.none$,
    $r() {
        if (this.in$repr) {
            // prevent recursively calling oneself
            return new Sk.builtin.str("...");
        }
        this.in$repr = true;
        let ret = Sk.misceval.arrayFromIterable(this);
        ret = ret.map((x) => Sk.misceval.objectRepr(x));
        this.in$repr = false;
        return new Sk.builtin.str(Sk.abstr.typeName(this) + "([" + ret.join(", ") + "])");
    },
    tp$richcompare(other, op) {
        if (!(Sk.builtin.checkAnySet(other) || checkAnyView(other))) {
            return Sk.builtin.NotImplemented.NotImplemented$;
        }
        const len_self = this.sq$length();
        const len_other = other.sq$length();
        switch (op) {
            case "NotEq":
            case "Eq":
                let res;
                if (this === other) {
                    res = true;
                } else if (len_self === len_other) {
                    res = all_contained_in(this, other);
                }
                return op === "NotEq" ? !res : res;
            case "Lt":
                return len_self < len_other && all_contained_in(this, other);
            case "LtE":
                return len_self <= len_other && all_contained_in(this, other);
            case "Gt":
                return len_self > len_other && all_contained_in(other, this);
            case "GtE":
                return len_self >= len_other && all_contained_in(other, this);
        }
    },
    nb$subtract(other) {
        const set = as_set(this);
        return set.difference.$meth.call(set, other);
    },
    nb$and(other) {
        const set = as_set(this);
        return set.intersection.$meth.call(set, other);
    },
    nb$or(other) {
        const set = as_set(this);
        return set.union.$meth.call(set, other);
    },
    nb$xor(other) {
        const set = as_set(this);
        return set.symmetric_difference.$meth.call(set, other);
    },
    sq$length() {
        return this.dict.get$size();
    },
};

function buildDictView(typename, slots, reverse_method) {
    const options = {
        constructor: function dict_view(dict) {
            if (arguments.length !== 1) {
                throw new Sk.builtin.TypeError("cannot create '" + Sk.abstr.typeName(this) + "' instances");
            }
            this.dict = dict;
            this.in$repr = false;
        },
    };
    options.slots = Object.assign(slots, dict_view_slots);
    options.methods = {
        isdisjoint: {
            $meth(other) {
                const set = as_set(this);
                return set.isdisjoint.$meth.call(set, other);
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Return True if the view and the given iterable have a null intersection.",
        },
        __reversed__: {
            $meth: reverse_method,
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Return a reverse iterator over the dict keys.",
        },
    };
    options.flags = {
        sk$acceptable_as_base: false,
    };
    if (typename === "dict_values") {
        // dict_values doesn't have number or richcompare slots
        delete options.slots.tp$as_number;
        delete options.slots.tp$richcompare;
    }
    return Sk.abstr.buildNativeClass(typename, options);
}

var dict_keys = buildDictView(
    "dict_keys",
    {
        sq$contains(key) {
            return this.dict.mp$lookup(key) !== undefined;
        },
        tp$iter() {
            return new dict_iter_(this.dict);
        },
    },
    function __reverse__() {
        return new dict_reverse_iter_(this.dict);
    }
);

var dict_values = buildDictView(
    "dict_values",
    {
        tp$iter() {
            return new dict_valueiter_(this.dict);
        },
    },
    function __reverse__() {
        return new dict_reverse_valueiter_(this.dict);
    }
);

var dict_items = buildDictView(
    "dict_items",
    {
        sq$contains(item) {
            if (!(item instanceof Sk.builtin.tuple && item.sq$length() === 2)) {
                return false;
            }
            const key = item.mp$subscript(new Sk.builtin.int_(0));
            const value = item.mp$subscript(new Sk.builtin.int_(1));
            const found = this.dict.mp$lookup(key);
            if (found === undefined) {
                return false;
            }
            return found === value || Sk.misceval.richCompareBool(found, value, "Eq");
        },
        tp$iter() {
            return new dict_itemiter_(this.dict);
        },
    },
    function __reverse__() {
        return new dict_reverse_itemiter_(this.dict);
    }
);

/**
 * @param {string} typename
 * @param {Function} iternext
 * @param {Function=} constructor
 */
function buildDictIterClass(typename, iternext, reversed) {
    return Sk.abstr.buildIteratorClass(typename, {
        constructor: function dict_iter_constructor(dict) {
            this.$index = 0;
            this.$orig = dict;
            this.tp$iternext = () => {
                // only set up the array on the first iteration
                this.$seq = dict.$items();
                this.$version = dict.$version;
                if (reversed) {
                    this.$seq = this.$seq.reverse();
                }
                this.tp$iternext = this.constructor.prototype.tp$iternext;
                return this.tp$iternext();
            };
        },
        iternext: iternext,
        methods: {
            __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef,
        },
        flags: { sk$acceptable_as_base_class: false },
        proto: { next$item: itemIterNextCheckSize },
    });
}

function itemIterNextCheckSize() {
    if (this.$version !== this.$orig.$version) {
        if (this.$len !== this.$orig.get$size()) {
            throw new Sk.builtin.RuntimeError("dict changed size during iteration");
        }
        throw new Sk.builtin.RuntimeError("dictionary keys changed during iteration");
    }
    return this.$seq[this.$index++];
}

/**
 * @constructor
 * @param {Sk.builtin.dict} dict
 */
var dict_iter_ = buildDictIterClass("dict_keyiterator", function () {
    const item = this.next$item();
    return item && item[0];
});

/**
 * @constructor
 * @param {Sk.builtin.dict} dict
 */
var dict_itemiter_ = buildDictIterClass("dict_itemiterator", function () {
    const item = this.next$item();
    return item && new Sk.builtin.tuple([item[0], item[1]]);
});

/**
 * @constructor
 * @param {Sk.builtin.dict} dict
 */
var dict_valueiter_ = buildDictIterClass("dict_valueiterator", function () {
    const item = this.next$item();
    return item && item[1];
});

var dict_reverse_iter_ = buildDictIterClass("dict_reversekeyiterator", dict_iter_.prototype.tp$iternext, true);
var dict_reverse_itemiter_ = buildDictIterClass("dict_reverseitemiterator", dict_itemiter_.prototype.tp$iternext, true);
var dict_reverse_valueiter_ = buildDictIterClass("dict_reversevalueiterator", dict_valueiter_.prototype.tp$iternext, true);

/**
 * Py2 methods
 * @private
 */
Sk.builtin.dict.py2$methods = {
    has_key: {
        $name: "has_key",
        $flags: { OneArg: true },
        $meth(k) {
            return new Sk.builtin.bool(this.sq$contains(k));
        },
        $doc: "D.has_key(k) -> True if D has a key k, else False",
    },
    keys: {
        $name: "keys",
        $meth() {
            return new Sk.builtin.list(this.sk$asarray());
        },
        $flags: { NoArgs: true },
        $textsig: null,
        $doc: "D.keys() -> a set-like object providing a view on D's keys",
    },
    items: {
        $name: "items",
        $meth() {
            return new Sk.builtin.list(this.$items().map(([key, val]) => new Sk.builtin.tuple([key, val])));
        },
        $flags: { NoArgs: true },
        $textsig: null,
        $doc: "D.items() -> a set-like object providing a view on D's items",
    },
    values: {
        $name: "values",
        $meth() {
            return new Sk.builtin.list(this.$items().map(([_, val]) => val));
        },
        $flags: { NoArgs: true },
        $textsig: null,
        $doc: "D.values() -> an object providing a view on D's values",
    },
};


/***/ }),

/***/ "./src/enumerate.js":
/*!**************************!*\
  !*** ./src/enumerate.js ***!
  \**************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {pyObject} iterable
 * @param {number|string=} start
 * @extends Sk.builtin.object
 */
Sk.builtin.enumerate = Sk.abstr.buildIteratorClass("enumerate", {
    constructor: function enumerate(iterable, start) {
        if (!(this instanceof Sk.builtin.enumerate)) {
            throw TypeError("Failed to construct 'enumerate': Please use the 'new' operator");
        }
        this.$iterable = iterable;
        this.$index = start;
        return this;
    },
    iternext(canSuspend) {
        const ret = Sk.misceval.chain(this.$iterable.tp$iternext(canSuspend), (i) => {
            if (i === undefined) {
                return undefined;
            }
            return new Sk.builtin.tuple([new Sk.builtin.int_(this.$index++), i]);
        });
        return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
    },
    slots: {
        tp$doc:
            "Return an enumerate object.\n\n  iterable\n    an object supporting iteration\n\nThe enumerate object yields pairs containing a count (from start, which\ndefaults to zero) and a value yielded by the iterable argument.\n\nenumerate is useful for obtaining an indexed list:\n    (0, seq[0]), (1, seq[1]), (2, seq[2]), ...",
        tp$new(args, kwargs) {
            let [iterable, start] = Sk.abstr.copyKeywordsToNamedArgs("enumerate", ["iterable", "start"], args, kwargs, [new Sk.builtin.int_(0)]);
            iterable = Sk.abstr.iter(iterable);
            start = Sk.misceval.asIndexOrThrow(start);
            if (this === Sk.builtin.enumerate.prototype) {
                return new Sk.builtin.enumerate(iterable, start);
            } else {
                const instance = new this.constructor();
                Sk.builtin.enumerate.call(instance, iterable, start);
                return instance;
            }
        },
    },
});
Sk.exportSymbol("Sk.builtin.enumerate", Sk.builtin.enumerate);

/***/ }),

/***/ "./src/env.js":
/*!********************!*\
  !*** ./src/env.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Base namespace for Skulpt. This is the only symbol that Skulpt adds to the
 * global namespace. Other user accessible symbols are noted and described
 * below.
 */

/**
 *
 * Set various customizable parts of Skulpt.
 *
 * output: Replacable output redirection (called from print, etc.).
 * read: Replacable function to load modules with (called via import, etc.)
 * sysargv: Setable to emulate arguments to the script. Should be an array of JS
 * strings.
 * syspath: Setable to emulate PYTHONPATH environment variable (for finding
 * modules). Should be an array of JS strings.
 * nonreadopen: Boolean - set to true to allow non-read file operations
 * fileopen: Optional function to call any time a file is opened
 * filewrite: Optional function to call when writing to a file
 *
 * Any variables that aren't set will be left alone.
 */

Sk.bool_check = function (variable, name) {
    if (variable === undefined || variable === null || typeof variable !== "boolean") {
        throw new Error("must specify " + name + " and it must be a boolean");
    }
};

/**
 * Please use python3 flag to control new behavior that is different
 * between Python 2/3, rather than adding new flags.
 */

Sk.python2 = {
    print_function: false,
    division: false,
    absolute_import: null,
    unicode_literals: false,
    // skulpt specific
    python3: false,
    class_repr: false,
    inherit_from_object: false,
    super_args: false,
    octal_number_literal: false,
    bankers_rounding: false,
    python_version: false,
    dunder_round: false,
    exceptions: false,
    no_long_type: false,
    ceil_floor_int: false,
    silent_octal_literal: true,
};

Sk.python3 = {
    print_function: true,
    division: true,
    absolute_import: null,
    unicode_literals: true,
    // skulpt specific
    python3: true,
    class_repr: true,
    inherit_from_object: true,
    super_args: true,
    octal_number_literal: true,
    bankers_rounding: true,
    python_version: true,
    dunder_round: true,
    exceptions: true,
    no_long_type: true,
    ceil_floor_int: true,
    silent_octal_literal: false,
};

Sk.configure = function (options) {
    "use strict";
    Sk.output = options["output"] || Sk.output;
    Sk.asserts.assert(typeof Sk.output === "function");

    Sk.debugout = options["debugout"] || Sk.debugout;
    Sk.asserts.assert(typeof Sk.debugout === "function");

    Sk.uncaughtException = options["uncaughtException"] || Sk.uncaughtException;
    Sk.asserts.assert(typeof Sk.uncaughtException === "function");

    Sk.read = options["read"] || Sk.read;
    Sk.asserts.assert(typeof Sk.read === "function");

    Sk.nonreadopen = options["nonreadopen"] || false;
    Sk.asserts.assert(typeof Sk.nonreadopen === "boolean");

    Sk.fileopen = options["fileopen"] || undefined;
    Sk.asserts.assert(typeof Sk.fileopen === "function" || typeof Sk.fileopen === "undefined");

    Sk.filewrite = options["filewrite"] || undefined;
    Sk.asserts.assert(typeof Sk.filewrite === "function" || typeof Sk.filewrite === "undefined");

    Sk.timeoutMsg = options["timeoutMsg"] || Sk.timeoutMsg;
    Sk.asserts.assert(typeof Sk.timeoutMsg === "function");
    Sk.exportSymbol("Sk.timeoutMsg", Sk.timeoutMsg);

    Sk.sysargv = options["sysargv"] || Sk.sysargv;
    Sk.asserts.assert(Sk.isArrayLike(Sk.sysargv));

    Sk.__future__ = options["__future__"] || Sk.python3;

    Sk.bool_check(Sk.__future__.print_function, "Sk.__future__.print_function");
    Sk.bool_check(Sk.__future__.division, "Sk.__future__.division");
    Sk.bool_check(Sk.__future__.unicode_literals, "Sk.__future__.unicode_literals");
    Sk.bool_check(Sk.__future__.class_repr, "Sk.__future__.class_repr");
    Sk.bool_check(Sk.__future__.inherit_from_object, "Sk.__future__.inherit_from_object");
    Sk.bool_check(Sk.__future__.super_args, "Sk.__future__.super_args");
    Sk.bool_check(Sk.__future__.octal_number_literal, "Sk.__future__.octal_number_literal");
    Sk.bool_check(Sk.__future__.bankers_rounding, "Sk.__future__.bankers_rounding");
    Sk.bool_check(Sk.__future__.python_version, "Sk.__future__.python_version");
    Sk.bool_check(Sk.__future__.dunder_round, "Sk.__future__.dunder_round");
    Sk.bool_check(Sk.__future__.exceptions, "Sk.__future__.exceptions");
    Sk.bool_check(Sk.__future__.no_long_type, "Sk.__future__.no_long_type");
    Sk.bool_check(Sk.__future__.ceil_floor_int, "Sk.__future__.ceil_floor_int");
    Sk.bool_check(Sk.__future__.silent_octal_literal, "Sk.__future__.silent_octal_literal");

    // in __future__ add checks for absolute_import

    Sk.imageProxy = options["imageProxy"] || "http://localhost:8080/320x";
    Sk.asserts.assert(typeof Sk.imageProxy === "string" || typeof Sk.imageProxy === "function");

    Sk.inputfun = options["inputfun"] || Sk.inputfun;
    Sk.asserts.assert(typeof Sk.inputfun === "function");

    Sk.inputfunTakesPrompt = options["inputfunTakesPrompt"] || false;
    Sk.asserts.assert(typeof Sk.inputfunTakesPrompt === "boolean");

    Sk.retainGlobals = options["retainglobals"] || options["retainGlobals"] || false;
    Sk.asserts.assert(typeof Sk.retainGlobals === "boolean");

    Sk.debugging = options["debugging"] || false;
    Sk.asserts.assert(typeof Sk.debugging === "boolean");

    Sk.killableWhile = options["killableWhile"] || false;
    Sk.asserts.assert(typeof Sk.killableWhile === "boolean");

    Sk.killableFor = options["killableFor"] || false;
    Sk.asserts.assert(typeof Sk.killableFor === "boolean");

    Sk.signals = typeof options["signals"] !== undefined ? options["signals"] : null;
    if (Sk.signals === true) {
        Sk.signals = {
            listeners: [],
            addEventListener(handler) {
                Sk.signals.listeners.push(handler);
            },
            removeEventListener(handler) {
                var index = Sk.signals.listeners.indexOf(handler);
                if (index >= 0) {
                    Sk.signals.listeners.splice(index, 1); // Remove items
                }
            },
            signal(signal, data) {
                for (var i = 0; i < Sk.signals.listeners.length; i++) {
                    Sk.signals.listeners[i].call(null, signal, data);
                }
            },
        };
    } else {
        Sk.signals = null;
    }
    Sk.asserts.assert(typeof Sk.signals === "object");

    Sk.breakpoints =
        options["breakpoints"] ||
        function () {
            return true;
        };
    Sk.asserts.assert(typeof Sk.breakpoints === "function");

    Sk.setTimeout = options["setTimeout"];
    if (Sk.setTimeout === undefined) {
        if (typeof setTimeout === "function") {
            Sk.setTimeout = function (func, delay) {
                setTimeout(func, delay);
            };
        } else {
            Sk.setTimeout = function (func, delay) {
                func();
            };
        }
    }
    Sk.asserts.assert(typeof Sk.setTimeout === "function");

    if ("execLimit" in options) {
        Sk.execLimit = options["execLimit"];
    }

    if ("yieldLimit" in options) {
        Sk.yieldLimit = options["yieldLimit"];
    }

    if (options["syspath"]) {
        Sk.syspath = options["syspath"];
        Sk.asserts.assert(Sk.isArrayLike(Sk.syspath));
        // assume that if we're changing syspath we want to force reimports.
        // not sure how valid this is, perhaps a separate api for that.
        Sk.realsyspath = undefined;
        Sk.sysmodules = new Sk.builtin.dict([]);
    }

    Sk.misceval.softspace_ = false;

    Sk.switch_version(Sk.__future__.python3);

    Sk.builtin.str.$next = Sk.__future__.python3 ? new Sk.builtin.str("__next__") : new Sk.builtin.str("next");

    Sk.setupOperators(Sk.__future__.python3);
    Sk.setupDunderMethods(Sk.__future__.python3);
    Sk.setupObjects(Sk.__future__.python3);
    Sk.token.setupTokens(Sk.__future__.python3);
};

Sk.exportSymbol("Sk.configure", Sk.configure);

/*
 * Replaceable handler for uncaught exceptions
 */
Sk.uncaughtException = function (err) {
    throw err;
};

/*
 * Replaceable handler for uncaught exceptions
 */
Sk.uncaughtException = function (err) {
    throw err;
};
Sk.exportSymbol("Sk.uncaughtException", Sk.uncaughtException);

/*
 *      Replaceable message for message timeouts
 */
Sk.timeoutMsg = function () {
    return "Program exceeded run time limit.";
};
Sk.exportSymbol("Sk.timeoutMsg", Sk.timeoutMsg);

/*
 *  Hard execution timeout, throws an error. Set to null to disable
 */
Sk.execLimit = Number.POSITIVE_INFINITY;

/*
 *  Soft execution timeout, returns a Suspension. Set to null to disable
 */
Sk.yieldLimit = Number.POSITIVE_INFINITY;

/*
 * Replacable output redirection (called from print, etc).
 */
Sk.output = function (x) {};

/*
 * Replaceable function to load modules with (called via import, etc.)
 * todo; this should be an async api
 */
Sk.read = function (x) {
    if (Sk.builtinFiles === undefined) {
        throw "skulpt-stdlib.js has not been loaded";
    } else if (Sk.builtinFiles.files[x] === undefined) {
        throw "File not found: '" + x + "'";
    }
    return Sk.builtinFiles.files[x];
};

/*
 * Setable to emulate arguments to the script. Should be array of JS strings.
 */
Sk.sysargv = [];

// lame function for sys module
Sk.getSysArgv = function () {
    return Sk.sysargv;
};
Sk.exportSymbol("Sk.getSysArgv", Sk.getSysArgv);

/**
 * Setable to emulate PYTHONPATH environment variable (for finding modules).
 * Should be an array of JS strings.
 */
Sk.syspath = [];

Sk.inBrowser = Sk.global["document"] !== undefined;

/**
 * Internal function used for debug output.
 * @param {...} args
 */
Sk.debugout = function (args) {};

(function () {
    // set up some sane defaults based on availability
    if (Sk.global["write"] !== undefined) {
        Sk.output = Sk.global["write"];
    } else if (Sk.global["console"] !== undefined && Sk.global["console"]["log"] !== undefined) {
        Sk.output = function (x) {
            Sk.global["console"]["log"](x);
        };
    } else if (Sk.global["print"] !== undefined) {
        Sk.output = Sk.global["print"];
    }
    if (Sk.global["console"] !== undefined && Sk.global["console"]["log"] !== undefined) {
        Sk.debugout = function (x) {
            Sk.global["console"]["log"](x);
        };
    } else if (Sk.global["print"] !== undefined) {
        Sk.debugout = Sk.global["print"];
    }
})();

Sk.inputfun = function (args) {
    return window.prompt(args);
};

/**
 * currently can't seem to remove these functions without a serious slow down of 2x
 */
Sk.setup_method_mappings = function () {
};
Sk.setupDictIterators = function (python3) {
};

Sk.switch_version = function (py3) {
    const methods_to_map = {
        float_: {
            method_names: ["__round__"],
            2: [false],
            3: [true],
        },
        int_: {
            method_names: ["__round__"],
            2: [false],
            3: [true],
        },
        list: {
            method_names: ["clear", "copy", "sort"],
            2: [false, false, true],
            3: [true, true, true],
        },
        dict: {
            method_names: ["has_key", "keys", "items", "values"],
            2: [true, true, true, true],
            3: [false, true, true, true],
        },
    };

    for (let klass_name in methods_to_map) {
        const klass = Sk.builtin[klass_name];
        const method_names = methods_to_map[klass_name].method_names;
        const in_py3 = methods_to_map[klass_name][3];

        // if we're not changing to py2 and we have no py3$methods then don't continue since these methods exist by default
        if (py3 && klass.py3$methods === undefined) {
            return;
        } else if (klass.py3$methods === undefined) {
            // Set up py3$methods if we haven't done so already
            klass.py3$methods = {};
            for (let i = 0; i < method_names.length; i++) {
                const method_name = method_names[i];
                if (!in_py3[i]) {
                    continue;
                }
                klass.py3$methods[method_name] = klass.prototype[method_name].d$def;
            }
        }
        let in_version, new_methods;
        if (py3) {
            in_version = in_py3;
            new_methods = klass.py3$methods;
        } else {
            in_version = methods_to_map[klass_name][2];
            new_methods = klass.py2$methods;
        }
        for (let i = 0; i < method_names.length; i++) {
            const method_name = method_names[i];
            delete klass.prototype[method_name];
            if (in_version[i]) {
                klass.prototype[method_name] = new Sk.builtin.method_descriptor(klass, new_methods[method_name]);
            }
        }
    }
};

Sk.exportSymbol("Sk.__future__", Sk.__future__);
Sk.exportSymbol("Sk.inputfun", Sk.inputfun);


/***/ }),

/***/ "./src/errors.js":
/*!***********************!*\
  !*** ./src/errors.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports) {

/*
 * The filename, line number, and column number of exceptions are
 * stored within the exception object.  Note that not all exceptions
 * clearly report the column number.  To customize the exception
 * message to use any/all of these fields, you can either modify
 * tp$str below to print the desired message, or use them in the
 * skulpt wrapper (i.e., runit) to present the exception message.
 */

/**
 * @constructor
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.BaseException = Sk.abstr.buildNativeClass("BaseException", {
    constructor: function Exception(...args) {
        // internally args is either a string
        Sk.asserts.assert(this instanceof Sk.builtin.BaseException, "bad call to exception constructor, use 'new'");

        // for all internal calls only the first argument is included in args
        let arg = args[0];
        if (typeof arg === "string") {
            arg = new Sk.builtin.str(arg);
        }
        this.args = new Sk.builtin.tuple(arg ? [arg] : []);
        this.traceback = [];
        this.$d = new Sk.builtin.dict();

        if (args.length >= 3) {
            // For errors occurring during normal execution, the line/col/etc
            // of the error are populated by each stack frame of the runtime code,
            // but we can seed it with the supplied parameters.
            this.traceback.push({
                lineno: args[2],
                filename: args[1] || "<unknown>",
            });
        }
    },
    slots: /**@lends {Sk.builtin.BaseException}*/ {
        tp$getattr: Sk.generic.getAttr,
        tp$doc: "Common base class for all exceptions",
        tp$new(args, kwargs) {
            let instance;
            if (!this.hp$type) {
                // then we have a builtin constructor so just return it as new this
                instance = new this.constructor();
            } else {
                instance = new this.constructor();
                Sk.builtin.BaseException.call(instance);
            }
            // called from python so do the args here
            instance.args = new Sk.builtin.tuple(args.slice()); // reset args in __init__ method
            return instance;
        },
        tp$init(args, kwargs) {
            Sk.abstr.checkNoKwargs(Sk.abstr.typeName(this), kwargs);
        },
        $r() {
            let ret = this.tp$name;
            ret += "(" + this.args.v.map((x) => Sk.misceval.objectRepr(x)).join(", ") + ")";
            return new Sk.builtin.str(ret);
        },
        tp$str() {
            if (this.args.v.length <= 1) {
                return new Sk.builtin.str(this.args.v[0]);
            }
            return this.args.$r();
        },
    },
    getsets: /**@lends {Sk.builtin.BaseException}*/ {
        args: {
            $get() {
                return this.args;
            },
        },
        __dict__: Sk.generic.getSetDict,
    },
    proto: /**@lends {Sk.builtin.BaseException}*/ {
        toString() {
            let ret = this.tp$name;
            ret += ": " + this.tp$str().v;

            if (this.traceback.length !== 0) {
                ret += " on line " + this.traceback[0].lineno;
            } else {
                ret += " at <unknown>";
            }

            // if (this.args.v.length > 4) {
            //     ret += "\n" + this.args.v[4].v + "\n";
            //     for (let i = 0; i < this.args.v[3]; ++i) {
            //         ret += " ";
            //     }
            //     ret += "^\n";
            // }

            /*for (i = 0; i < this.traceback.length; i++) {
                ret += "\n  at " + this.traceback[i].filename + " line " + this.traceback[i].lineno;
                if ("colno" in this.traceback[i]) {
                    ret += " column " + this.traceback[i].colno;
                }
            }*/

            return ret;
        },
    },
});

Sk.exportSymbol("Sk.builtin.BaseException", Sk.builtin.BaseException);

/**
 * @constructor
 * @extends Sk.builtin.BaseException
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.Exception = Sk.abstr.buildNativeClass("Exception", {
    constructor: function Exception(...args) {
        Sk.builtin.BaseException.apply(this, args);
    },
    base: Sk.builtin.BaseException,
});
Sk.exportSymbol("Sk.builtin.Exception", Sk.builtin.Exception);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.AssertionError = Sk.abstr.buildNativeClass("AssertionError", {
    constructor: function AssertionError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.AssertionError", Sk.builtin.AssertionError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.AttributeError = Sk.abstr.buildNativeClass("AttributeError", {
    constructor: function AttributeError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.ImportError = Sk.abstr.buildNativeClass("ImportError", {
    constructor: function ImportError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.IndentationError = Sk.abstr.buildNativeClass("IndentationError", {
    constructor: function IndentationError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.IndexError = Sk.abstr.buildNativeClass("IndexError", {
    constructor: function IndexError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.LookupError = Sk.abstr.buildNativeClass("LookupError", {
    constructor: function LookupError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.LookupError
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.KeyError = Sk.abstr.buildNativeClass("KeyError", {
    constructor: function KeyError(...args) {
        Sk.builtin.LookupError.apply(this, args);
    },
    base: Sk.builtin.LookupError,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.NameError = Sk.abstr.buildNativeClass("NameError", {
    constructor: function NameError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.UnboundLocalError = Sk.abstr.buildNativeClass("UnboundLocalError", {
    constructor: function UnboundLocalError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.OverflowError = Sk.abstr.buildNativeClass("OverflowError", {
    constructor: function OverflowError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args
 */
Sk.builtin.SyntaxError = Sk.abstr.buildNativeClass("SyntaxError", {
    constructor: function SyntaxError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.RuntimeError = Sk.abstr.buildNativeClass("RuntimeError", {
    constructor: function RuntimeError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.RuntimeError", Sk.builtin.RuntimeError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.SuspensionError = Sk.abstr.buildNativeClass("SuspensionError", {
    constructor: function SuspensionError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.SuspensionError", Sk.builtin.SuspensionError);

/**
 * @constructor
 * @extends Sk.builtin.BaseException
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.SystemExit = Sk.abstr.buildNativeClass("SystemExit", {
    constructor: function SystemExit(...args) {
        Sk.builtin.BaseException.apply(this, args);
    },
    base: Sk.builtin.BaseException,
});
Sk.exportSymbol("Sk.builtin.SystemExit", Sk.builtin.SystemExit);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.TypeError = Sk.abstr.buildNativeClass("TypeError", {
    constructor: function TypeError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.TypeError", Sk.builtin.TypeError);
/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.ValueError = Sk.abstr.buildNativeClass("ValueError", {
    constructor: function ValueError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.ValueError", Sk.builtin.ValueError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.ZeroDivisionError = Sk.abstr.buildNativeClass("ZeroDivisionError", {
    constructor: function ZeroDivisionError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.TimeLimitError = Sk.abstr.buildNativeClass("TimeLimitError", {
    constructor: function TimeLimitError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.TimeLimitError", Sk.builtin.TimeLimitError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.IOError = Sk.abstr.buildNativeClass("IOError", {
    constructor: function IOError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.IOError", Sk.builtin.IOError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.NotImplementedError = Sk.abstr.buildNativeClass("NotImplementedError", {
    constructor: function NotImplementedError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.NotImplementedError", Sk.builtin.NotImplementedError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.NegativePowerError = Sk.abstr.buildNativeClass("NegativePowerError", {
    constructor: function NegativePowerError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.NegativePowerError", Sk.builtin.NegativePowerError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args
 */
Sk.builtin.ExternalError = Sk.abstr.buildNativeClass("ExternalError", {
    constructor: function ExternalError(...args) {
        this.nativeError = args[0];
        if (!Sk.builtin.checkString(this.nativeError)) {
            args[0] = this.nativeError.toString();
            if (args[0].startsWith("RangeError: Maximum call")) {
                args[0] = "Maximum call stack size exceeded";
                return new Sk.builtin.RecursionError(...args);
            }
        }
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.ExternalError", Sk.builtin.ExternalError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.RecursionError = Sk.abstr.buildNativeClass("RecursionError", {
    constructor: function RecursionError(...args) {
        Sk.builtin.RuntimeError.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.RecursionError", Sk.builtin.RecursionError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.OperationError = Sk.abstr.buildNativeClass("OperationError", {
    constructor: function OperationError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.OperationError", Sk.builtin.OperationError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.SystemError = Sk.abstr.buildNativeClass("SystemError", {
    constructor: function SystemError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.SystemError", Sk.builtin.SystemError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.UnicodeDecodeError = Sk.abstr.buildNativeClass("UnicodeDecodeError", {
    constructor: function UnicodeDecodeError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.UnicodeDecodeError", Sk.builtin.UnicodeDecodeError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args
 */
Sk.builtin.UnicodeEncodeError = Sk.abstr.buildNativeClass("UnicodeEncodeError", {
    constructor: function UnicodeEncodeError(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.UnicodeEncodeError", Sk.builtin.UnicodeEncodeError);

/**
 * @constructor
 * @extends Sk.builtin.Exception
 * @param {...} args Typically called with a single string argument
 */
Sk.builtin.StopIteration = Sk.abstr.buildNativeClass("StopIteration", {
    constructor: function StopIteration(...args) {
        Sk.builtin.Exception.apply(this, args);
    },
    base: Sk.builtin.Exception,
});
Sk.exportSymbol("Sk.builtin.StopIteration", Sk.builtin.StopIteration);

// TODO: Extract into sys.exc_info(). Work out how the heck
// to find out what exceptions are being processed by parent stack frames...
Sk.builtin.getExcInfo = function (e) {
    var v = [e.ob$type || Sk.builtin.none.none$, e, Sk.builtin.none.none$];

    // TODO create a Traceback object for the third tuple element

    return new Sk.builtin.tuple(v);
};
// NOT exported


/***/ }),

/***/ "./src/ffi.js":
/*!********************!*\
  !*** ./src/ffi.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @namespace Sk.ffi
 *
 */

Sk.ffi = Sk.ffi || {};

/**
 * maps from Javascript Object/Array/string to Python dict/list/str.
 *
 * only works on basic objects that are being used as storage, doesn't handle
 * functions, etc.
 */
Sk.ffi.remapToPy = function (obj) {
    var k;
    var kvs;
    var i;
    var arr;

    if (obj === null || typeof obj === "undefined") {
        return Sk.builtin.none.none$;
    }

    if (obj.ob$type) {
        return obj;
    }

    if (obj instanceof Sk.misceval.Suspension) {
        return obj;
    }

    if (Object.prototype.toString.call(obj) === "[object Array]") {
        arr = [];
        for (i = 0; i < obj.length; ++i) {
            arr.push(Sk.ffi.remapToPy(obj[i]));
        }
        return new Sk.builtin.list(arr);
    }

    if (typeof obj === "object") {
        kvs = [];
        for (k in obj) {
            kvs.push(Sk.ffi.remapToPy(k));
            kvs.push(Sk.ffi.remapToPy(obj[k]));
        }
        return new Sk.builtin.dict(kvs);
    }

    if (typeof obj === "string") {
        return new Sk.builtin.str(obj);
    }

    if (typeof obj === "number") {
        return Sk.builtin.assk$(obj);
    }

    if (typeof obj === "boolean") {
        return new Sk.builtin.bool(obj);
    } else if (typeof obj === "undefined") {
        return Sk.builtin.none.none$;
    }

    if (typeof obj === "function") {
        return new Sk.builtin.func(obj);
    }

    Sk.asserts.fail("unhandled remap type " + typeof(obj));
};
Sk.exportSymbol("Sk.ffi.remapToPy", Sk.ffi.remapToPy);

/**
 * Maps from Python dict/list/str/number to Javascript Object/Array/string/number.
 *
 * If obj is a
 *
 * @param obj {Object}  Any Python object (except a function)
 *
 */
Sk.ffi.remapToJs = function (obj) {
    var i;
    var kAsJs;
    var ret;
    if (obj instanceof Sk.builtin.dict) {
        ret = {};
        obj.$items().forEach(([key, val]) => {
            kAsJs = Sk.ffi.remapToJs(key);
            // todo; assert that this is a reasonble lhs?
            ret[kAsJs] = Sk.ffi.remapToJs(val);
        });
        return ret;
    } else if (obj instanceof Sk.builtin.list || obj instanceof Sk.builtin.tuple) {
        ret = [];
        for (i = 0; i < obj.v.length; ++i) {
            ret.push(Sk.ffi.remapToJs(obj.v[i]));
        }
        return ret;
    } else if (obj instanceof Sk.builtin.bool) {
        return obj.v ? true : false;
    } else if (obj instanceof Sk.builtin.int_) {
        return Sk.builtin.asnum$(obj);
    } else if (obj instanceof Sk.builtin.float_) {
        return Sk.builtin.asnum$(obj);
    } else if (obj instanceof Sk.builtin.lng) {
        return Sk.builtin.asnum$(obj);
    } else if (typeof obj === "number" || typeof obj === "boolean" || typeof obj === "string") {
        return obj;
    } else if (obj === undefined) {
        return undefined;
    } else {
        return obj.v;
    }
};
Sk.exportSymbol("Sk.ffi.remapToJs", Sk.ffi.remapToJs);

Sk.ffi.callback = function (fn) {
    if (fn === undefined) {
        return fn;
    }
    return function () {
        return Sk.misceval.apply(fn, undefined, undefined, undefined, Array.prototype.slice.call(arguments, 0));
    };
};
Sk.exportSymbol("Sk.ffi.callback", Sk.ffi.callback);

Sk.ffi.stdwrap = function (type, towrap) {
    var inst = new type();
    inst["v"] = towrap;
    return inst;
};
Sk.exportSymbol("Sk.ffi.stdwrap", Sk.ffi.stdwrap);

/**
 * for when the return type might be one of a variety of basic types.
 * number|string, etc.
 */
Sk.ffi.basicwrap = function (obj) {
    if (obj instanceof Sk.builtin.int_) {
        return Sk.builtin.asnum$(obj);
    }
    if (obj instanceof Sk.builtin.float_) {
        return Sk.builtin.asnum$(obj);
    }
    if (obj instanceof Sk.builtin.lng) {
        return Sk.builtin.asnum$(obj);
    }
    if (typeof obj === "number" || typeof obj === "boolean") {
        return obj;
    }
    if (typeof obj === "string") {
        return new Sk.builtin.str(obj);
    }
    Sk.asserts.fail("unexpected type for basicwrap");
};
Sk.exportSymbol("Sk.ffi.basicwrap", Sk.ffi.basicwrap);

Sk.ffi.unwrapo = function (obj) {
    if (obj === undefined) {
        return undefined;
    }
    return obj["v"];
};
Sk.exportSymbol("Sk.ffi.unwrapo", Sk.ffi.unwrapo);

Sk.ffi.unwrapn = function (obj) {
    if (obj === null) {
        return null;
    }
    return obj["v"];
};
Sk.exportSymbol("Sk.ffi.unwrapn", Sk.ffi.unwrapn);


/***/ }),

/***/ "./src/file.js":
/*!*********************!*\
  !*** ./src/file.js ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Sk.builtin.str} name
 * @param {Sk.builtin.str} mode
 * @param {Object} buffering
 * 
 * @todo - adjust this to be more inline with cpython implementation and use new api
 */
Sk.builtin.file = function (name, mode, buffering) {
    var i;
    var elem;

    if (!(this instanceof Sk.builtin.file)) {
        return new Sk.builtin.file(name, mode, buffering);
    }

    this.mode = mode;
    this.name = Sk.ffi.remapToJs(name);
    this.closed = false;

    if (this.name === "/dev/stdout") {
        this.data$ = Sk.builtin.none.none$;
        this.fileno = 1;
    } else if (this.name === "/dev/stdin") {
        this.fileno = 0;
    } else if (this.name === "/dev/stderr") {
        this.fileno = 2;
    } else {
        if (Sk.inBrowser) {  // todo:  Maybe provide a replaceable function for non-import files
            this.fileno = 10;
            elem = document.getElementById(name.v);
            if (elem == null) {
                if (mode.v == "w" || mode.v == "a") {
                    this.data$ = "";
                } else {
                    throw new Sk.builtin.IOError("[Errno 2] No such file or directory: '" + name.v + "'");
                }
            } else {
                if (elem.nodeName.toLowerCase() == "textarea") {
                    this.data$ = elem.value;
                } else {
                    this.data$ = elem.textContent;
                }
            }
        } else {
            this.fileno = 11;
            this.data$ = Sk.read(name.v);
        }

        this.lineList = this.data$.split("\n");
        this.lineList = this.lineList.slice(0, -1);

        for (i in this.lineList) {
            this.lineList[i] = this.lineList[i] + "\n";
        }
        this.currentLine = 0;
    }
    this.pos$ = 0;


    if (Sk.fileopen && this.fileno >= 10) {
        Sk.fileopen(this);
    }

    return this;
};

Sk.abstr.setUpInheritance("file", Sk.builtin.file, Sk.builtin.object);
Sk.abstr.setUpBuiltinMro(Sk.builtin.file);

Sk.builtin.file.prototype["$r"] = function () {
    return new Sk.builtin.str("<" +
        (this.closed ? "closed" : "open") +
        "file '" +
        this.name +
        "', mode '" +
        Sk.ffi.remapToJs(this.mode) +
        "'>");
};

Sk.builtin.file.prototype.tp$iter = function () {
    var allLines = this.lineList;
    var currentLine = this.currentLine;

    var ret =
    {
        tp$iter    : function () {
            return ret;
        },
        $obj       : this,
        $index     : currentLine,
        $lines     : allLines,
        tp$iternext: function () {
            if (ret.$index >= ret.$lines.length) {
                return undefined;
            }
            return new Sk.builtin.str(ret.$lines[ret.$index++]);
        }
    };
    return ret;
};

Sk.abstr.setUpSlots(Sk.builtin.file);

Sk.builtin.file.prototype["__enter__"] = new Sk.builtin.func(function __enter__(self) {
    return self;
});

Sk.builtin.file.prototype["__exit__"] = new Sk.builtin.func(function __exit__(self) {
    return Sk.misceval.callsimArray(Sk.builtin.file.prototype["close"], [self]);
});



Sk.builtin.file.prototype["close"] = new Sk.builtin.func(function close(self) {
    self.closed = true;
    return Sk.builtin.none.none$;
});

Sk.builtin.file.prototype["flush"] = new Sk.builtin.func(function flush(self) {
});

Sk.builtin.file.prototype["fileno"] = new Sk.builtin.func(function fileno(self) {
    return this.fileno;
}); // > 0, not 1/2/3

Sk.builtin.file.prototype["isatty"] = new Sk.builtin.func(function isatty(self) {
    return false;
});

Sk.builtin.file.prototype["read"] = new Sk.builtin.func(function read(self, size) {
    var ret;
    var len = self.data$.length;
    var l_size;
    if (self.closed) {
        throw new Sk.builtin.ValueError("I/O operation on closed file");
    }

    if (size === undefined) {
        l_size = len;
    } else {
        l_size = Sk.ffi.remapToJs(size);
    }

    ret = new Sk.builtin.str(self.data$.substr(self.pos$, l_size));
    if(size === undefined){
        self.pos$ = len;
    }else{
        self.pos$ += Sk.ffi.remapToJs(size);
    }
    if (self.pos$ >= len) {
        self.pos$ = len;
    }

    return ret;
});

Sk.builtin.file.$readline = function (self, size, prompt) {
    if (self.fileno === 0) {
        var x, susp;

        var lprompt = Sk.ffi.remapToJs(prompt);

        lprompt = lprompt ? lprompt : "";

        x = Sk.inputfun(lprompt);

        if (x instanceof Promise || (x && typeof x.then === "function")) {
            susp = new Sk.misceval.Suspension();

            susp.resume = function() {
                if (susp.data.error) {
                    throw susp.data.error;
                }

                return new Sk.builtin.str(susp.data.result);
            };

            susp.data = {
                type: "Sk.promise",
                promise: x
            };

            return susp;
        } else {
            return new Sk.builtin.str(x);
        }
    } else {
        var line = "";
        if (self.currentLine < self.lineList.length) {
            line = self.lineList[self.currentLine];
            self.currentLine++;
        }
        return new Sk.builtin.str(line);
    }
};

Sk.builtin.file.prototype["readline"] = new Sk.builtin.func(function readline(self, size) {
    return Sk.builtin.file.$readline(self, size, undefined);
});

Sk.builtin.file.prototype["readlines"] = new Sk.builtin.func(function readlines(self, sizehint) {
    if (self.fileno === 0) {
        return new Sk.builtin.NotImplementedError("readlines ins't implemented because the web doesn't support Ctrl+D");
    }

    var i;
    var arr = [];
    for (i = self.currentLine; i < self.lineList.length; i++) {
        arr.push(new Sk.builtin.str(self.lineList[i]));
    }
    return new Sk.builtin.list(arr);
});

Sk.builtin.file.prototype["seek"] = new Sk.builtin.func(function seek(self, offset, whence) {
    var l_offset =  Sk.ffi.remapToJs(offset);

    if (whence === undefined) {
        whence = 0;
    }
    if (whence === 0) {
        self.pos$ = l_offset;
    } else if (whence == 1) {
        self.pos$ = self.data$.length + l_offset;
    } else if (whence == 2) {
        self.pos$ = self.data$.length + l_offset;
    }

    return Sk.builtin.none.none$;
});

Sk.builtin.file.prototype["tell"] = new Sk.builtin.func(function tell(self) {
    return Sk.ffi.remapToPy(self.pos$);
});

Sk.builtin.file.prototype["truncate"] = new Sk.builtin.func(function truncate(self, size) {
    Sk.asserts.fail();
});

Sk.builtin.file.prototype["write"] = new Sk.builtin.func(function write(self, str) {
    var mode = Sk.ffi.remapToJs(self.mode);
    if (mode === "w" || mode === "wb" || mode === "a" || mode === "ab") {
        if (Sk.filewrite) {
            if (self.closed) {
                throw new Sk.builtin.ValueError("I/O operation on closed file");
            }

            if (self.fileno === 1) {
                Sk.output(Sk.ffi.remapToJs(str));
            } else {
                Sk.filewrite(self, str);
            }
        } else {
            if (self.fileno === 1) {
                Sk.output(Sk.ffi.remapToJs(str));
            } else {
                Sk.asserts.fail();
            }
        }
    } else {
        throw new Sk.builtin.IOError("File not open for writing");
    }
    return Sk.builtin.none.none$;
});


Sk.exportSymbol("Sk.builtin.file", Sk.builtin.file);


/***/ }),

/***/ "./src/filter.js":
/*!***********************!*\
  !*** ./src/filter.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {pyObject} func
 * @param {pyObject} iterable
 * @extends Sk.builtin.object
 */
Sk.builtin.filter_ = Sk.abstr.buildIteratorClass("filter", {
    constructor: function filter_(func, iterable) {
        this.$func = func;
        this.$iterable = iterable;
    },
    iternext(canSuspend) {
        // iterate over iterable until we pass the predicate
        // this.chcek$filter either returns the item or undefined
        const ret = Sk.misceval.iterFor(this.$iterable, (i) =>
            Sk.misceval.chain(this.check$filter(i), (i) => (i ? new Sk.misceval.Break(i) : undefined))
        );
        return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
    },
    slots: {
        tp$doc:
            "Return an iterator yielding those items of iterable for which function(item)\nis true. If function is None, return the items that are true.",
        tp$new(args, kwargs) {
            let [func, iterable] = Sk.abstr.copyKeywordsToNamedArgs("filter", ["predicate", "iterable"], args, kwargs, []);
            func = Sk.builtin.checkNone(func) ? null : func;
            iterable = Sk.abstr.iter(iterable);
            // in theory you could subclass
            if (this === Sk.builtin.filter_.prototype) {
                return new Sk.builtin.filter_(func, iterable);
            } else {
                const instance = new this.constructor();
                Sk.builtin.filter_.call(instance, func, iterable);
                return instance;
            }
        },
    },
    proto: {
        check$filter(item) {
            let res;
            if (this.$func === null) {
                res = item;
            } else {
                res = Sk.misceval.callsimOrSuspendArray(this.$func, [item]);
            }
            return Sk.misceval.chain(res, (ret) => (Sk.misceval.isTrue(ret) ? item : undefined));
        },
    },
});

Sk.exportSymbol("Sk.builtin.filter_", Sk.builtin.filter_);

/***/ }),

/***/ "./src/float.js":
/*!**********************!*\
  !*** ./src/float.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

/** @typedef {Sk.builtin.object} */ var pyObject;

const hashMap = Object.create(null, {
    Infinity: { value: 314159 },
    "-Infinity": { value: -314159 },
    NaN: { value: 0 },
});

/**
 * @constructor
 * @extends {Sk.builtin.object}
 *
 * @param {number} x only be called with a JS number
 *
 * @return {Sk.builtin.float_} Python float
 */
Sk.builtin.float_ = Sk.abstr.buildNativeClass("float", {
    constructor: function float_(x) {
        Sk.asserts.assert(this instanceof Sk.builtin.float_, "bad call to float use 'new'");
        if (typeof x === "number") {
            this.v = x;
        } else if (x === undefined) {
            this.v = 0.0;
        } else if (typeof x === "string") {
            // be careful with converting a string as it could result in infinity
            this.v = parseFloat(x);
        } else if (x.nb$float) {
            return x.nb$float(); // allow this as a slow path
        } else {
            Sk.asserts.fail("bad argument to float constructor");
        }
    },
    slots: /**@lends {Sk.builtin.float_.prototype} */ {
        tp$gettattr: Sk.generic.getAttr,
        tp$as_number: true,
        tp$doc: "Convert a string or number to a floating point number, if possible.",
        tp$hash() {
            const v = this.v;
            let hash = hashMap[v];
            if (hash !== undefined) {
                return hash;
            } else if (Number.isInteger(v)) {
                hash = this.nb$int().tp$hash();
            } else {
                hash = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER - Number.MAX_SAFE_INTEGER / 2);
            }
            hashMap[this.v] = hash;
            return hash;
        },
        $r() {
            return new Sk.builtin.str(this.str$(10, true));
        },
        tp$new(args, kwargs) {
            if (kwargs && kwargs.length) {
                throw new Sk.builtin.TypeError("float() takes no keyword arguments");
            } else if (args && args.length > 1) {
                throw new Sk.builtin.TypeError("float expected at most 1 arguments, got " + args.length);
            }
            const arg = args[0];
            let x;
            // is args always an empty list?
            if (arg === undefined) {
                x = new Sk.builtin.float_(0.0);
            } else if (arg.nb$float) {
                x = arg.nb$float();
            } else if (Sk.builtin.checkString(arg)) {
                x = _str_to_float(arg.v);
            }
            if (x === undefined) {
                throw new Sk.builtin.TypeError("float() argument must be a string or a number");
            }
            if (this === Sk.builtin.float_.prototype) {
                return x;
            } else {
                const instance = new this.constructor();
                instance.v = x.v;
                return instance;
            }
        },

        // number slots
        nb$int() {
            let v = this.v;
            if (v < 0) {
                v = Math.ceil(v);
            } else {
                v = Math.floor(v);
            }
            if (!Number.isInteger(v)) {
                throw new Sk.builtin.ValueError("cannot convert float " + Sk.misceval.objectRepr(this) + " to integer");
            }
            if (Sk.builtin.int_.withinThreshold(v)) {
                return new Sk.builtin.int_(v);
            } else {
                return new Sk.builtin.int_(JSBI.BigInt(v));
            }
        },
        nb$float: cloneSelf,
        nb$long() {
            return new Sk.builtin.lng(this.nb$int().v);
        },
        nb$add: numberSlot((v, w) => new Sk.builtin.float_(v + w)),

        nb$subtract: numberSlot((v, w) => new Sk.builtin.float_(v - w)),
        nb$reflected_subtract: numberSlot((v, w) => new Sk.builtin.float_(w - v)),

        nb$multiply: numberSlot((v, w) => new Sk.builtin.float_(v * w)),

        nb$divide: numberSlot(divide),
        nb$reflected_divide: numberSlot((v, w) => divide(w, v)),

        nb$floor_divide: numberSlot(floordivide),
        nb$reflected_floor_divide: numberSlot((v, w) => floordivide(w, v)),

        nb$remainder: numberSlot(remainder),
        nb$reflected_remainder: numberSlot((v, w) => remainder(w, v)),

        nb$divmod: numberSlot((v, w) => new Sk.builtin.tuple([floordivide(v, w), remainder(v, w)])),
        nb$reflected_divmod: numberSlot((v, w) => new Sk.builtin.tuple([floordivide(w, v), remainder(w, v)])),

        nb$power: ternarySlot(power),
        nb$reflected_power: ternarySlot((v, w) => power(w, v)),

        nb$abs() {
            return new Sk.builtin.float_(Math.abs(this.v));
        },
        nb$negative() {
            return new Sk.builtin.float_(-this.v);
        },
        nb$positive() {
            return new Sk.builtin.float_(this.v);
        },
        nb$bool() {
            return this.v !== 0;
        },
        nb$isnegative() {
            return this.v < 0;
        },
        nb$ispositive() {
            return this.v >= 0;
        },
        ob$eq: numberSlot((v, w) => v == w),
        ob$ne: numberSlot((v, w) => v != w),
        ob$gt: numberSlot((v, w) => v > w),
        ob$ge: numberSlot((v, w) => v >= w),
        ob$lt: numberSlot((v, w) => v < w),
        ob$le: numberSlot((v, w) => v <= w),
    },
    getsets: /**@lends {Sk.builtin.float_.prototype} */ {
        real: {
            $get: cloneSelf,
            $doc: "the real part of a complex number",
        },
        imag: {
            $get() {
                return new Sk.builtin.float_(0.0);
            },
            $doc: "the imaginary part of a complex number",
        },
    },
    methods: /**@lends {Sk.builtin.float_.prototype} */ {
        conjugate: {
            $meth: cloneSelf,
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Return self, the complex conjugate of any float.",
        },
        __trunc__: {
            $meth() {
                return this.nb$int();
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Return the Integral closest to x between 0 and x.",
        },
        __round__: {
            $meth(ndigits) {
                return this.round$(ndigits);
            },
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, ndigits=None, /)",
            $doc: "Return the Integral closest to x, rounding half toward even.\n\nWhen an argument is passed, work like built-in round(x, ndigits).",
        },
        // as_integer_ratio: {
        //     $meth: methods.as_integer_ratio,
        //     $flags: { NoArgs: true },
        //     $textsig: "($self, /)",
        //     $doc:
        //         "Return integer ratio.\n\nReturn a pair of integers, whose ratio is exactly equal to the original float\nand with a positive denominator.\n\nRaise OverflowError on infinities and a ValueError on NaNs.\n\n>>> (10.0).as_integer_ratio()\n(10, 1)\n>>> (0.0).as_integer_ratio()\n(0, 1)\n>>> (-.25).as_integer_ratio()\n(-1, 4)",
        // },
        // hex: {
        //     $meth: methods.hex,
        //     $flags: { NoArgs: true },
        //     $textsig: "($self, /)",
        //     $doc:
        //         "Return a hexadecimal representation of a floating-point number.\n\n>>> (-0.1).hex()\n'-0x1.999999999999ap-4'\n>>> 3.14159.hex()\n'0x1.921f9f01b866ep+1'",
        // },
        is_integer: {
            $meth() {
                return new Sk.builtin.bool(Number.isInteger(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Return True if the float is an integer.",
        },
        __getnewargs__: {
            $meth() {
                return new Sk.builtin.tuple([this]);
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: Sk.builtin.none.none$,
        },
        __format__: {
            $meth: Sk.formatting.mkNumber__format__(true),
            $flags: { OneArg: true },
            $textsig: "($self, format_spec, /)",
            $doc: Sk.builtin.none.none$,
        },
    },
});

const invalidUnderscores = /_[eE]|[eE]_|\._|_\.|[+-]_|__/;
const validUnderscores = /_(?=[^_])/g;
function _str_to_float(str) {
    let ret;
    let tmp = str;
    if (str.indexOf("_") !== -1) {
        if (invalidUnderscores.test(str)) {
            throw new Sk.builtin.ValueError("could not convert string to float: '" + str + "'");
        }
        tmp = str.charAt(0) + str.substring(1).replace(validUnderscores, "");
    }

    if (str.match(/^-inf$/i)) {
        ret = -Infinity;
    } else if (str.match(/^[+]?inf$/i)) {
        ret = Infinity;
    } else if (str.match(/^[-+]?nan$/i)) {
        ret = NaN;
    } else if (!isNaN(tmp)) {
        ret = parseFloat(tmp);
        if (Number.isNaN(ret)) {
            ret = undefined;
        }
    }
    if (ret === undefined) {
        throw new Sk.builtin.ValueError("could not convert string to float: " + Sk.misceval.objectRepr(new Sk.builtin.str(str)));
    }
    return new Sk.builtin.float_(ret);
}

function cloneSelf() {
    return new Sk.builtin.float_(this.v);
}

/**
 * Checks for float subtypes, though skulpt does not allow to
 * extend them for now.
 *
 * Javascript function, returns Javascript object.
 * @param {Object} op The object to check as subtype.
 * @return {boolean} true if op is a subtype of Sk.builtin.float_, false otherwise
 */
Sk.builtin.float_.PyFloat_Check = function (op) {
    if (op === undefined) {
        return false;
    }
    // this is a little bit hacky
    // ToDo: subclassable builtins do not require this
    if (Sk.builtin.checkNumber(op)) {
        return true;
    }
    if (Sk.builtin.checkFloat(op)) {
        return true;
    }
    if (op.ob$type.$isSubType(Sk.builtin.float_)) {
        return true;
    }
    return false;
};

/**
 * Returns this instance's value as a string formatted using fixed-point notation.
 *
 * Javascript function, returns Javascript object.
 *
 * @param  {Object|number} x The numer of digits to appear after the decimal point.
 * @return {string}   The string representation of this instance's value.
 */
Sk.builtin.float_.prototype.toFixed = function (x) {
    x = Sk.builtin.asnum$(x);
    return this.v.toFixed(x);
};

function numberSlot(f) {
    return function (other) {
        const v = this.v;
        let w = other.v;
        if (typeof w === "number") {
            // pass
        } else if (JSBI.__isBigInt(w)) {
            w = fromBigIntToNumberOrOverflow(w);
        } else {
            return Sk.builtin.NotImplemented.NotImplemented$;
        }
        return f(v, w);
    };
}

function ternarySlot(f) {
    const binSlot = numberSlot(f);
    return function (other, z) {
        if (z !== undefined && !Sk.builtin.checkNone(z)) {
            throw new Sk.builtin.TypeError("pow() 3rd argument not allowed unless all arguments are integers");
        }
        return binSlot.call(this, other);
    };
}

function divide(v, w) {
    if (w === 0) {
        throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");
    }
    if (v === Infinity) {
        if (w === Infinity || v === -Infinity) {
            return new Sk.builtin.float_(NaN);
        } else if (w < 0) {
            return new Sk.builtin.float_(-Infinity);
        } else {
            return new Sk.builtin.float_(Infinity);
        }
    }
    if (v === -Infinity) {
        if (w === Infinity || v === -Infinity) {
            return new Sk.builtin.float_(NaN);
        } else if (w < 0) {
            return new Sk.builtin.float_(Infinity);
        } else {
            return new Sk.builtin.float_(-Infinity);
        }
    }
    return new Sk.builtin.float_(v / w);
}

function floordivide(v, w) {
    if (v === Infinity || v === -Infinity) {
        return new Sk.builtin.float_(NaN);
    }
    if (w === 0) {
        throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");
    }

    if (w === Infinity) {
        if (v < 0) {
            return new Sk.builtin.float_(-1);
        } else {
            return new Sk.builtin.float_(0);
        }
    }
    if (w === -Infinity) {
        if (v < 0 || v !== 0) {
            return new Sk.builtin.float_(0);
        } else {
            return new Sk.builtin.float_(-1);
        }
    }
    return new Sk.builtin.float_(Math.floor(v / w));
}

function remainder(v, w) {
    if (w === 0) {
        throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");
    }
    if (v === 0) {
        return new Sk.builtin.float_(0);
    }
    if (w === Infinity) {
        if (v === Infinity || this.v === -Infinity) {
            return new Sk.builtin.float_(NaN);
        } else if (v > 0) {
            return new Sk.builtin.float_(v);
        } else {
            return new Sk.builtin.float_(Infinity);
        }
    }

    //  Javacript logic on negatives doesn't work for Python... do this instead
    let tmp = v % w;

    if (v < 0) {
        if (w > 0 && tmp < 0) {
            tmp = tmp + w;
        }
    } else {
        if (w < 0 && tmp !== 0) {
            tmp = tmp + w;
        }
    }
    if (tmp === 0) {
        if (w < 0) {
            tmp = -0.0; // otherwise the sign gets lost by javascript modulo
        } else if (Infinity / tmp === -Infinity) {
            tmp = 0.0;
        }
    }
    return new Sk.builtin.float_(tmp);
}

function power(v, w) {
    if (v < 0 && w % 1 !== 0) {
        throw new Sk.builtin.ValueError("negative number cannot be raised to a fractional power");
    }
    if (v === 0 && w < 0) {
        throw new Sk.builtin.ZeroDivisionError("0.0 cannot be raised to a negative power");
    }

    const result = Math.pow(v, w);

    if (Math.abs(result) === Infinity && Math.abs(v) !== Infinity && Math.abs(w) !== Infinity) {
        throw new Sk.builtin.OverflowError("Numerical result out of range");
    }
    return new Sk.builtin.float_(result);
}

/**
 * Round this instance to a given number of digits, or zero if omitted.
 *
 * Implements `__round__` dunder method.
 *
 * Javascript function, returns Python object.
 *
 * @param  {pyObject=} ndigits The number of digits after the decimal point to which to round.
 * @return {Sk.builtin.float_|Sk.builtin.int_} The rounded float.
 *
 */
Sk.builtin.float_.prototype.round$ = function (ndigits) {
    var result, multiplier, number, num10, rounded, bankRound, ndigs;
    number = Sk.builtin.asnum$(this);
    if (ndigits === undefined) {
        ndigs = 0;
    } else {
        ndigs = Sk.misceval.asIndexSized(ndigits);
    }

    if (Sk.__future__.bankers_rounding) {
        num10 = number * Math.pow(10, ndigs);
        rounded = Math.round(num10);
        bankRound = (num10 > 0 ? num10 : -num10) % 1 === 0.5 ? (0 === rounded % 2 ? rounded : rounded - 1) : rounded;
        result = bankRound / Math.pow(10, ndigs);
        if (ndigits === undefined) {
            return new Sk.builtin.int_(result);
        } else {
            return new Sk.builtin.float_(result);
        }
    } else {
        multiplier = Math.pow(10, ndigs);
        result = Math.round(number * multiplier) / multiplier;

        return new Sk.builtin.float_(result);
    }
};

/**
 * Convert this instance's value to a Javascript string.
 *
 * Javascript function, returns Javascript object.
 *
 * @param {number} base The base of the value.
 * @param {boolean} sign true if the value should be signed, false otherwise.
 * @return {string} The Javascript string representation of this instance.
 */
Sk.builtin.float_.prototype.str$ = function (base, sign) {
    var post;
    var pre;
    var idx;
    var tmp;
    var work;

    if (isNaN(this.v)) {
        return "nan";
    }

    if (sign === undefined) {
        sign = true;
    }

    if (this.v == Infinity) {
        return "inf";
    }
    if (this.v == -Infinity && sign) {
        return "-inf";
    }
    if (this.v == -Infinity && !sign) {
        return "inf";
    }

    work = sign ? this.v : Math.abs(this.v);

    if (base === undefined || base === 10) {
        if (Sk.__future__.python3) {
            tmp = work.toPrecision(16);
        } else {
            tmp = work.toPrecision(12);
        }

        // transform fractions with 4 or more leading zeroes into exponents
        idx = tmp.indexOf(".");
        pre = work.toString().slice(0, idx);
        post = work.toString().slice(idx);

        if (pre.match(/^-?0$/) && post.slice(1).match(/^0{4,}/)) {
            if (tmp.length < 12) {
                tmp = work.toExponential();
            } else {
                tmp = work.toExponential(11);
            }
        }

        if (tmp.indexOf("e") < 0 && tmp.indexOf(".") >= 0) {
            while (tmp.charAt(tmp.length - 1) == "0") {
                tmp = tmp.substring(0, tmp.length - 1);
            }
            if (tmp.charAt(tmp.length - 1) == ".") {
                tmp = tmp + "0";
            }
        }

        tmp = tmp.replace(new RegExp("\\.0+e"), "e", "i");
        // make exponent two digits instead of one (ie e+09 not e+9)
        tmp = tmp.replace(/(e[-+])([1-9])$/, "$10$2");
        // remove trailing zeroes before the exponent
        tmp = tmp.replace(/0+(e.*)/, "$1");
    } else {
        tmp = work.toString(base);
    }

    // restore negative zero sign
    if (this.v === 0 && 1 / this.v === -Infinity) {
        tmp = "-" + tmp;
    }

    if (tmp.indexOf(".") < 0 && tmp.indexOf("E") < 0 && tmp.indexOf("e") < 0) {
        tmp = tmp + ".0";
    }

    return tmp;
};

Sk.builtin.float_.py2$methods = {};

function fromBigIntToNumberOrOverflow(big) {
    const x = parseFloat(JSBI.toNumber(big));
    if (x == Infinity || x == -Infinity) {
        //trying to convert a large js string to a float
        throw new Sk.builtin.OverflowError("int too large to convert to float");
    }
    return x;
}


/***/ }),

/***/ "./src/formatting.js":
/*!***************************!*\
  !*** ./src/formatting.js ***!
  \***************************/
/*! no static exports found */
/***/ (function(module, exports) {

// Implement the default "format specification mini-language"
// for numbers and strings
// https://docs.python.org/3.7/library/string.html#formatspec

const FORMAT_SPEC_REGEX = /^(?:(.)?([<\>\=\^]))?([\+\-\s])?(#)?(0)?(\d+)?(,)?(?:\.(\d+))?([bcdeEfFgGnosxX%])?$/;
const FMT = {
    FILL_CHAR: 1,
    FILL_ALIGN: 2,
    SIGN: 3,
    ALT_FORM: 4,
    ZERO_PAD: 5,
    FIELD_WIDTH: 6,
    COMMA: 7,
    PRECISION: 8,
    CONVERSION_TYPE: 9
};

Sk.formatting = {};

let handleWidth = function (m, r, prefix, isNumber) {
    // print(prefix);
    Sk.asserts.assert(typeof(r) === "string");

    if (m[FMT.FIELD_WIDTH]) {
        let fieldWidth = parseInt(m[FMT.FIELD_WIDTH], 10);
        let fillChar = m[FMT.FILL_CHAR] || (m[FMT.ZERO_PAD] ? "0" : " ");
        let fillAlign = m[FMT.FILL_ALIGN] || (m[FMT.ZERO_PAD] ? "=" : isNumber ? ">" : "<");
        let nFill = fieldWidth - (r.length + (prefix ? prefix.length : 0));

        if (nFill <= 0) {
            return r;
        }

        let fill = fillChar.repeat(nFill);

        switch (fillAlign) {
            case "=":
                if (m[FMT.CONVERSION_TYPE] === "s") {
                    throw new Sk.builtin.ValueError("'=' alignment not allowed in string format specifier");
                }
                return prefix + fill + r;
            case ">":
                return fill + prefix + r;
            case "<":
                return prefix + r + fill;
            case "^":
                let idx = Math.floor(nFill/2);
                return fill.substring(0, idx) + prefix + r + fill.substring(idx);
        }
    }
    return prefix + r;
};

let signForNeg = function(m, neg) {
    return neg ? "-" :
        (m[FMT.SIGN] === "+") ? "+" :
        (m[FMT.SIGN] === " ") ? " " : "";
};

let handleInteger = function(m, n, base){
    // TODO: Do we need to tolerate float inputs for integer conversions?
    // Python doesn't, but I'm guessing this is something to do with JS's
    // int/float ambiguity
    Sk.asserts.assert(n instanceof Sk.builtin.int_ || n instanceof Sk.builtin.lng);

    if (m[FMT.PRECISION]) {
        throw new Sk.builtin.ValueError("Precision not allowed in integer format");
    }

    let r = n.str$(base, false);
    let neg = n.nb$isnegative();

    let prefix = signForNeg(m, neg);

    if (m[FMT.ALT_FORM]) {
        if (base === 16) {
            prefix += "0x";
        } else if (base === 8) {
            prefix += "0o";
        } else if (base === 2){
            prefix += "0b";
        }
    }

    if (m[FMT.CONVERSION_TYPE] === "X") {
        r = r.toUpperCase();
    }

    if (m[FMT.CONVERSION_TYPE] === "n"){
        r = (+r).toLocaleString();
    } else if (m[FMT.COMMA]){
        var parts = r.toString().split(".");
        parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        r = parts.join(".");
    }

    return handleWidth(m, r, prefix, true);
};

// Common implementation of __format__ for Python number objects
let formatNumber = function(num, formatSpec, isFractional) {
    if (!formatSpec) { // empty or undefined
        return num.str$(10, true);
    }
    let m = formatSpec.match(FORMAT_SPEC_REGEX);
    if (!m) {
        throw new Sk.builtin.ValueError("Invalid format specifier");
    }

    let conversionType = m[FMT.CONVERSION_TYPE];
    if (!conversionType) {
        conversionType = (isFractional ? "g" : "d");
    }

    let validConversions = isFractional ? "fFeEgG%" : "bcdoxXnfFeEgG%";
    if (validConversions.indexOf(conversionType) == -1) {
        throw new Sk.builtin.ValueError("Unknown format code '" + m[FMT.CONVERSION_TYPE] + "' for object of type '" + Sk.abstr.typeName(num) +"'");
    }

    switch (conversionType) {
        case "d":
        case "n":
            return handleInteger(m, num, 10);
        case "x":
        case "X":
            return handleInteger(m, num, 16);
        case "o":
            return handleInteger(m, num, 8);
        case "b":
            return handleInteger(m, num, 2);
        case "c": {
            if (m[FMT.SIGN]) {
                throw new Sk.builtin.ValueError("Sign not allowed with integer format specifier 'c'");
            }
            if (m[FMT.ALT_FORM]) {
                throw new Sk.builtin.ValueError("Alternate form not allowed with integer format specifier 'c'");
            }
            if (m[FMT.COMMA]) {
                throw new Sk.builtin.ValueError("Cannot specify ',' with 'c'");
            }
            if (m[FMT.PRECISION]) {
                throw new Sk.builtin.ValueError("Cannot specify ',' with 'c'");
            }
            return handleWidth(m, String.fromCodePoint(Sk.builtin.asnum$(num)), "", true);
        };

        case "f":
        case "F":
        case "e":
        case "E":
        case "g":
        case "G": {
            if (m[FMT.ALT_FORM]){
                throw new Sk.builtin.ValueError("Alternate form (#) not allowed in float format specifier");
            }
            let convValue = Sk.builtin.asnum$(num);
            if (typeof convValue === "string") {
                convValue = Number(convValue);
            }
            if (convValue === Infinity) {
                return handleWidth(m, "inf", "", true);
            }
            if (convValue === -Infinity) {
                return handleWidth(m, "inf", "-", true);
            }
            if (isNaN(convValue)) {
                return handleWidth(m, "nan", "", true);
            }
            let neg = false;
            if (convValue < 0) {
                convValue = -convValue;
                neg = true;
            }
            let convName = ["toExponential", "toFixed", "toPrecision"]["efg".indexOf(conversionType.toLowerCase())];
            let precision = m[FMT.PRECISION] ? parseInt(m[FMT.PRECISION], 10) : 6;
            let result = (convValue)[convName](precision);
            if ("EFG".indexOf(conversionType) !== -1) {
                result = result.toUpperCase();
            }
            // Python's 'g' does not show trailing 0s
            if (conversionType.toLowerCase()==="g" || !m[FMT.CONVERSION_TYPE]) {
                let trailingZeros = result.match(/\.(\d*[1-9])?(0+)$/);
                if (trailingZeros) {
                    let [_, hasMoreDigits, zs] = trailingZeros;
                    // Python's default conversion shows at least one trailing zero
                    result = result.slice(0, hasMoreDigits ? -zs.length : -(zs.length+1));
                }
                if (result.indexOf(".") == -1 && !m[FMT.CONVERSION_TYPE]) {
                    result += ".0";
                }
            }
            if (conversionType.toLowerCase()==="e") {
                result = result.replace(/^([-+]?[0-9]*\.?[0-9]+[eE][-+]?)([0-9])?$/, "$10$2");
            }
            if (m[FMT.COMMA]){
                var parts = result.toString().split(".");
                parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
                result = parts.join(".");
            }

            return handleWidth(m, result, signForNeg(m, neg), true);
        };

        case "%": {
            if (m[FMT.ALT_FORM]) {
                throw new Sk.builtin.ValueError("Alternate form (#) not allowed with format specifier '%'");
            }
            let convValue = Sk.builtin.asnum$(num);
            if (typeof convValue === "string") {
                convValue = Number(convValue);
            }
            if (convValue === Infinity) {
                return handleWidth(m, "inf%", "", true);
            }
            if (convValue === -Infinity) {
                return handleWidth(m, "inf%", "-", true);
            }
            if (isNaN(convValue)) {
                return handleWidth(m, "nan%", "", true);
            }
            let neg = false;
            if (convValue < 0) {
                convValue = -convValue;
                neg = true;
            }
            let precision = m[FMT.PRECISION] ? parseInt(m[FMT.PRECISION], 10) : 6;
            let result = (convValue*100.0).toFixed(precision) + "%";
            return handleWidth(m, result, signForNeg(m, neg), true);
        };

        default:
            throw new Sk.builtin.ValueError("Unknown format code '" + m[FMT.CONVERSION_TYPE] + "'");
    }
};

Sk.formatting.mkNumber__format__ = (isFractional) => function (format_spec) {
    if (!Sk.builtin.checkString(format_spec)) {
        throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec));
    }
    return new Sk.builtin.str(formatNumber(this, format_spec.$jsstr(), isFractional));
};

function formatString(format_spec) {
    if (!Sk.builtin.checkString(format_spec)) {
        throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec));
    }
    let m = format_spec.$jsstr().match(FORMAT_SPEC_REGEX);
    if (m[FMT.CONVERSION_TYPE] && m[FMT.CONVERSION_TYPE] !== "s") {
        throw new Sk.builtin.ValueError("Unknown format code '" + m[FMT.CONVERSION_TYPE] + "' for object of type 'str'");
    }

    if (m[FMT.SIGN]) {
        throw new Sk.builtin.ValueError("Sign not allowed in string format specifier");
    }

    if (m[FMT.ALT_FORM]) {
        throw new Sk.builtin.ValueError("Alternate form (#) not allowed with string format specifier");
    }

    if (m[FMT.COMMA]) {
        throw new Sk.builtin.ValueError("Cannot specify ',' with 's'");
    }

    let value = this.v;

    if (m[FMT.PRECISION]) {
        value = value.substring(0, m[FMT.PRECISION]);
    }

    return new Sk.builtin.str(handleWidth(m, value, "", false));
};

// str.format() implementation
function format(args, kwargs) {
    // following PEP 3101
    kwargs = kwargs || [];
    const arg_dict = {};
    const regex = /{(((?:\d+)|(?:\w+))?((?:\.(\w+))|(?:\[((?:\d+)|(?:\w+))\])?))?(?:\!([rs]))?(?:\:([^}]*))?}/g;
    // ex: {o.name!r:*^+#030,.9b}
    // Field 1, Field_name, o.name
    // Field 2, arg_name, o
    // Field 3, attribute_name/Element_index , .name
    // Field 4, Attribute name, name
    // Field 5, element_index, [0]
    // Field 6, conversion, r
    // Field 7, format_spec,*^+#030,.9b

    // Detect empty/int/complex name
    // retrive field value
    // hand off format spec
    // return resulting spec to function
    for (let i = 0; i < kwargs.length; i += 2) {
        arg_dict[kwargs[i]] = kwargs[i + 1];
    }
    for (let i in args) {
        arg_dict[i] = args[i];
    }

    let index = 0;
    function replFunc (substring, field_name, arg_name, attr_name, attribute_name, element_index, conversion, format_spec, offset, str_whole) {
        let value;

        if (element_index !== undefined && element_index !== "") {
            let container = arg_dict[arg_name];
            if (container.constructor === Array) {
                value = container[element_index];
            } else if (/^\d+$/.test(element_index)) {
                value = Sk.abstr.objectGetItem(container, new Sk.builtin.int_(parseInt(element_index, 10)), false);
            } else {
                value = Sk.abstr.objectGetItem(container, new Sk.builtin.str(element_index), false);
            }
            index++;
        } else if (attribute_name !== undefined && attribute_name !== "") {
            value = Sk.abstr.gattr(arg_dict[arg_name || index++], new Sk.builtin.str(attribute_name));
        } else if (arg_name !== undefined && arg_name !== "") {
            value = arg_dict[arg_name];
        } else if (field_name === undefined || field_name === "") {
            value = arg_dict[index];
            index++;
        } else if (
            field_name instanceof Sk.builtin.int_ ||
            field_name instanceof Sk.builtin.float_ ||
            field_name instanceof Sk.builtin.lng ||
            /^\d+$/.test(field_name)
        ) {
            value = arg_dict[field_name];
            index++;
        }

        if (conversion === "s") {
            value = new Sk.builtin.str(value);
        } else if (conversion === "r") {
            value = Sk.builtin.repr(value);
        } else if (conversion !== "" && conversion !== undefined) {
            throw new Sk.builtin.ValueError("Unknown conversion specifier " + conversion);
        }
        // TODO "!a" I guess?

        return Sk.abstr.objectFormat(value, new Sk.builtin.str(format_spec)).$jsstr();
    };

    const ret = this.v.replace(regex, replFunc);
    return new Sk.builtin.str(ret);
};

Sk.formatting.format = format;
Sk.formatting.formatString = formatString;

/***/ }),

/***/ "./src/function.js":
/*!*************************!*\
  !*** ./src/function.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * Sk.builtin.func
 *
 * @description
 * This function converts a Javascript function into a Python object that is callable.  Or just
 * think of it as a Python function rather than a Javascript function now.  This is an important
 * distinction in skulpt because once you have Python function you cannot just call it.
 * You must now use Sk.misceval.callsim to call the Python function.
 *
 * @param {Function} code the javascript implementation of this function
 * @param {Object=} globals the globals where this function was defined.
 * Can be undefined (which will be stored as null) for builtins. (is
 * that ok?)
 * @param {Object=} closure dict of free variables
 * @param {Object=} closure2 another dict of free variables that will be
 * merged into 'closure'. there's 2 to simplify generated code (one is $free,
 * the other is $cell)
 *
 * closure is the cell variables from the parent scope that we need to close
 * over. closure2 is the free variables in the parent scope that we also might
 * need to access.
 *
 * NOTE: co_varnames and co_name are defined by compiled code only, so we have
 * to access them via dict-style lookup for closure.
 *
 */
Sk.builtin.func = Sk.abstr.buildNativeClass("function", {
    constructor: function func(code, globals, closure, closure2) {
        Sk.asserts.assert(this instanceof Sk.builtin.func, "builtin func should be called as a class with `new`");

        this.func_code = code;
        this.func_globals = globals || null;

        this.$name = (code.co_name && code.co_name.v) || code.name || "<native JS>";
        this.$d = Sk.builtin.dict ? new Sk.builtin.dict() : undefined;
        this.$doc = code.co_docstring || Sk.builtin.none.none$;
        this.$module = (Sk.globals && Sk.globals["__name__"]) || Sk.builtin.none.none$;
        this.$qualname = (code.co_qualname && code.co_qualname.v) || this.$name;

        if (closure2 !== undefined) {
            // todo; confirm that modification here can't cause problems
            for (let k in closure2) {
                closure[k] = closure2[k];
            }
        }
        this.func_closure = closure;
        this.$memoiseFlags();
        this.memoised = code.co_fastcall || null;
        if (code.co_fastcall) {
            this.tp$call = code.bind(this);
        } else {
            this.tp$call = Sk.builtin.func.prototype.tp$call.bind(this); // keep func the same shape
        }
    },
    slots: {
        tp$getattr: Sk.generic.getAttr,
        tp$descr_get(obj, objtype) {
            if (obj === null) {
                return this;
            }
            return new Sk.builtin.method(this, obj);
        },
        $r() {
            return new Sk.builtin.str("<function " + this.$qualname + ">");
        },
        tp$call(posargs, kw) {
            // Property reads from func_code are slooow, but
            // the existing external API allows setup first, so as a
            // hack we delay this initialisation.
            // TODO change the external API to require all the co_vars
            // to be supplied at construction time!
            if (!this.memoised) {
                this.$memoiseFlags();
                this.memoised = true;
            }
            
            // Fast path for JS-native functions (which should be implemented
            // in a separate tp$call, really)
            if (this.co_argcount === undefined && this.co_varnames === undefined  && !this.co_kwargs && !this.func_closure) {
                // It's a JS function with no type info, don't hang around
                // resolving anything.
                if (kw && kw.length !== 0) {
                    throw new Sk.builtin.TypeError(this.$name + "() takes no keyword arguments");
                }
                return this.func_code.apply(this.func_globals, posargs);
            }
            // end js fast path
        
            let args = this.$resolveArgs(posargs, kw);
            if (this.func_closure) {
                args.push(this.func_closure);
            }
            // note: functions expect 'this' to be globals to avoid having to
            // slice/unshift onto the main args
            return this.func_code.apply(this.func_globals, args);
        },
    },
    getsets: {
        __name__: {
            $get() {
                return new Sk.builtin.str(this.$name);
            },
            $set(value) {
                if (!Sk.builtin.checkString(value)) {
                    throw new Sk.builtin.TypeError("__name__ must be set to a string object");
                }
                this.$name = value.$jsstr();
            },
        },
        __qualname__: {
            $get() {
                return new Sk.builtin.str(this.$qualname);
            },
            $set(value) {
                if (!Sk.builtin.checkString(value)) {
                    throw new Sk.builtin.TypeError("__qualname__ must be set to a string object");
                }
                this.$qualname = value.$jsstr();
            },
        },
        __dict__: Sk.generic.getSetDict,
        __defaults__: {
            $get() {
                return new Sk.builtin.tuple(this.$defaults);
            }, // technically this is a writable property but we'll leave it as read-only for now
        },
        __doc__: {
            $get() {
                return this.$doc;
            },
            $set(v) {
                // The value the user is setting __doc__ to can be any Python
                // object.  If we receive 'undefined' then the user is deleting
                // __doc__, which is allowed and results in __doc__ being None.
                this.$doc = v || Sk.builtin.none.none$;
            },
        },
    },
    proto: {
        $memoiseFlags() {
            this.co_varnames = this.func_code.co_varnames;
            this.co_argcount = this.func_code.co_argcount;
            if (this.co_argcount === undefined && this.co_varnames) {
                this.co_argcount = this.co_argcount = this.co_varnames.length;
            }
            this.co_kwonlyargcount = this.func_code.co_kwonlyargcount || 0;
            this.co_varargs = this.func_code.co_varargs;
            this.co_kwargs = this.func_code.co_kwargs;
            this.$defaults = this.func_code.$defaults || [];
            this.$kwdefs = this.func_code.$kwdefs || [];
        },
        $resolveArgs,

    }
});


function $resolveArgs(posargs, kw) {
    // The rest of this function is a logical Javascript port of
    // _PyEval_EvalCodeWithName, and follows its logic,
    // plus fast-paths imported from _PyFunction_FastCall* as marked

    let co_argcount = this.co_argcount;

    if (co_argcount === undefined) {
        co_argcount = this.co_varnames ? this.co_varnames.length : posargs.length;
    }
    let varnames = this.co_varnames || [];
    let co_kwonlyargcount = this.co_kwonlyargcount || 0;
    let totalArgs = co_argcount + co_kwonlyargcount;

    // Fast path from _PyFunction_FastCallDict
    if (co_kwonlyargcount === 0 && !this.co_kwargs && (!kw || kw.length === 0) && !this.co_varargs) {
        if (posargs.length == co_argcount) {
            return posargs;
        } else if(posargs.length === 0 && this.$defaults &&
                    this.$defaults.length === co_argcount) {
            for (let i=0; i!=this.$defaults.length; i++) {
                posargs[i] = this.$defaults[i];
            }
            return posargs;
        }
    }
    // end fast path from _PyFunction_FastCallDict
    


    let kwargs;

    /* Create a NOT-a-dictionary for keyword parameters (**kwags) */
    if (this.co_kwargs) {
        kwargs = [];
    }

    /* Copy positional arguments into arguments to our JS function*/
    let nposargs = posargs.length;
    let args = (posargs.length <= co_argcount) ? posargs : posargs.slice(0, co_argcount);


    /* Pack other positional arguments into the *args argument */
    if (this.co_varargs) {
        let vararg = (posargs.length > args.length) ? posargs.slice(args.length) : [];
        args[totalArgs] = new Sk.builtin.tuple(vararg);
    } else if (nposargs > co_argcount) {
        throw new Sk.builtin.TypeError(this.$name + "() takes " + co_argcount + " positional argument" + (co_argcount == 1 ? "" : "s") + " but " + nposargs + (nposargs == 1 ? " was " : " were ") + " given");
    }

    /* Handle keyword arguments */
    if (kw) {
        if (this.func_code["no_kw"]) {
            throw new Sk.builtin.TypeError(this.$name + "() takes no keyword arguments");
        }

        for (let i = 0; i < kw.length; i += 2) {
            let name = kw[i]; // JS string
            let value = kw[i+1]; // Python value
            let idx = varnames.indexOf(name);

            if (idx >= 0) {
                if (args[idx] !== undefined) {
                    throw new Sk.builtin.TypeError(this.$name + "() got multiple values for argument '" + name + "'");
                }
                args[idx] = value;
            } else if (kwargs) {
                kwargs.push(new Sk.builtin.str(name), value);
            } else {
                throw new Sk.builtin.TypeError(this.$name + "() got an unexpected keyword argument '" + name + "'");
            }
        }
    }

    /* "Check the number of positional arguments" (which only checks for too many)
       has been handled before keywords */

    /* Add missing positional arguments (copy default values from defs)
       (also checks for missing args where no defaults) */
    {
        let defaults = this.$defaults || [];
        let i = 0, missing = [], missingUnnamed = false;
        // Positional args for which we *don't* have a default
        let defaultStart = co_argcount - defaults.length;
        for (; i < defaultStart; i++) {
            if (args[i] === undefined) {
                missing.push(varnames[i]);
                if (varnames[i] === undefined) {
                    missingUnnamed = true;
                }
            }
        }
        if (missing.length != 0 && (this.co_argcount || this.co_varnames)) {
            throw new Sk.builtin.TypeError(this.$name + "() missing " + missing.length + " required argument" + (missing.length==1?"":"s") + (missingUnnamed ? "" : (": " + missing.join(", "))));
        }
        for (; i < co_argcount; i++) {
            if (args[i] === undefined) {
                args[i] = defaults[i - defaultStart];
            }
        }
    }

    /* Add missing keyword arguments (copy default values from kwdefs) */

    if (co_kwonlyargcount > 0) {
        let missing = [];
        let kwdefs = this.$kwdefs;

        for (let i = co_argcount; i < totalArgs; i++) {
            if (args[i] === undefined) {
                if (kwdefs[i-co_argcount] !== undefined) {
                    args[i] = kwdefs[i-co_argcount];
                } else {
                    missing.push(varnames[i]);
                }
            }
        }
        if (missing.length !== 0) {
            throw new Sk.builtin.TypeError(this.$name + "() missing " + missing.length + " required keyword argument" + (missing.length==1?"":"s") + ": " + missing.join(", "));
        }
    }

    if (this.func_closure) {
        // todo; OK to modify?
        if (varnames) {
            // Make sure all default arguments are in args before adding closure
            for (let i = args.length; i < varnames.length; i++) {
                args.push(undefined);
            }
        }
    }

    if (kwargs) {
        args.unshift(kwargs);
    }

    return args;
};



/***/ }),

/***/ "./src/generator.js":
/*!**************************!*\
  !*** ./src/generator.js ***!
  \**************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Function} code javascript code object for the function
 * @param {Object} globals where this function was defined
 * @param {Object} args arguments to the original call (stored into locals for
 * the generator to reenter)
 * @param {Object=} closure dict of free variables
 * @param {Object=} closure2 another dict of free variables that will be
 * merged into 'closure'. there's 2 to simplify generated code (one is $free,
 * the other is $cell)
 *
 * co_varnames and co_name come from generated code, must access as dict.
 */
Sk.builtin.generator = Sk.abstr.buildIteratorClass("generator", {
    constructor: function generator(code, globals, args, closure, closure2) {
        var k;
        var i;
        if (!code) {
            return;
        } // ctor hack

        if (!(this instanceof Sk.builtin.generator)) {
            throw new TypeError("bad internal call to generator, use 'new'");
        }

        this.func_code = code;
        this.func_globals = globals || null;
        this.gi$running = false;
        this.gi$resumeat = 0;
        this.gi$sentvalue = undefined;
        this.gi$locals = {};
        this.gi$cells = {};
        if (args.length > 0) {
            // store arguments into locals because they have to be maintained
            // too. 'fast' var lookups are locals in generator functions.
            for (i = 0; i < code.co_varnames.length; ++i) {
                this.gi$locals[code.co_varnames[i]] = args[i];
            }
        }
        if (closure2 !== undefined) {
            // todo; confirm that modification here can't cause problems
            for (k in closure2) {
                closure[k] = closure2[k];
            }
        }
        //print(JSON.stringify(closure));
        this.func_closure = closure;
    },
    slots: {
        $r() {
            return new Sk.builtin.str("<generator object " + this.func_code.co_name.v + ">");
        },
    },
    iternext(canSuspend, yielded) {
        var ret;
        var args;
        var self = this;
        this["gi$running"] = true;
        if (yielded === undefined) {
            yielded = Sk.builtin.none.none$;
        }
        this["gi$sentvalue"] = yielded;

        // note: functions expect 'this' to be globals to avoid having to
        // slice/unshift onto the main args
        args = [this];
        if (this.func_closure) {
            args.push(this.func_closure);
        }
        ret = this.func_code.apply(this.func_globals, args);
        return (function finishIteration(ret) {
            if (ret instanceof Sk.misceval.Suspension) {
                if (canSuspend) {
                    return new Sk.misceval.Suspension(finishIteration, ret);
                } else {
                    ret = Sk.misceval.retryOptionalSuspensionOrThrow(ret);
                }
            }
            //print("ret", JSON.stringify(ret));
            self["gi$running"] = false;
            Sk.asserts.assert(ret !== undefined);
            if (ret !== Sk.builtin.none.none$) {
                // returns a pair: resume target and yielded value
                self["gi$resumeat"] = ret[0];
                ret = ret[1];
            } else {
                // todo; StopIteration
                return undefined;
            }
            //print("returning:", JSON.stringify(ret));
            return ret;
        })(ret);
    },
    methods: {
        send: {
            $meth(value) {
                return this.tp$iternext(true, value);
            },
            $flags: { OneArg: true },
            $doc: "send(arg) -> send 'arg' into generator,\nreturn next yielded value or raise StopIteration.",
        },
    },
});
Sk.exportSymbol("Sk.builtin.generator", Sk.builtin.generator);

/**
 * Creates a generator with the specified next function and additional
 * instance data. Useful in Javascript-implemented modules to implement
 * the __iter__ method.
 */
Sk.builtin.makeGenerator = function (next, data) {
    var key;
    var gen = new Sk.builtin.generator(null, null, null);
    gen.tp$iternext = next;

    for (key in data) {
        if (data.hasOwnProperty(key)) {
            gen[key] = data[key];
        }
    }

    return gen;
};
Sk.exportSymbol("Sk.builtin.makeGenerator", Sk.builtin.makeGenerator);


/***/ }),

/***/ "./src/generic.js":
/*!************************!*\
  !*** ./src/generic.js ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @namespace Sk.generic
 *
 * @description
 * Some useful default methods for native classes
 *
 */
Sk.generic = {};

/** @typedef {Sk.builtin.object} */ var pyObject;
/** @typedef {Sk.builtin.type|Function} */ var typeObject;

/**
 * @method
 *
 * @param {Sk.builtin.str} pyName Python string name of the attribute
 * @param {boolean=} canSuspend Can we return a suspension?
 *
 * @description
 * The default implementation of __getattribute__. This is used by most instances and will be inherited from object.
 *
 * If undefined is returned by this method then the object has no attribute
 * It is the responsibility of the user to throw the error.
 * Currently this is thrown in Sk.abstr.gattr or directly in compile code
 *
 * @return {Sk.builtin.object|undefined}
 */
Sk.generic.getAttr = function __getattribute__(pyName, canSuspend) {
    let f;
    const type = this.ob$type;
    const descr = type.$typeLookup(pyName);
    // look in the type for a descriptor
    if (descr !== undefined) {
        f = descr.tp$descr_get;
        if (f !== undefined && descr.tp$descr_set !== undefined) {
            // then we're a data descriptor
            return f.call(descr, this, type, canSuspend);
        }
    }

    const dict = this.$d;

    if (dict !== undefined) {
        const res = dict.quick$lookup(pyName);
        if (res !== undefined) {
            return res;
        }
    }
    if (f !== undefined) {
        return f.call(descr, this, type, canSuspend);
    }
    if (descr !== undefined) {
        return descr;
    }
    return;
};
Sk.exportSymbol("Sk.generic.getAttr", Sk.generic.getAttr);

/**
 * @method
 *
 * @description
 * The default implementation of __setattr__/__delattr__ used by most instance objects
 * There is no return value for this function
 * An error will be thrown if no attribute exists
 *
 * A value=undefined signifies that the attribute is to be deleted
 *
 * @param {Sk.builtin.str} pyName
 * @param {Sk.builtin.object|undefined} value
 * @param {boolean=} canSuspend ? can this function suspend
 * @return {undefined}
 */
Sk.generic.setAttr = function __setattr__(pyName, value, canSuspend) {
    const descr = this.ob$type.$typeLookup(pyName);
    // otherwise, look in the type for a descr
    if (descr !== undefined && descr !== null) {
        const f = descr.tp$descr_set;
        // todo; is this the right lookup priority for data descriptors?
        if (f) {
            return f.call(descr, this, value, canSuspend);
        }
    }

    const dict = this.$d;
    if (dict !== undefined) {
        if (dict.mp$ass_subscript) {
            if (value !== undefined) {
                return dict.mp$ass_subscript(pyName, value);
            } else {
                try {
                    return dict.mp$ass_subscript(pyName);
                } catch (e) {
                    if (e instanceof Sk.builtin.KeyError) {
                        throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(this) + "' object has no attribute '" + pyName.$jsstr() + "'");
                    }
                    throw e;
                }
            }
        } else if (typeof dict === "object") {
            const jsMangled = pyName.$mangled;
            if (value !== undefined) {
                dict[jsMangled] = value;
                return;
            } else if (dict[jsMangled] !== undefined) {
                delete dict[jsMangled];
                return;
            }
        }
    }
    throw new Sk.builtin.AttributeError(this.sk$attrError() + " has no attribute '" + pyName.$jsstr() + "'");
};
Sk.exportSymbol("Sk.generic.setAttr", Sk.generic.setAttr);

/**
 * @method
 *
 * @description
 * The default implementation of tp$new for builtin type objects that are mutable
 * args and kwargs are ignored
 * either a new instance of the builtin is returned or an instance of a subtype
 *
 * @see {Sk.builtin.type.prototype.tp$new}
 *
 * @param {typeObject} builtin
 */
Sk.generic.new = function (builtin) {
    const genericNew = function __new__(args, kwargs) {
        // this = prototype of an sk$type object.
        if (this.constructor === builtin) {
            return new this.constructor();
        } else {
            const instance = new this.constructor();
            // now we want to apply instance to the builtin
            builtin.call(instance);
            return instance;
        }
    };
    return genericNew;
};

/**
 * @method
 *
 * @description
 * method definitaion for __new__ that wraps tp$new
 * typically called by subtypes using super().__new__(args, kwargs)
 *
 * the algorithm follows Cpython
 *
 * @see {Sk.slots.__new__}
 *
 */
Sk.generic.newMethodDef = {
    $meth(args, kwargs) {
        // this = a type object
        let this_name, subs_name;
        const native_type_proto = this.prototype;

        if (args.length < 1) {
            this_name = native_type_proto.tp$name;
            throw new Sk.builtin.TypeError(this_name + ".__new__(): not enough arguments");
        }

        const subtype = args.shift();

        if (subtype.sk$type === undefined) {
            this_name = native_type_proto.tp$name;
            throw new Sk.builtin.TypeError(this_name + "__new__(X): X is not a type object (" + Sk.abstr.typeName(subtype) + ")");
        }

        if (!subtype.$isSubType(this)) {
            this_name = native_type_proto.tp$name;
            subs_name = subtype.prototype.tp$name;
            throw new Sk.builtin.TypeError(this_name + ".__new__(" + subs_name + "): " + subs_name + " is not a subtype of " + this_name);
        }
        /* from CPython: Check that the use doesn't do something silly and unsafe like
       object.__new__(dict).  To do this, we check that the
       most derived base that's not a heap type is this type. */

        /**@todo is  prototypical inheritance fine here - i think it is because of the best base algorithm*/
        const static_proto = subtype.prototype.sk$staticNew.prototype;

        if (static_proto.tp$new !== native_type_proto.tp$new) {
            this_name = native_type_proto.tp$name;
            subs_name = subtype.prototype.tp$name;
            const suitable = static_proto.tp$name;
            throw new Sk.builtin.TypeError(this_name + ".__new__(" + subs_name + ") is not safe, use " + suitable + ".__new__()");
        }
        return native_type_proto.tp$new.call(subtype.prototype, args, kwargs);
    },
    $flags: { FastCall: true },
    $textsig: "($type, *args, **kwargs)",
    $name: "__new__",
};

/**
 * @description
 * used by most iterators that return self
 *
 * @function
 */
Sk.generic.selfIter = function __iter__() {
    return this;
};

/**
 * @method
 *
 * @description
 * the $seq of the iterator must be an array
 * $orig must be provided and must have a get$size private method
 * note we do not use sq$length since this can be override by subclasses
 *
 * typically used by mutable iterators like dict_iter_ and set_iter_
 */
Sk.generic.iterNextWithArrayCheckSize = function __next__() {
    if (this.$seq.length !== this.$orig.get$size()) {
        const error_name = this.tp$name.split("_")[0];
        throw new Sk.builtin.RuntimeError(error_name + " changed size during iteration");
    } else if (this.$index >= this.$seq.length) {
        return undefined;
    }
    return this.$seq[this.$index++];
};

/**
 * @method
 *
 * @description
 * the $seq of the iterator must be an array
 */
Sk.generic.iterNextWithArray = function __next__() {
    const next = this.$seq[this.$index++];
    if (next === undefined) {
        this.tp$iternext = () => undefined; // consumed iterator
    }
    return next;
};

/**
 * @method
 *
 * @description
 * compares the $seq.length to the $index
 */
Sk.generic.iterLengthHintWithArrayMethodDef = {
    $meth: function __length_hint__() {
        return new Sk.builtin.int_(this.$seq.length - this.$index);
    },
    $flags: { NoArgs: true },
};

/**
 * @method
 *
 * @description
 * returns the current index
 */
Sk.generic.iterReverseLengthHintMethodDef = {
    $meth: function __length_hint__() {
        return new Sk.builtin.int_(this.$index);
    },
    $flags: { NoArgs: true },
};

/**
 * @description
 * typical implementation of `__dict__` for type objects that support it
 */
Sk.generic.getSetDict = {
    $get() {
        return this.$d;
    },
    $set(value) {
        if (value === undefined) {
            this.$d = new Sk.builtin.dict();
        } else if (value instanceof Sk.builtin.dict) {
            this.$d = value;
        } else {
            throw new Sk.builtin.TypeError("__dict__ must be set to a dictionary, not a '" + Sk.abstr.typeName(value) + "'");
        }
    },
    $doc: "dictionary for instance variables (if defined)",
    $name: "__dict__",
};

/**
 * Logic used by seq and tuple to do rich comparisons
 */
Sk.generic.seqCompare = function (other, op) {
    if (this === other && Sk.misceval.opAllowsEquality(op)) {
        return true;
    }
    // w not a tuple
    if (!(other instanceof this.sk$builtinBase)) {
        return Sk.builtin.NotImplemented.NotImplemented$;
    }
    const v = this.v;
    const w = other.v;
    let i;
    if (v.length !== w.length && (op === "Eq" || op === "NotEq")) {
        /* Shortcut: if the lengths differ, the tuples differ */
        return op === "Eq" ? false : true;
    }
    for (i = 0; i < v.length && i < w.length; ++i) {
        if (!(v[i] === w[i] || Sk.misceval.richCompareBool(v[i], w[i], "Eq"))) {
            break;
        }
    }
    const vl = v.length; // it's possible for list lengths to have changed during iteration!
    const wl = w.length;
    if (i >= vl || i >= wl) {
        // no more items to compare, compare sizes
        switch (op) {
            case "Lt":
                return vl < wl;
            case "LtE":
                return vl <= wl;
            case "Eq":
                return vl === wl;
            case "NotEq":
                return vl !== wl;
            case "Gt":
                return vl > wl;
            case "GtE":
                return vl >= wl;
            default:
                Sk.asserts.fail();
        }
    }

    // we have an item that's different
    // shortcuts for eq/not
    if (op === "Eq") {
        return false;
    }
    if (op === "NotEq") {
        return true;
    }

    // or, compare the differing element using the proper operator
    return Sk.misceval.richCompareBool(v[i], w[i], op);
};


/***/ }),

/***/ "./src/import.js":
/*!***********************!*\
  !*** ./src/import.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @namespace Sk
 *
 */

// this is stored into sys specially, rather than created by sys
Sk.sysmodules = new Sk.builtin.dict([]);
Sk.realsyspath = undefined;

/**
 * @param {string} name to look for
 * @param {string} ext extension to use (.py or .js)
 * @param {Object=} searchPath an iterable set of path strings
 */
Sk.importSearchPathForName = function (name, ext, searchPath) {
    var fn;
    var j;
    var fns = [];
    var nameAsPath = name.replace(/\./g, "/");
    var it, i;

    var tryPathAndBreakOnSuccess = function(filename, packagePath) {
        return Sk.misceval.chain(
            Sk.misceval.tryCatch(function() {
                return Sk.read(filename);
            }, function(e) { /* Exceptions signal "not found" */ }),
            function(code) {
                if (code !== undefined) {
                    // This will cause the iterFor() to return the specified value
                    return new Sk.misceval.Break({filename: filename, code: code, packagePath: packagePath});
                }
            }
        );
    };

    if (searchPath === undefined) {
        searchPath = Sk.realsyspath;
    }

    return Sk.misceval.iterFor(searchPath.tp$iter(), function(pathStr) {
        // For each element of path, try loading the module, and if that
        // doesn't work, try the corresponding package.
        return Sk.misceval.chain(
            tryPathAndBreakOnSuccess(pathStr.v + "/" + nameAsPath + ext, false), // module
            function(r) {
                return r ? r : tryPathAndBreakOnSuccess(pathStr.v + "/" + nameAsPath + "/__init__" + ext,
                                                        pathStr.v + "/" + nameAsPath); // package
            }
        );
    });
};



/**
 * currently only pull once from Sk.syspath. User might want to change
 * from js or from py.
 */
Sk.importSetUpPath = function (canSuspend) {
    var i;
    var paths;
    if (!Sk.realsyspath) {
        paths = [
            new Sk.builtin.str("src/builtin"),
            new Sk.builtin.str("src/lib"),
            new Sk.builtin.str(".")
        ];
        for (i = 0; i < Sk.syspath.length; ++i) {
            paths.push(new Sk.builtin.str(Sk.syspath[i]));
        }
        Sk.realsyspath = new Sk.builtin.list(paths);
    }
};

/**
 * @param {string} name name of module to import
 * @param {boolean=} dumpJS whether to output the generated js code
 * @param {string=} modname what to call the module after it's imported if
 * it's to be renamed (i.e. __main__)
 * @param {string=} suppliedPyBody use as the body of the text for the module
 * rather than Sk.read'ing it.
 * @param {Object=} relativeToPackage perform import relative to this package
 * @param {boolean=} returnUndefinedOnTopLevelNotFound return 'undefined' rather than throwing ImportError if the *first* load failed
 * @param {boolean=} canSuspend whether we may return a Suspension object
 */
Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend) {
    //dumpJS = true;
    var filename;
    var prev;
    var parentModName;
    var parentModule;
    var modNameSplit;
    var ret;
    var module;
    var topLevelModuleToReturn = null;
    var relativePackageName = relativeToPackage !== undefined ? relativeToPackage.tp$getattr(Sk.builtin.str.$name) : undefined;
    var absolutePackagePrefix = relativePackageName !== undefined ? relativePackageName.v + "." : "";
    var searchPath = relativeToPackage !== undefined ? relativeToPackage.tp$getattr(Sk.builtin.str.$path) : undefined;
    Sk.importSetUpPath(canSuspend);

    if (relativeToPackage && !relativePackageName) {
        if (returnUndefinedOnTopLevelNotFound) {
            return undefined;
        } else {
            throw new Sk.builtin.ValueError("Attempted to import relative to invalid package (no name)");
        }
    }

    // if no module name override, supplied, use default name
    if (modname === undefined) {
        modname = absolutePackagePrefix + name;
    }

    modNameSplit = name.split(".");

    if (modNameSplit.length > 1) {
        // if we're a module inside a package (i.e. a.b.c), then we'll need to return the
        // top-level package ('a'). recurse upwards on our parent, importing
        // all parent packages. so, here we're importing 'a.b', which will in
        // turn import 'a', and then return 'a' eventually.
        parentModName = modNameSplit.slice(0, modNameSplit.length - 1).join(".");
        topLevelModuleToReturn = Sk.importModuleInternal_(parentModName, dumpJS, undefined, undefined, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend);
    }

    ret = Sk.misceval.chain(topLevelModuleToReturn, function(topLevelModuleToReturn_) {
        topLevelModuleToReturn = topLevelModuleToReturn_;

        // if leaf is already in sys.modules, early out
        prev = Sk.sysmodules.quick$lookup(new Sk.builtin.str(modname));
        if (prev !== undefined) {
            // if we're a dotted module, return the top level, otherwise ourselves
            return topLevelModuleToReturn || prev;
        }
        // not in sys.modules, continue

        return Sk.misceval.chain(undefined, function() {
            var codeAndPath, co, googClosure;
            var searchFileName = name;
            var result;

            // If we're inside a package, look search using its __path__
            if (modNameSplit.length > 1) {
                if (!topLevelModuleToReturn) {
                    return undefined;
                }
                parentModule = Sk.sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName));
                searchFileName = modNameSplit[modNameSplit.length-1];
                searchPath = parentModule.tp$getattr(Sk.builtin.str.$path);
            }

            // otherwise:
            // - create module object
            // - add module object to sys.modules
            // - compile source to (function(){...});
            // - run module and set the module locals returned to the module __dict__
            module = new Sk.builtin.module();

            if (typeof suppliedPyBody === "string") {
                filename = name + ".py";
                co = Sk.compile(suppliedPyBody, filename, "exec", canSuspend);
            } else {
                co = Sk.misceval.chain(undefined, function() {
                    // If an onBeforeImport method is supplied, call it and if
                    // the result is false or a string, prevent the import.
                    // This allows for a user to conditionally prevent the usage
                    // of certain libraries.
                    if (Sk.onBeforeImport && typeof Sk.onBeforeImport === "function") {
                        return Sk.onBeforeImport(name);
                    }

                    return;
                }, function(result) {
                    if (result === false) {
                        throw new Sk.builtin.ImportError("Importing " + name + " is not allowed");
                    } else if (typeof result === "string") {
                        throw new Sk.builtin.ImportError(result);
                    }

                    // Try loading as a builtin (i.e. already in JS) module, then try .py files
                    return Sk.importSearchPathForName(searchFileName, ".js", searchPath);
                }, function(codeAndPath) {
                    if (codeAndPath) {
                        return {
                            funcname: "$builtinmodule", code: codeAndPath.code,
                            filename: codeAndPath.filename, packagePath: codeAndPath.packagePath
                        };
                    } else {
                        return Sk.misceval.chain(Sk.importSearchPathForName(searchFileName, ".py", searchPath), function(codeAndPath_) {
                            codeAndPath = codeAndPath_; // We'll want it in a moment
                            if (codeAndPath) {
                                return Sk.compile(codeAndPath.code, codeAndPath.filename, "exec", canSuspend);
                            }
                        }, function(co) {
                            if (co) {
                                co.packagePath = codeAndPath.packagePath;
                                return co;
                            }
                        });
                    }
                });

            }
            return co;

        }, function(co) {

            var finalcode;
            var withLineNumbers;
            var modscope;

            if (!co) {
                return undefined;
            }

            // Now we know this module exists, we can add it to the cache
            Sk.sysmodules.mp$ass_subscript(new Sk.builtin.str(modname), module);

            module.$js = co.code; // todo; only in DEBUG?
            finalcode = co.code;

            if (filename == null) {
                filename = co.filename;
            }

            if (Sk.dateSet == null || !Sk.dateSet) {
                finalcode = "Sk.execStart = Sk.lastYield = new Date();\n" + co.code;
                Sk.dateSet = true;
            }

            // if (!COMPILED)
            // {
            if (dumpJS) {
                withLineNumbers = function (code) {
                    var j;
                    var pad;
                    var width;
                    var i;
                    var beaut = Sk.js_beautify(code);
                    var lines = beaut.split("\n");
                    for (i = 1; i <= lines.length; ++i) {
                        width = ("" + i).length;
                        pad = "";
                        for (j = width; j < 5; ++j) {
                            pad += " ";
                        }
                        lines[i - 1] = "/* " + pad + i + " */ " + lines[i - 1];
                    }
                    return lines.join("\n");
                };
                finalcode = withLineNumbers(finalcode);
                Sk.debugout(finalcode);
            }
            // }

            finalcode += "\n" + co.funcname + ";";

            modscope = Sk.global["eval"](finalcode);

            module["$d"] = {
                "__name__": new Sk.builtin.str(modname),
                "__doc__": Sk.builtin.none.none$,
                "__package__": co.packagePath ? new Sk.builtin.str(modname) :
                parentModName ? new Sk.builtin.str(absolutePackagePrefix + parentModName) :
                relativePackageName ? relativePackageName : Sk.builtin.none.none$
            };
            if (co.packagePath) {
                module["$d"]["__path__"] = new Sk.builtin.tuple([new Sk.builtin.str(co.packagePath)]);
            }

            return modscope(module["$d"]);

        }, function (modlocs) {
            var i;

            if (modlocs === undefined) {
                if (returnUndefinedOnTopLevelNotFound && !topLevelModuleToReturn) {
                    return undefined;
                } else {
                    throw new Sk.builtin.ImportError("No module named " + name);
                }
            }

            // Some builtin modules replace their globals entirely.
            // For their benefit, we copy over any of the standard
            // dunder-values they didn't supply.
            if (modlocs !== module["$d"]) {
                for (i in module["$d"]) {
                    if (!modlocs[i]) {
                        modlocs[i] = module["$d"][i];
                    }
                }
                module["$d"] = modlocs;
            }

            // If an onAfterImport method is defined on the global Sk
            // then call it now after a library has been successfully imported
            // and compiled.
            if (Sk.onAfterImport && typeof Sk.onAfterImport === "function") {
                try {
                    Sk.onAfterImport(name);
                } catch (e) {
                }
            }

            if (topLevelModuleToReturn) {
                // if we were a dotted name, then we want to return the top-most
                // package. we store ourselves into our parent as an attribute
                parentModule.tp$setattr(new Sk.builtin.str(modNameSplit[modNameSplit.length - 1]), module);
                //print("import returning parent module, modname", modname, "__name__", toReturn.tp$getattr("__name__").v);
                return topLevelModuleToReturn;
            }

            if (relativeToPackage) {
                relativeToPackage.tp$setattr(new Sk.builtin.str(name), module);
            }

            //print("name", name, "modname", modname, "returning leaf");
            // otherwise we return the actual module that we just imported
            return module;
        });
    });

    return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
};

/**
 * @param {string} name the module name
 * @param {boolean=} dumpJS print out the js code after compilation for debugging
 * @param {boolean=} canSuspend can this function suspend and return a Suspension object?
 */
Sk.importModule = function (name, dumpJS, canSuspend) {
    return Sk.importModuleInternal_(name, dumpJS, undefined, undefined, undefined, false, canSuspend);
};

Sk.importMain = function (name, dumpJS, canSuspend) {
    Sk.dateSet = false;
    Sk.filesLoaded = false;
    // Added to reset imports
    Sk.sysmodules = new Sk.builtin.dict([]);
    Sk.realsyspath = undefined;

    Sk.resetCompiler();

    return Sk.importModuleInternal_(name, dumpJS, "__main__", undefined, undefined, false, canSuspend);
};

/**
 * **Run Python Code in Skulpt**
 *
 * When you want to hand Skulpt a string corresponding to a Python program this is the function.
 *
 * @param name {string}  File name to use for messages related to this run
 * @param dumpJS {boolean} print out the compiled javascript
 * @param body {string} Python Code
 * @param canSuspend {boolean}  Use Suspensions for async execution
 *
 */
Sk.importMainWithBody = function (name, dumpJS, body, canSuspend) {
    Sk.dateSet = false;
    Sk.filesLoaded = false;
    // Added to reset imports
    Sk.sysmodules = new Sk.builtin.dict([]);
    Sk.realsyspath = undefined;

    Sk.resetCompiler();

    return Sk.importModuleInternal_(name, dumpJS, "__main__", body, undefined, false, canSuspend);
};

/**
 * Imports internal python files into the `__builtin__` module. Used during startup
 * to compile and import all *.py files from the src/ directory.
 *
 * @param name {string}  File name to use for messages related to this run
 * @param dumpJS {boolean} print out the compiled javascript
 * @param body {string} Python Code
 * @param canSuspend {boolean}  Use Suspensions for async execution
 *
 */
Sk.importBuiltinWithBody = function (name, dumpJS, body, canSuspend) {
    return Sk.importModuleInternal_(name, dumpJS, "__builtin__."+name, body, undefined, false, canSuspend);
};

Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) {
    //print("Importing: ", JSON.stringify(name), JSON.stringify(fromlist), level);
    //if (name == "") { debugger; }

    // Save the Sk.globals variable importModuleInternal_ may replace it when it compiles
    // a Python language module.
    var saveSk = Sk.globals;

    // This might be a relative import, so first we get hold of the module object
    // representing this module's package (so we can search its __path__).
    // module.__package__ contains its name, so we use that to look it up in sys.modules.

    var relativeToPackage;
    var relativeToPackageName;
    var relativeToPackageNames;

    if (level === undefined) {
        level = Sk.__future__.absolute_import ? 0 : -1;
    }

    if (level !== 0 && globals["__package__"] && globals["__package__"] !== Sk.builtin.none.none$) {
        relativeToPackageName = globals["__package__"].v;
        if (relativeToPackageName && level > 0) {
            // Trim <level> packages off the end
            relativeToPackageNames = relativeToPackageName.split(".");
            if (level-1 >= relativeToPackageNames.length) {
                throw new Sk.builtin.ValueError("Attempted relative import beyond toplevel package");
            }
            relativeToPackageNames.length -= level-1;
            relativeToPackageName = relativeToPackageNames.join(".");
        }

        relativeToPackage = Sk.sysmodules.quick$lookup(new Sk.builtin.str(relativeToPackageName));
    }

    if (level > 0 && relativeToPackage === undefined) {
        throw new Sk.builtin.ValueError("Attempted relative import in non-package");
    }

    var dottedName = name.split(".");
    var firstDottedName = dottedName[0];

    return Sk.misceval.chain(undefined, function() {
        // Attempt local load first (and just fall through to global
        // case if level == -1 and we fail to load the top-level package)
        if (level !== 0 && relativeToPackage !== undefined) {
            if (name === "") {
                // "from .. import ..."
                return relativeToPackage;
            } else {
                return Sk.importModuleInternal_(name, undefined, relativeToPackageName + "." + name, undefined, relativeToPackage, level==-1, true);
            }
        }
    }, function(ret) {
        if (ret === undefined) {
            // Either it was always a global import, or it was an
            // either-way import that just fell through.
            relativeToPackage = undefined;
            relativeToPackageName = undefined;
            return Sk.importModuleInternal_(name, undefined, undefined, undefined, undefined, false, true);
        } else {
            return ret;
        }
    }, function(ret) {
        // We might also have to load modules named by the fromlist.
        // If there is no fromlist, we have reached the end of the lookup, return
        if (!fromlist || fromlist.length === 0) {
            return ret;
        } else {
            // try to load from-names as modules from the file system
            // if they are not present on the module itself
            var i;
            var fromName;
            var leafModule;
            var importChain;

            leafModule = Sk.sysmodules.mp$subscript(
                new Sk.builtin.str((relativeToPackageName || "") +
                    ((relativeToPackageName && name) ? "." : "") +
                    name));

            for (i = 0; i < fromlist.length; i++) {
                fromName = fromlist[i];

                // "ret" is the module we're importing from
                // Only import from file system if we have not found the fromName in the current module
                if (fromName != "*" && leafModule.tp$getattr(new Sk.builtin.str(fromName)) === undefined) {
                    importChain = Sk.misceval.chain(importChain,
                                                    Sk.importModuleInternal_.bind(null, fromName, undefined, undefined, undefined, leafModule, true, true)
                    );
                }
            }

            return Sk.misceval.chain(importChain, function() {
                // if there's a fromlist we want to return the leaf module
                // (ret), not the toplevel namespace
                Sk.asserts.assert(leafModule);
                return leafModule;
            });
        }

    }, function(ret) {
        if (saveSk !== Sk.globals) {
            Sk.globals = saveSk;
        }
        return ret;
    });
};

Sk.importStar = function (module, loc, global) {
    var __all__ = module.tp$getattr(new Sk.builtin.str("__all__"));

    if (__all__) {
        // TODO this does not support naming *modules* in __all__,
        // only variables
        for(let it = Sk.abstr.iter(__all__), i = it.tp$iternext();
            i !== undefined; i = it.tp$iternext()) {

            loc[i.v] = Sk.abstr.gattr(module, i);
        }
    } else {
        let props = Object["getOwnPropertyNames"](module["$d"]);
        for (let i in props) {
            if (props[i].charAt(0) != "_") {
                loc[props[i]] = module["$d"][props[i]];
            }
        }
    }
};

Sk.exportSymbol("Sk.importMain", Sk.importMain);
Sk.exportSymbol("Sk.importMainWithBody", Sk.importMainWithBody);
Sk.exportSymbol("Sk.importBuiltinWithBody", Sk.importBuiltinWithBody);
Sk.exportSymbol("Sk.builtin.__import__", Sk.builtin.__import__);
Sk.exportSymbol("Sk.importStar", Sk.importStar);


/***/ }),

/***/ "./src/int.js":
/*!********************!*\
  !*** ./src/int.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * 
 * @constructor
 * @extends {Sk.builtin.object}
 * @description
 * Function should only be called with a JS number|BigInt|String
 * If the number is a string then the size will be checked to determined whether it should be a number or BigInt
 * It assumed that a number passed it is within `Number.MaxSafeInteger`
 * Similarly if a BigInt is passed it is assumed that this is larger than `Number.MaxSafeInteger`
 * Internal code like `float.nb$int` checks the resulting JS instance before calling `new Sk.builtin.int_`
 * 
 * @param  {number|JSBI|string=} x
 * 
 */
Sk.builtin.int_ = Sk.abstr.buildNativeClass("int", {
    constructor: function int_(x) {
        Sk.asserts.assert(this instanceof Sk.builtin.int_, "bad call to int use 'new'");
        let v;
        if (typeof x === "number" || JSBI.__isBigInt(x)) {
            v = x;
        } else if (x === undefined) {
            v = 0;
        } else if (typeof x === "string") {
            v = stringToNumberOrBig(x);
        } else if (x.nb$int) {
            return x.nb$int(); // allow this as a slow path
        } else {
            Sk.asserts.fail("bad argument to int constructor");
        }
        this.v = v;
    },
    slots: /** @lends {Sk.builtin.int_.prototype}*/ {
        tp$as_number: true,
        tp$doc:
            "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given.  If x is a number, return x.__int__().  For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base.  The literal can be preceded by '+' or '-' and be surrounded\nby whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4",
        $r() {
            return new Sk.builtin.str(this.v.toString());
        },
        tp$hash() {
            const v = this.v;
            return typeof v === "number" ? v : JSBI.toNumber(JSBI.remainder(v, JSBI.__MAX_SAFE));
        },
        tp$new(args, kwargs) {
            let x, base;
            if (args.length + (kwargs ? kwargs.length : 0) === 1) {
                x = args[0];
                base = Sk.builtin.none.none$;
            } else {
                args = Sk.abstr.copyKeywordsToNamedArgs("int", [null, "base"], args, kwargs, [new Sk.builtin.int_(0), Sk.builtin.none.none$]);
                x = args[0];
                base = args[1];
            }
            x = getInt(x, base);

            if (this === Sk.builtin.int_.prototype) {
                return x;
            } else {
                const instance = new this.constructor();
                instance.v = x.v;
                return instance;
            }
        },
        tp$getattr: Sk.generic.getAttr,
        ob$eq: compareSlot((v, w) => v == w, JSBI.equal),
        ob$ne: compareSlot((v, w) => v != w, JSBI.notEqual),
        ob$gt: compareSlot((v, w) => v > w, JSBI.greaterThan),
        ob$ge: compareSlot((v, w) => v >= w, JSBI.greaterThanOrEqual),
        ob$lt: compareSlot((v, w) => v < w, JSBI.lessThan),
        ob$le: compareSlot((v, w) => v <= w, JSBI.lessThanOrEqual),

        nb$int: cloneSelf,
        nb$index() {
            return this.v;
        },
        nb$float() {
            const v = this.v;
            if (typeof v === "number") {
                return new Sk.builtin.float_(v);
            } else {
                const x = parseFloat(JSBI.toNumber(v));
                if (x === Infinity || x === -Infinity) {
                    throw new Sk.builtin.OverflowError("int too large to convert to float");
                }
                return new Sk.builtin.float_(x);
            }
        },
        nb$isnegative() {
            const v = this.v;
            return typeof v === "number" ? v < 0 : JSBI.lessThan(v, JSBI.__ZERO);
        },
        nb$ispositive() {
            const v = this.v;
            return typeof v === "number" ? v < 0 : JSBI.greaterThanOrEqual(v, JSBI.__ZERO);
        },
        nb$bool() {
            return this.v !== 0; // should be fine not to check BigInt here
        },

        nb$positive: cloneSelf,

        nb$negative: numberUnarySlot((v) => -v, JSBI.unaryMinus),

        nb$add: numberSlot(
            (v, w) => v + w,
            (v, w) => JSBI.numberIfSafe(JSBI.add(v, w))
        ),
        nb$subtract: numberSlot(
            (v, w) => v - w,
            (v, w) => JSBI.numberIfSafe(JSBI.subtract(v, w))
        ),
        nb$multiply: numberSlot((v, w) => v * w, JSBI.multiply),
        nb$divide(other) {
            if (Sk.__future__.division) {
                return this.nb$float().nb$divide(other);
            }
            return this.nb$floor_divide(other);
        },
        nb$floor_divide: numberDivisionSlot((v, w) => Math.floor(v / w), JSBI.divide),
        nb$remainder: numberDivisionSlot((v, w) => v - Math.floor(v / w) * w, JSBI.remainder),
        nb$divmod(other) {
            const floor = this.nb$floor_divide(other);
            const remainder = this.nb$remainder(other);
            if (floor === Sk.builtin.NotImplemented.NotImplemented$ || remainder === Sk.builtin.NotImplemented.NotImplemented$) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            return new Sk.builtin.tuple([floor, remainder]);
        },
        nb$and: numberBitSlot((v, w) => v & w, JSBI.bitwiseAnd),
        nb$or: numberBitSlot((v, w) => v | w, JSBI.bitwiseOr),
        nb$xor: numberBitSlot((v, w) => v ^ w, JSBI.bitwiseXor),

        nb$abs: numberUnarySlot(Math.abs, (v) => (JSBI.lessThan(v, JSBI.__ZERO) ? JSBI.unaryMinus(v) : v)),

        nb$lshift: numberShiftSlot((v, w) => {
            if (w < 53) {
                const tmp = v * 2 * shiftconsts[w];
                if (numberOrStringWithinThreshold(tmp)) {
                    return tmp;
                }
                return;
            }
        }, JSBI.leftShift),
        nb$rshift: numberShiftSlot(
            (v, w) => {
                const tmp = v >> w;
                if (v > 0 && tmp < 0) {
                    return tmp & (Math.pow(2, 32 - w) - 1);
                }
                return tmp;
            },
            (v, w) => JSBI.numberIfSafe(JSBI.signedRightShift(v, w))
        ),

        nb$invert: numberUnarySlot((v) => ~v, JSBI.bitwiseNot),
        nb$power(other, mod) {
            let ret;
            if (mod !== undefined) {
                if (Sk.builtin.checkNone(mod)) {
                    mod = undefined;
                }
            }
            if (other instanceof Sk.builtin.int_ && (mod === undefined || mod instanceof Sk.builtin.int_)) {
                let v = this.v;
                let w = other.v;
                if (typeof v === "number" && typeof w === "number") {
                    const power = Math.pow(v, w);
                    if (numberOrStringWithinThreshold(power)) {
                        ret = w < 0 ? new Sk.builtin.float_(power) : new Sk.builtin.int_(power);
                        if (mod === undefined) {
                            return ret;
                        }
                    }
                }
                if (mod !== undefined) {
                    if (other.nb$isnegative()) {
                        throw new Sk.builtin.ValueError("pow() 2nd argument cannot be negative when 3rd argument specified");
                    } else if (mod.v === 0) {
                        throw new Sk.builtin.ValueError("pow() 3rd argument cannot be 0");
                    }
                    if (ret !== undefined) {
                        return ret.nb$remainder(mod);
                    }
                    return new Sk.builtin.int_(JSBI.powermod(bigUp(v), bigUp(w), bigUp(mod.v)));
                }
                // if we're here then we've fallen through so do bigint exponentiate
                return new Sk.builtin.int_(JSBI.exponentiate(bigUp(v), bigUp(w)));
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
        nb$long() {
            return new Sk.builtin.lng(this.v);
        },
    },
    getsets: /** @lends {Sk.builtin.int_.prototype}*/ {
        real: {
            $get: cloneSelf,
            $doc: "the real part of a complex number",
        },
        imag: {
            $get() {
                return new Sk.builtin.int_(0);
            },
            $doc: "the imaginary part of a complex number",
        },
    },
    methods: /** @lends {Sk.builtin.int_.prototype}*/ {
        conjugate: {
            $meth: cloneSelf,
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Returns self, the complex conjugate of any int.",
        },
        bit_length: {
            $meth() {
                return new Sk.builtin.int_(Sk.builtin.bin(this).sq$length() - 2);
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Number of bits necessary to represent self in binary.\n\n>>> bin(37)\n'0b100101'\n>>> (37).bit_length()\n6",
        },
        to_bytes: {
            $meth() {
                throw new Sk.builtin.NotImplementedError("Not yet implemented in Skulpt");
            },
            $flags: { FastCall: true },
            $textsig: "($self, /, length, byteorder, *, signed=False)",
            $doc:
                "Return an array of bytes representing an integer.\n\n  length\n    Length of bytes object to use.  An OverflowError is raised if the\n    integer is not representable with the given number of bytes.\n  byteorder\n    The byte order used to represent the integer.  If byteorder is 'big',\n    the most significant byte is at the beginning of the byte array.  If\n    byteorder is 'little', the most significant byte is at the end of the\n    byte array.  To request the native byte order of the host system, use\n    `sys.byteorder' as the byte order value.\n  signed\n    Determines whether two's complement is used to represent the integer.\n    If signed is False and a negative integer is given, an OverflowError\n    is raised.",
        },
        __trunc__: {
            $meth: cloneSelf,
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Truncating an Integral returns itself.",
        },
        __floor__: {
            $meth: cloneSelf,
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Flooring an Integral returns itself.",
        },
        __ceil__: {
            $meth: cloneSelf,
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Ceiling of an Integral returns itself.",
        },
        __round__: {
            $meth(ndigits) {
                return this.round$(ndigits);
            },
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: null,
            $doc: "Rounding an Integral returns itself.\nRounding with an ndigits argument also returns an integer.",
        },
        __getnewargs__: {
            $meth() {
                return new Sk.builtin.tuple([new Sk.builtin.int_(this.v)]);
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: Sk.builtin.none.none$,
        },
        __format__: {
            $meth: Sk.formatting.mkNumber__format__(false),
            $flags: { OneArg: true },
            $textsig: "($self, format_spec, /)",
            $doc: Sk.builtin.none.none$,
        },
    },
    proto: /** @lends {Sk.builtin.int_.prototype}*/ {
        str$(base, sign) {
            let tmp;
            if (base === undefined || base === 10) {
                tmp = this.v.toString();
            } else {
                tmp = this.v.toString(base);
            }
            if (sign || sign === undefined) {
                return tmp;
            } else if (tmp[0] === "-") {
                tmp = tmp.substring(1);
            }
            return tmp;
        },
        round$(ndigits) {
            if (ndigits === undefined) {
                ndigits = 0;
            } else {
                ndigits = Sk.misceval.asIndexSized(ndigits);
            }
            const v = this.v;
            const multiplier = Math.pow(10, -ndigits);
            let tmp;
            if (ndigits > 0) {
                return new Sk.builtin.int_(v);
            }
            if (typeof v === "number" && Sk.__future__.bankers_rounding) {
                const num10 = v / multiplier;
                const rounded = Math.round(num10);
                const bankRound = (num10 > 0 ? num10 : -num10) % 1 === 0.5 ? (0 === rounded % 2 ? rounded : rounded - 1) : rounded;
                const result = bankRound * multiplier;
                return new Sk.builtin.int_(result);
            } else if (typeof v === "number") {
                return new Sk.builtin.int_(Math.round(v / multiplier) * multiplier);
            } else {
                const BigMultiplier = JSBI.BigInt(multiplier * 10);
                const ten = JSBI.BigInt(10);
                tmp = JSBI.divide(v, BigMultiplier);
                const undecided = JSBI.divide(tmp, ten);
                const pt5 = JSBI.subtract(tmp, JSBI.multiply(ten, undecided));
                if (JSBI.toNumber(pt5) < 5) {
                    tmp = JSBI.multiply(JSBI.multiply(undecided, ten), BigMultiplier);
                } else {
                    JSBI.multiply(JSBI.multiply(JSBI.add(undecided, JSBI.BigInt(1), ten), BigMultiplier));
                }
                return new Sk.builtin.int_(tmp);
            }
        },
    },
});

Sk.exportSymbol("Sk.builtin.int_", Sk.builtin.int_);
/**
 * A function that will return either a number or a BigInt
 * 
 * There are two functions passed to this slot the quick function where both int values are number
 * and the JSBI.BigInt version of the same function
 * The fall through case where one or both of the int values is a bigint
 * @ignore
 * 
 * @private
 * 
 * @param {Function} number_func
 * @param {Function} bigint_func
 */
function numberSlot(number_func, bigint_func) {
    /**
    * @this {Sk.builtin.int_}
    * 
    * @param {Sk.builtin.int_|Sk.builtin.object} other 
    * @return {Sk.builtin.int_|Sk.builtin.NotImplemented}
    */
    function doNumberSlot(other) {
        if (other instanceof Sk.builtin.int_) {
            /**@type {number|JSBI} */
            let v = this.v;
            /**@type {number|JSBI} */
            let w = other.v;
            if (typeof v === "number" && typeof w === "number") {
                const res = number_func(v, w);
                if (numberOrStringWithinThreshold(res)) {
                    return new Sk.builtin.int_(res);
                }
            }
            v = bigUp(v);
            w = bigUp(w);
            return new Sk.builtin.int_(bigint_func(v, w));
        }
        return Sk.builtin.NotImplemented.NotImplemented$;
    };
    return doNumberSlot;
}

function compareSlot(number_func, bigint_func) {
    return function (other) {
        if (other instanceof Sk.builtin.int_) {
            let v = this.v;
            let w = other.v;
            if (typeof v === "number" && typeof w === "number") {
                return number_func(v, w);
            }
            v = bigUp(v);
            w = bigUp(w);
            return bigint_func(v, w);
        }
        return Sk.builtin.NotImplemented.NotImplemented$;
    };
}

/**
 * 
 * @param {function(number): number} number_func 
 * @param {function(JSBI): JSBI} bigint_func 
 * @ignore
 * 
 */
function numberUnarySlot(number_func, bigint_func) {
    /**
     * @this {Sk.builtin.int_}
     * @return {Sk.builtin.int_}
     */
    function doUnarySlot () {
        const v = this.v;
        if (typeof v === "number") {
            return new Sk.builtin.int_(number_func(v));
        }
        return new Sk.builtin.int_(bigint_func(v));
    };
    return doUnarySlot;
}

function cloneSelf() {
    return new Sk.builtin.int_(this.v);
}

function numberDivisionSlot(number_func, bigint_func) {
    return function (other) {
        if (other instanceof Sk.builtin.int_) {
            let v = this.v;
            let w = other.v;
            if (w === 0) {
                throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");
            }
            if (typeof v === "number" && typeof w === "number") {
                // it's integer division so no need to check if the number got bigger!
                return new Sk.builtin.int_(number_func(v, w));
            }
            v = bigUp(v);
            w = bigUp(w);
            return new Sk.builtin.int_(JSBI.numberIfSafe(bigint_func(v, w)));
        }
        return Sk.builtin.NotImplemented.NotImplemented$;
    };
}

function numberShiftSlot(number_func, bigint_func) {
    return function (other) {
        if (other instanceof Sk.builtin.int_) {
            let v = this.v;
            let w = other.v;
            if (v === 0) {
                return new Sk.builtin.int_(this.v);
            }
            if (typeof w === "number") {
                if (w < 0) {
                    throw new Sk.builtin.ValueError("negative shift count");
                }
                if (typeof v === "number") {
                    const tmp = number_func(v, w);
                    if (tmp !== undefined) {
                        return new Sk.builtin.int_(tmp);
                    }
                }
                w = JSBI.BigInt(w);
            } else if (JSBI.lessThan(JSBI.BigInt(0))) {
                throw new Sk.builtin.ValueError("negative shift count");
            }
            v = bigUp(v);
            return new Sk.builtin.int_(bigint_func(v, w)); // con't convert if safe for leftshift
        }
        return Sk.builtin.NotImplemented.NotImplemented$;
    };
}

function numberBitSlot(number_func, bigint_func) {
    return function (other) {
        if (other instanceof Sk.builtin.int_) {
            let v = this.v;
            let w = other.v;
            if (typeof v === "number" && typeof w === "number") {
                let tmp = number_func(v, w);
                if (tmp < 0) {
                    tmp = tmp + 4294967296; // convert back to unsigned
                }
                return new Sk.builtin.int_(tmp);
            }
            v = bigUp(v);
            w = bigUp(w);
            return new Sk.builtin.int_(JSBI.numberIfSafe(bigint_func(v, w)));
        }
        return Sk.builtin.NotImplemented.NotImplemented$;
    };
}


const validUnderscores = /_(?=[^_])/g;
/**
 * Takes a JavaScript string and returns a number using the parser and negater
 *  functions (for int/long right now)
 * @param  {string} s       Javascript string to convert to a number.
 * @param  {number|string=} base    The base of the number.
 */
Sk.str2number = function (s, base) {
    var origs = s,
        neg = false,
        i,
        ch,
        val;
    // strip whitespace from ends
    // s = s.trim();
    s = s.replace(/^\s+|\s+$/g, "");

    // check for minus sign
    if (s.charAt(0) === "-") {
        neg = true;
        s = s.substring(1);
    }

    // check for plus sign
    if (s.charAt(0) === "+") {
        s = s.substring(1);
    }

    if (base === null || base === undefined) {
        base = 10;
    } // default radix is 10, not dwim
    if (base < 2 || base > 36) {
        if (base !== 0) {
            throw new Sk.builtin.ValueError("int() base must be >= 2 and <= 36");
        }
    }
    if (typeof base === "string") {
        base = Number(base); // keep closure happy for parseInt
    }

    if (s.substring(0, 2).toLowerCase() === "0x") {
        if (base === 16 || base === 0) {
            s = s.substring(2);
            base = 16;
        } else if (base < 34) {
            throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'");
        }
    } else if (s.substring(0, 2).toLowerCase() === "0b") {
        if (base === 2 || base === 0) {
            s = s.substring(2);
            base = 2;
        } else if (base < 12) {
            throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'");
        }
    } else if (s.substring(0, 2).toLowerCase() === "0o") {
        if (base === 8 || base === 0) {
            s = s.substring(2);
            base = 8;
        } else if (base < 25) {
            throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'");
        }
    } else if (s.charAt(0) === "0") {
        if (s === "0") {
            return 0;
        }
        if (base === 8 || base === 0) {
            base = 8;
        }
    }

    if (base === 0) {
        base = 10;
    }

    if (s.indexOf("_") !== -1) {
        if (s.indexOf("__") !== -1) {
            throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'");
        }

        if (base !== 10) {
            s = s.replace(validUnderscores, "");
        } else {
            // avoid replacing initial `_` if present
            // workaround since closure-compiler errors on lookbehinds
            s = s.charAt(0) + s.substring(1).replace(validUnderscores, "");
        }
    }

    if (s.length === 0) {
        throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'");
    }

    // check all characters are valid
    for (i = 0; i < s.length; i = i + 1) {
        ch = s.charCodeAt(i);
        val = base;
        if (ch >= 48 && ch <= 57) {
            // 0-9
            val = ch - 48;
        } else if (ch >= 65 && ch <= 90) {
            // A-Z
            val = ch - 65 + 10;
        } else if (ch >= 97 && ch <= 122) {
            // a-z
            val = ch - 97 + 10;
        }

        if (val >= base) {
            throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'");
        }
    }

    if (neg) {
        s = "-" + s;
    }
    val = parseInt(s, base);
    if (numberOrStringWithinThreshold(val)) {
        return val; // will convert our string to a number
    }
    return fromStrToBigWithBase(s, base);
};

Sk.builtin.int_.py2$methods = {};

/**
 * 
 * @param {string} s 
 * @param {number=} base 
 */
Sk.longFromStr = function (s, base) {
    if (Sk.__future__.python3) {
        return new Sk.builtin.int_(stringToNumberOrBig(s));
    } else {
        const num = Sk.str2number(s, base);
        return new Sk.builtin.lng(num);
    }
};
Sk.exportSymbol("Sk.longFromStr", Sk.longFromStr);


function numberOrStringWithinThreshold(v) {
    return v <= Number.MAX_SAFE_INTEGER && v >= -Number.MAX_SAFE_INTEGER;
}

Sk.builtin.int_.withinThreshold = numberOrStringWithinThreshold;

function stringToNumberOrBig(s) {
    if (s <= Number.MAX_SAFE_INTEGER && s >= -Number.MAX_SAFE_INTEGER) {
        return +s;
    }
    return JSBI.BigInt(s);
}

Sk.builtin.int_.stringToNumberOrBig = stringToNumberOrBig;


function bigUp(v) {
    if (typeof v === "number") {
        return JSBI.BigInt(v);
    }
    return v;
}


function getInt(x, base) {
    let func, res;
    // if base is not of type int, try calling .__index__
    if (base !== Sk.builtin.none.none$) {
        base = Sk.misceval.asIndexOrThrow(base);
    } else {
        base = null;
    }

    if (x instanceof Sk.builtin.str) {
        if (base === null) {
            base = 10;
        }
        return new Sk.builtin.int_(Sk.str2number(x.v, base));
    } else if (base !== null) {
        throw new Sk.builtin.TypeError("int() can't convert non-string with explicit base");
    } else if (x.nb$int) {
        return x.nb$int();
    }

    if ((func = Sk.abstr.lookupSpecial(x, Sk.builtin.str.$trunc))) {
        res = Sk.misceval.callsimArray(func, []);
        // check return type of magic methods
        if (!Sk.builtin.checkInt(res)) {
            throw new Sk.builtin.TypeError(Sk.builtin.str.$trunc.$jsstr() + " returned non-Integral (type " + Sk.abstr.typeName(x) + ")");
        }
        return new Sk.builtin.int_(res.v);
    }

    throw new Sk.builtin.TypeError("int() argument must be a string, a bytes-like object or a number, not '" + Sk.abstr.typeName(x) + "'");
}

/**
 *
 * We don't need to check the string has valid digits since str2number did that for us
 * @param {*} s
 * @param {*} base
 * @ignore
 */
function fromStrToBigWithBase(s, base) {
    let neg = false;
    if (s[0] === "-") {
        neg = true;
        s = s.substring(1);
    }
    base = JSBI.BigInt(base);
    let power = JSBI.BigInt(1);
    let num = JSBI.BigInt(0);
    let toadd, val;
    for (let i = s.length - 1; i >= 0; i--) {
        val = s.charCodeAt(i);
        if (val >= 48 && val <= 57) {
            // 0-9
            val = val - 48;
        } else if (val >= 65 && val <= 90) {
            // A-Z
            val = val - 65 + 10;
        } else if (val >= 97 && val <= 122) {
            // a-z
            val = val - 97 + 10;
        }
        toadd = JSBI.multiply(JSBI.BigInt(val), power);
        num = JSBI.add(num, toadd);
        power = JSBI.multiply(power, base);
    }
    if (neg) {
        num = JSBI.multiply(num, JSBI.BigInt(-1));
    }
    return num;
}

const shiftconsts = [
    0.5,
    1,
    2,
    4,
    8,
    16,
    32,
    64,
    128,
    256,
    512,
    1024,
    2048,
    4096,
    8192,
    16384,
    32768,
    65536,
    131072,
    262144,
    524288,
    1048576,
    2097152,
    4194304,
    8388608,
    16777216,
    33554432,
    67108864,
    134217728,
    268435456,
    536870912,
    1073741824,
    2147483648,
    4294967296,
    8589934592,
    17179869184,
    34359738368,
    68719476736,
    137438953472,
    274877906944,
    549755813888,
    1099511627776,
    2199023255552,
    4398046511104,
    8796093022208,
    17592186044416,
    35184372088832,
    70368744177664,
    140737488355328,
    281474976710656,
    562949953421312,
    1125899906842624,
    2251799813685248,
    4503599627370496,
    9007199254740992,
];


/**
 * @constructor
 *
 * @description
 * This is only for backward compatibility with py2. 
 * We take the approach of using a trivial subclass with int and overriding a few methods
 *
 * @param {number|string|JSBI} x 
 * @extends {Sk.builtin.int_}
 * @ignore
 */
Sk.builtin.lng = Sk.abstr.buildNativeClass("long", {
    base: Sk.builtin.int_, // not technically correct but makes backward compatibility easy
    constructor: function lng (x) {
        Sk.builtin.int_.call(this, x);
    },
    slots: /** @lends {Sk.builtin.lng.prototype} */{
        $r() {
            return new Sk.builtin.str(this.v.toString() + "L");
        },
        tp$as_number: true,
        nb$negative() {
            return new Sk.builtin.lng(intProto.nb$negative.call(this).v);
        },
        nb$positive() {
            return new Sk.builtin.lng(intProto.nb$positive.call(this).v);
        },
    },
});

const intProto = Sk.builtin.int_.prototype;


/***/ }),

/***/ "./src/list.js":
/*!*********************!*\
  !*** ./src/list.js ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Array} L
 *
 * @extends {Sk.builtin.object}
 */
Sk.builtin.list = Sk.abstr.buildNativeClass("list", {
    constructor: function list(L) {
        // this is an internal function and should be called with an array object
        if (L === undefined) {
            L = [];
        } else if (!Array.isArray(L)) {
            L = Sk.misceval.arrayFromIterable(L); 
            // internal calls to constructor can't suspend - avoid using this;
        }
        Sk.asserts.assert(this instanceof Sk.builtin.list, "bad call to list, use 'new' with an Array of python objects");
        this.v = L;
        this.in$repr = false;
    },
    slots: /** @lends {Sk.builtin.list.prototype}*/ {
        tp$getattr: Sk.generic.getAttr,
        tp$as_sequence_or_mapping: true,
        tp$hash: Sk.builtin.none.none$,
        tp$doc:
            "Built-in mutable sequence.\n\nIf no argument is given, the constructor creates a new empty list.\nThe argument must be an iterable if specified.",
        tp$new: Sk.generic.new,
        tp$init(args, kwargs) {
            // this will be an Sk.builtin.list.prototype or a sk$klass.prototype that inherits from Sk.builtin.list.prototype
            Sk.abstr.checkNoKwargs("list", kwargs);
            Sk.abstr.checkArgsLen("list", args, 0, 1);
            return Sk.misceval.chain(Sk.misceval.arrayFromIterable(args[0], true), (L) => {
                this.v = L;
            });
        },
        $r() {
            if (this.in$repr) {
                return new Sk.builtin.str("[...]");
            }
            this.in$repr = true;
            const ret = this.v.map((x) => Sk.misceval.objectRepr(x));
            this.in$repr = false;
            return new Sk.builtin.str("[" + ret.join(", ") + "]");
        },
        tp$richcompare: Sk.generic.seqCompare,
        tp$iter() {
            return new list_iter_(this);
        },

        // sequence and mapping slots
        sq$length() {
            return this.v.length;
        },
        sq$concat(other) {
            if (!(other instanceof Sk.builtin.list)) {
                throw new Sk.builtin.TypeError("can only concatenate list to list");
            }
            return new Sk.builtin.list(this.v.concat(other.v));
        },
        sq$contains(item) {
            for (let it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
                if (i === item || Sk.misceval.richCompareBool(i, item, "Eq")) {
                    return true;
                }
            }
            return false;
        },
        sq$repeat(n) {
            if (!Sk.misceval.isIndex(n)) {
                throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'");
            }
            n = Sk.misceval.asIndexSized(n, Sk.builtin.OverflowError);
            if (n * this.v.length > Number.MAX_SAFE_INTEGER) {
                throw new Sk.builtin.OverflowError();
            }
            const ret = [];
            for (let i = 0; i < n; i++) {
                for (let j = 0; j < this.v.length; j++) {
                    ret.push(this.v[j]);
                }
            }
            return new Sk.builtin.list(ret);
        },
        mp$subscript(index) {
            if (Sk.misceval.isIndex(index)) {
                let i = Sk.misceval.asIndexSized(index, Sk.builtin.IndexError);
                i = this.list$inRange(i, "list index out of range");
                return this.v[i];
            } else if (index instanceof Sk.builtin.slice) {
                const ret = [];
                index.sssiter$(this.v.length, (i) => {
                    ret.push(this.v[i]);
                });
                return new Sk.builtin.list(ret);
            }
            throw new Sk.builtin.TypeError("list indices must be integers or slices, not " + Sk.abstr.typeName(index));
        },
        mp$ass_subscript(index, value) {
            if (value === undefined) {
                this.del$subscript(index);
            } else {
                this.ass$subscript(index, value);
            }
        },
        sq$inplace_concat(other) {
            if (other === this) {
                // prevent an infinite loop
                this.v.push(...this.v);
                return this;
            }
            return Sk.misceval.chain(
                Sk.misceval.iterFor(Sk.abstr.iter(other), (i) => {
                    this.v.push(i);
                }),
                () => this
            );
        },
        sq$inplace_repeat(n) {
            if (!Sk.misceval.isIndex(n)) {
                throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'");
            }
            n = Sk.misceval.asIndexSized(n, Sk.builtin.OverflowError);
            const len = this.v.length;
            if (n <= 0) {
                this.v.length = 0;
            } else if (n * len > Number.MAX_SAFE_INTEGER) {
                throw new Sk.builtin.OverflowError();
            }
            
            for (let i = 1; i < n; i++) {
                for (let j = 0; j < len; j++) {
                    this.v.push(this.v[j]);
                }
            }
            return this;
        },
    },
    methods: /** @lends {Sk.builtin.list.prototype}*/ {
        __reversed__: {
            $meth() {
                return new reverselist_iter_(this);
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Return a reverse iterator over the list.",
        },
        clear: {
            $meth() {
                this.v.length = 0;
                return Sk.builtin.none.none$;
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Remove all items from list.",
        },
        copy: {
            $meth() {
                return new Sk.builtin.list(this.v.slice(0));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Return a shallow copy of the list.",
        },
        append: {
            $meth(item) {
                this.v.push(item);
                return Sk.builtin.none.none$;
            },
            $flags: { OneArg: true },
            $textsig: "($self, object, /)",
            $doc: "Append object to the end of the list.",
        },
        insert: {
            $meth(i, x) {
                i = Sk.misceval.asIndexSized(i, Sk.builtin.OverflowError);
                const { start } = Sk.builtin.slice.startEnd$wrt(this, i);
                this.v.splice(start, 0, x);
                return Sk.builtin.none.none$;
            },
            $flags: { MinArgs: 2, MaxArgs: 2 },
            $textsig: "($self, index, object, /)",
            $doc: "Insert object before index.",
        },
        extend: {
            $meth(iterable) {
                if (iterable === this) {
                    // prevent an infinite loop
                    this.v.push(...this.v);
                    return Sk.builtin.none.none$;
                }
                return Sk.misceval.chain(
                    Sk.misceval.iterFor(Sk.abstr.iter(iterable), (i) => {
                        this.v.push(i);
                    }),
                    () => Sk.builtin.none.none$
                );
            },
            $flags: { OneArg: true },
            $textsig: "($self, iterable, /)",
            $doc: "Extend list by appending elements from the iterable.",
        },
        pop: {
            $meth(i) {
                if (i === undefined) {
                    i = this.v.length - 1;
                } else {
                    i = Sk.misceval.asIndexSized(i, Sk.builtin.OverflowError);
                }
                i = this.list$inRange(i, "pop index out of range");
                const res = this.v[i];
                this.v.splice(i, 1);
                return res;
            },
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, index=-1, /)",
            $doc: "Remove and return item at index (default last).\n\nRaises IndexError if list is empty or index is out of range.",
        },
        remove: {
            $meth(item) {
                const i = this.list$indexOf(item);
                if (i === -1) {
                    throw new Sk.builtin.ValueError("list.remove(x): x not in list");
                }
                this.v.splice(i, 1);
                return Sk.builtin.none.none$;
            },
            $flags: { OneArg: true },
            $textsig: "($self, value, /)",
            $doc: "Remove first occurrence of value.\n\nRaises ValueError if the value is not present.",
        },
        sort: {
            $meth(args, kwargs) {
                if (args.length) {
                    throw new Sk.builtin.TypeError("sort() takes no positional arguments");
                }
                const [key, reverse] = Sk.abstr.copyKeywordsToNamedArgs("sort", ["key", "reverse"], args, kwargs, [
                    Sk.builtin.none.none$,
                    Sk.builtin.bool.false$,
                ]);
                return this.list$sort(undefined, key, reverse);
            },
            $flags: { FastCall: true },
            $textsig: "($self, /, *, key=None, reverse=False)",
            $doc: "Stable sort *IN PLACE*.",
        },
        index: {
            $meth(value, start, stop) {
                if ((start !== undefined && !Sk.misceval.isIndex(start)) || (stop !== undefined && !Sk.misceval.isIndex(stop))) {
                    // unusually can't have None here so check this first...
                    throw new Sk.builtin.TypeError("slice indices must be integers or have an __index__ method");
                }
                const i = this.list$indexOf(value, start, stop);
                if (i === -1) {
                    throw new Sk.builtin.ValueError(Sk.misceval.objectRepr(value) + " is not in list");
                }
                return new Sk.builtin.int_(i);
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: "($self, value, start=0, stop=sys.maxsize, /)",
            $doc: "Return first index of value.\n\nRaises ValueError if the value is not present.",
        },
        count: {
            $meth(item) {
                let count = 0;
                const len = this.v.length;
                for (let i = 0; i < len; i++) {
                    if (this.v[i] === item || Sk.misceval.richCompareBool(this.v[i], item, "Eq")) {
                        count += 1;
                    }
                }
                return new Sk.builtin.int_(count);
            },
            $flags: { OneArg: true },
            $textsig: "($self, value, /)",
            $doc: "Return number of occurrences of value.",
        },
        reverse: {
            $meth() {
                this.list$reverse();
                return Sk.builtin.none.none$;
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Reverse *IN PLACE*.",
        },
    },
    proto: /** @lends {Sk.builtin.list.prototype}*/ {
        sk$asarray() {
            return this.v.slice(0);
        },
        list$sort,
        list$inRange(i, msg) {
            if (i < 0) {
                i += this.v.length;
            }
            if (i >= 0 && i < this.v.length) {
                return i;
            }
            throw new Sk.builtin.IndexError(msg);
        },
        list$indexOf(item, start, end) {
            ({ start, end } = Sk.builtin.slice.startEnd$wrt(this, start, end));
            for (let i = start; i < end && i < this.v.length; i++) {
                if (this.v[i] === item || Sk.misceval.richCompareBool(this.v[i], item, "Eq")) {
                    return i;
                }
            }
            return -1;
        },
        list$reverse() {
            this.v.reverse();
        },
        ass$subscript(index, value) {
            if (Sk.misceval.isIndex(index)) {
                this.ass$index(index, value);
            } else if (index instanceof Sk.builtin.slice) {
                const { start, stop, step } = index.slice$indices(this.v.length);
                if (step === 1) {
                    this.ass$slice(start, stop, value);
                } else {
                    this.ass$ext_slice(index, value);
                }
            } else {
                throw new Sk.builtin.TypeError("list indices must be integers or slices, not " + Sk.abstr.typeName(index));
            }
        },
        ass$index(index, value) {
            let i = Sk.misceval.asIndexSized(index, Sk.builtin.IndexError);
            i = this.list$inRange(i, "list assignment index out of range");
            this.v[i] = value;
        },
        ass$slice(start, stop, iterable) {
            if (!Sk.builtin.checkIterable(iterable)) {
                throw new Sk.builtin.TypeError("can only assign an iterable");
            }
            const vals = Sk.misceval.arrayFromIterable(iterable);
            this.v.splice(start, stop - start, ...vals);
        },
        ass$ext_slice(slice, iterable) {
            const indices = [];
            slice.sssiter$(this.v.length, (i) => {
                indices.push(i);
            });
            if (!Sk.builtin.checkIterable(iterable)) {
                throw new Sk.builtin.TypeError("must assign iterable to extended slice");
            }
            const vals = Sk.misceval.arrayFromIterable(iterable);
            if (indices.length !== vals.length) {
                throw new Sk.builtin.ValueError("attempt to assign sequence of size " + vals.length + " to extended slice of size " + indices.length);
            }
            for (let i = 0; i < indices.length; i++) {
                this.v.splice(indices[i], 1, vals[i]);
            }
        },
        del$subscript(index) {
            if (Sk.misceval.isIndex(index)) {
                this.del$index(index);
            } else if (index instanceof Sk.builtin.slice) {
                const { start, stop, step } = index.slice$indices(this.v.length);
                if (step === 1) {
                    this.del$slice(start, stop);
                } else {
                    this.del$ext_slice(index, step > 0 ? 1 : 0);
                }
            } else {
                throw new Sk.builtin.TypeError("list indices must be integers, not " + Sk.abstr.typeName(index));
            }
        },
        del$index(index) {
            let i = Sk.misceval.asIndexSized(index, Sk.builtin.IndexError);
            i = this.list$inRange(i, "list assignment index out of range");
            this.v.splice(i, 1);
        },
        del$slice(start, stop) {
            this.v.splice(start, stop - start);
        },
        del$ext_slice(slice, offdir) {
            let dec = 0; // offset of removal for next index (because we'll have removed, but the iterator is giving orig indices)
            slice.sssiter$(this.v.length, (i) => {
                this.v.splice(i - dec, 1);
                dec += offdir;
            });
        },
    },
});

Sk.exportSymbol("Sk.builtin.list", Sk.builtin.list);

/**
 * @function
 * 
 * @param {?=} cmp optional (not supported in py3)
 * @param {?=} key optional (keyword only argument in py3)
 * @param {?=} reverse optional (keyword only argument in py3)
 * 
 * @private
 */
function list$sort(cmp, key, reverse) {
    const has_key = key != null && key !== Sk.builtin.none.none$;
    const has_cmp = cmp != null && cmp !== Sk.builtin.none.none$;
    let rev, item;
    if (reverse === undefined) {
        rev = false;
    } else if (!Sk.builtin.checkInt(reverse)) {
        throw new Sk.builtin.TypeError("an integer is required");
    } else {
        rev = Sk.misceval.isTrue(reverse);
    }
    const timsort = new Sk.builtin.timSort(this);

    this.v = [];
    const zero = new Sk.builtin.int_(0);

    if (has_key) {
        if (has_cmp) {
            timsort.lt = function (a, b) {
                var res = Sk.misceval.callsimArray(cmp, [a[0], b[0]]);
                return Sk.misceval.richCompareBool(res, zero, "Lt");
            };
        } else {
            timsort.lt = function (a, b) {
                return Sk.misceval.richCompareBool(a[0], b[0], "Lt");
            };
        }
        for (let i = 0; i < timsort.listlength; i++) {
            item = timsort.list.v[i];
            const keyvalue = Sk.misceval.callsimArray(key, [item]);
            timsort.list.v[i] = [keyvalue, item];
        }
    } else if (has_cmp) {
        timsort.lt = function (a, b) {
            var res = Sk.misceval.callsimArray(cmp, [a, b]);
            return Sk.misceval.richCompareBool(res, zero, "Lt");
        };
    }

    if (rev) {
        timsort.list.list$reverse();
    }

    timsort.sort();

    if (rev) {
        timsort.list.list$reverse();
    }

    if (has_key) {
        for (let j = 0; j < timsort.listlength; j++) {
            item = timsort.list.v[j][1];
            timsort.list.v[j] = item;
        }
    }

    const mucked = this.sq$length() > 0;

    this.v = timsort.list.v;

    if (mucked) {
        throw new Sk.builtin.ValueError("list modified during sort");
    }

    return Sk.builtin.none.none$;
};

Sk.builtin.list.py2$methods = {
    sort: {
        $name: "sort",
        $meth(args, kwargs) {
            const [cmp, key, reverse] = Sk.abstr.copyKeywordsToNamedArgs("sort", ["cmp", "key", "reverse"], args, kwargs, [
                Sk.builtin.none.none$,
                Sk.builtin.none.none$,
                Sk.builtin.bool.false$,
            ]);
            return this.list$sort(cmp, key, reverse);
        },
        $flags: {
            FastCall: true, // named args might be better here but one of the args is pyFalse
            // and bool class does not exist yet. So use FastCall instead.
        },
        $textsig: "($self, cmp=None, key=None, reverse=False)",
        $doc: "Stable sort *IN PLACE*.",
    },
};

/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {Sk.builtin.list} lst
 * @private
 */
var list_iter_ = Sk.abstr.buildIteratorClass("list_iterator", {
    constructor: function list_iter_(lst) {
        this.$index = 0;
        this.$seq = lst.v;
    },
    iternext: Sk.generic.iterNextWithArray,
    methods: {
        __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});

/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {Sk.builtin.list} lst
 * @private
 */
var reverselist_iter_ = Sk.abstr.buildIteratorClass("list_reverseiterator", {
    constructor: function reverselist_iter_(lst) {
        this.$index = lst.v.length - 1;
        this.$seq = lst.v;
    },
    iternext() {
        const item = this.$seq[this.$index--];
        if (item === undefined) {
            this.tp$iternext = () => undefined;
            return undefined;
        }
        return item;
    },
    methods: {
        __length_hint__: Sk.generic.iterReverseLengthHintMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});


/***/ }),

/***/ "./src/main.js":
/*!*********************!*\
  !*** ./src/main.js ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* jshint ignore:start */

// This file list is only used for testing.
// It should be kept in sync with ../skulpt.py.
// Order is important!

__webpack_require__(/*! ./util.js */ "./src/util.js");

// Global support functions
Sk.global["strftime"] = __webpack_require__(/*! strftime */ "./node_modules/strftime/strftime.js");
Sk.global["strptime"] = __webpack_require__(/*! ../support/time-helpers/strptime.js */ "./support/time-helpers/strptime.js");
__webpack_require__(/*! ../support/polyfills/JSBI */ "./support/polyfills/JSBI.js");
__webpack_require__(/*! setimmediate */ "./node_modules/setimmediate/setImmediate.js");

// Skulpt
__webpack_require__(/*! assert */ "./src/assert-dev.js");
__webpack_require__(/*! ./env.js */ "./src/env.js");
__webpack_require__(/*! ./type.js */ "./src/type.js");
__webpack_require__(/*! ./generic.js */ "./src/generic.js");
__webpack_require__(/*! ./check.js */ "./src/check.js");
__webpack_require__(/*! ./abstract.js */ "./src/abstract.js");
__webpack_require__(/*! ./object.js */ "./src/object.js");
__webpack_require__(/*! ./slotdefs.js */ "./src/slotdefs.js");
__webpack_require__(/*! ./descr.js */ "./src/descr.js");
// can only setUpMethods / setUpGetsets / setUpSlots from now.
__webpack_require__(/*! ./sk_method.js */ "./src/sk_method.js");
// can only do setUpSlots with tp$new from now since __new__ is a sk_method
[Sk.builtin.object, Sk.builtin.type].forEach((cls) => {
    Sk.abstr.setUpSlots(cls);
    Sk.abstr.setUpMethods(cls);
    Sk.abstr.setUpGetSets(cls);
});
__webpack_require__(/*! ./nonetype */ "./src/nonetype.js");
__webpack_require__(/*! ./formatting.js */ "./src/formatting.js");
__webpack_require__(/*! ./str.js */ "./src/str.js");

[Sk.builtin.str, Sk.builtin.none, Sk.builtin.NotImplemented, Sk.builtin.object].forEach((cls) => {
    const cls_proto = cls.prototype;
    cls_proto.__doc__ = cls_proto.hasOwnProperty("tp$doc") ? new Sk.builtin.str(cls_proto.tp$doc) : Sk.builtin.none.none$;
});

__webpack_require__(/*! ./function.js */ "./src/function.js");
__webpack_require__(/*! ./builtin.js */ "./src/builtin.js");
__webpack_require__(/*! ./errors.js */ "./src/errors.js");
__webpack_require__(/*! ./method.js */ "./src/method.js");
__webpack_require__(/*! ./misceval.js */ "./src/misceval.js");
__webpack_require__(/*! ./simple_iterators.js */ "./src/simple_iterators.js");
__webpack_require__(/*! ./list.js */ "./src/list.js");

__webpack_require__(/*! ./bytes.js */ "./src/bytes.js");
__webpack_require__(/*! ./tuple.js */ "./src/tuple.js");
__webpack_require__(/*! ./dict.js */ "./src/dict.js");
__webpack_require__(/*! ./mappingproxy.js */ "./src/mappingproxy.js");
__webpack_require__(/*! ./property_class_static.js */ "./src/property_class_static.js");
__webpack_require__(/*! ./int.js */ "./src/int.js");
__webpack_require__(/*! ./bool.js */ "./src/bool.js");
__webpack_require__(/*! ./float.js */ "./src/float.js");
__webpack_require__(/*! ./complex.js */ "./src/complex.js");
__webpack_require__(/*! ./slice.js */ "./src/slice.js");
__webpack_require__(/*! ./set.js */ "./src/set.js");
__webpack_require__(/*! ./print.js */ "./src/print.js");
__webpack_require__(/*! ./module.js */ "./src/module.js");
__webpack_require__(/*! ./structseq.js */ "./src/structseq.js");
__webpack_require__(/*! ./generator.js */ "./src/generator.js");
__webpack_require__(/*! ./file.js */ "./src/file.js");
__webpack_require__(/*! ./ffi.js */ "./src/ffi.js");
__webpack_require__(/*! ./range.js */ "./src/range.js");
__webpack_require__(/*! ./enumerate.js */ "./src/enumerate.js");
__webpack_require__(/*! ./filter.js */ "./src/filter.js");
__webpack_require__(/*! ./map.js */ "./src/map.js");
__webpack_require__(/*! ./reversed.js */ "./src/reversed.js");
__webpack_require__(/*! ./zip.js */ "./src/zip.js");
__webpack_require__(/*! ./token.js */ "./src/token.js");
__webpack_require__(/*! ./tokenize.js */ "./src/tokenize.js");
__webpack_require__(/*! ../gen/parse_tables.js */ "./gen/parse_tables.js");
__webpack_require__(/*! ./parser.js */ "./src/parser.js");
__webpack_require__(/*! ../gen/astnodes.js */ "./gen/astnodes.js");
__webpack_require__(/*! ./ast.js */ "./src/ast.js");
__webpack_require__(/*! ./symtable.js */ "./src/symtable.js");
__webpack_require__(/*! ./compile.js */ "./src/compile.js");
__webpack_require__(/*! ./import.js */ "./src/import.js");
__webpack_require__(/*! ./timsort.js */ "./src/timsort.js");
__webpack_require__(/*! ./super.js */ "./src/super.js");
__webpack_require__(/*! ./builtindict.js */ "./src/builtindict.js");
__webpack_require__(/*! ./constants.js */ "./src/constants.js");

/* jshint ignore:end */


/***/ }),

/***/ "./src/map.js":
/*!********************!*\
  !*** ./src/map.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Sk.builtin.func} func
 * @param {Array} array of iterators
 * @extends Sk.builtin.object
 */
Sk.builtin.map_ = Sk.abstr.buildIteratorClass("map", {
    constructor: function map_(func, iters) {
        this.$func = func;
        this.$iters = iters;
    },
    iternext(canSuspend) {
        const args = [];
        const ret = Sk.misceval.chain(
            Sk.misceval.iterArray(this.$iters, (it) =>
                Sk.misceval.chain(it.tp$iternext(canSuspend), (i) => {
                    if (i === undefined) {
                        return new Sk.misceval.Break(true);
                    }
                    args.push(i);
                })
            ),
            (endmap) => (endmap ? undefined : Sk.misceval.callsimOrSuspendArray(this.$func, args))
        );
        return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
    },
    slots: {
        tp$doc:
            "map(func, *iterables) --> map object\n\nMake an iterator that computes the function using arguments from\neach of the iterables.  Stops when the shortest iterable is exhausted.",
        tp$new(args, kwargs) {
            if (this === Sk.builtin.map_.prototype) {
                Sk.abstr.checkNoKwargs("map", kwargs);
            }
            Sk.abstr.checkArgsLen("map", args, 2);
            const func = args[0];
            const iters = [];
            for (let i = 1; i < args.length; i++) {
                iters.push(Sk.abstr.iter(args[i]));
            }
            if (this === Sk.builtin.map_.prototype) {
                return new Sk.builtin.map_(func, iters);
            } else {
                const instance = new this.constructor();
                Sk.builtin.map_.call(instance, func, iters);
                return instance;
            }
        },
    },
});

Sk.exportSymbol("Sk.builtin.map_", Sk.builtin.map_);


/***/ }),

/***/ "./src/mappingproxy.js":
/*!*****************************!*\
  !*** ./src/mappingproxy.js ***!
  \*****************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 *
 * @constructor
 *
 * @param {Object} d
 *
 * @description
 * This should be called with the prototype of a type object
 * It returns a mapping proxy
 * useful for when we do typeobject.__dict__
 * or module.__dict__ since a module $d is an object literal
 *
 * Internally this should be called with an object literal
 * from python this can be called with a dict instance (or @todo other mapping type)
 *
 * For internal object literals we create a dict object whose internal representation of
 * this.entries is created on the fly (when requested)
 *
 * We could potentially memoise the entries for static objects (builtin types @todo)
 * The problem with memoising for all type objects is that the mappingproxy
 * is a live view of the mapping rather than a static copy
 *
 * ```python
 * >>> x = A.__dict__
 * >>> A.foo = 'bar'
 * >>> x['foo']
 * 'bar'
 * ```
 *
 */
Sk.builtin.mappingproxy = Sk.abstr.buildNativeClass("mappingproxy", {
    constructor: function mappingproxy(d) {
        Sk.asserts.assert(this instanceof Sk.builtin.mappingproxy, "bad call to mapping proxy, use 'new'");
        this.mapping = new Sk.builtin.dict([]);
        if (d !== undefined) {
            // internal call so d is an object literal
            // adust this.mapping.entries to be a custom getter
            // allowing support for dynamic object literals
            customEntriesGetter(this.mapping, d);
        }
    },
    slots: {
        tp$getattr: Sk.generic.getAttr,
        tp$as_sequence_or_mapping: true,
        tp$hash: Sk.builtin.none.none$,
        tp$new(args, kwargs) {
            Sk.abstr.checkNoKwargs("mappingproxy", kwargs);
            Sk.abstr.checkOneArg("mappingproxy", args, kwargs);
            const mapping = args[0];
            if (!Sk.builtin.checkMapping(mapping)) {
                throw new Sk.builtin.TypeError("mappingproxy() argument must be a mapping, not " + Sk.abstr.typeName(mapping));
            }
            const mp = new Sk.builtin.mappingproxy();
            mp.mapping = mapping;
            return mp;
        },
        tp$richcompare(other, op) {
            return Sk.misceval.richCompareBool(this.mapping, other, op);
        },
        tp$str() {
            return this.mapping.tp$str();
        },
        $r() {
            return new Sk.builtin.str("mappingproxy(" + Sk.misceval.objectRepr(this.mapping) + ")");
        },
        mp$subscript(key, canSuspend) {
            return this.mapping.mp$subscript(key, canSuspend);
        },
        sq$contains(key) {
            return this.mapping.sq$contains(key);
        },
        sq$length() {
            return this.mapping.sq$length();
        },
        tp$iter() {
            return this.mapping.tp$iter();
        },
        tp$as_number: true,
        nb$or(other) {
            if (other instanceof Sk.builtin.mappingproxy) {
                other = other.mapping;
            }
            return Sk.abstr.numberBinOp(this.mapping, other, "BitOr");
        },
        nb$reflected_or(other) {
            if (other instanceof Sk.builtin.mappingproxy) {
                other = other.mapping;
            }
            return Sk.abstr.numberBinOp(other, this.mapping, "BitOr");
        },
        nb$inplace_or(other) {
            throw new Sk.builtin.TypeError("'|=' is not supported by " + Sk.abstr.typeName(this) + "; use '|' instead");
        },
    },
    methods: {
        get: {
            $meth(args, kwargs) {
                return Sk.misceval.callsimArray(this.mapping.tp$getattr(this.str$get), args, kwargs);
            },
            $flags: { FastCall: true },
            $textsig: null,
            $doc: "D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.",
        },
        keys: {
            $meth() {
                return Sk.misceval.callsimArray(this.mapping.tp$getattr(this.str$keys), []);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.keys() -> a set-like object providing a view on D's keys",
        },
        items: {
            $meth() {
                return Sk.misceval.callsimArray(this.mapping.tp$getattr(this.str$items), []);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.items() -> a set-like object providing a view on D's items",
        },
        values: {
            $meth() {
                return Sk.misceval.callsimArray(this.mapping.tp$getattr(this.str$values), []);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.values() -> a set-like object providing a view on D's values",
        },
        copy: {
            $meth() {
                return Sk.misceval.callsimArray(this.mapping.tp$getattr(this.str$copy), []);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "D.copy() -> a shallow copy of D",
        },
    },
    proto: {
        str$get: new Sk.builtin.str("get"),
        str$copy: new Sk.builtin.str("copy"),
        str$keys: new Sk.builtin.str("keys"),
        str$items: new Sk.builtin.str("items"),
        str$values: new Sk.builtin.str("values"),
        mp$lookup(key) {
            return this.mapping.mp$lookup(key);
        },
    },
    flags: {
        sk$acceptable_as_base_class: false,
    },
});

function customEntriesGetter(mapping, d) {
    Object.defineProperties(mapping, {
        entries: {
            get: () => {
                const entries = Object.create(null);
                Object.entries(d).forEach(([key, val]) => {
                    key = Sk.unfixReserved(key);
                    if (!key.includes("$")) {
                        key = new Sk.builtin.str(key);
                        entries[key.$savedKeyHash] = [key, val];
                    }
                });
                return entries;
            },
            configurable: true,
        },
        size: {
            get: () => {
                return Object.keys(d)
                    .map((k) => Sk.unfixReserved(k))
                    .filter((k) => !k.includes("$")).length;
            },
            configurable: true,
        },
    });
}


/***/ }),

/***/ "./src/method.js":
/*!***********************!*\
  !*** ./src/method.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 *
 * @param {Sk.builtin.func} func
 * @param {Sk.builtin.object} self
 *
 */
Sk.builtin.method = Sk.abstr.buildNativeClass("method", {
    constructor: function method(func, self) {
        Sk.asserts.assert(this instanceof Sk.builtin.method, "bad call to method constructor, use 'new'");
        this.im_func = func;
        this.im_self = self;
        this.im_call = func.tp$call;
    },
    slots: {
        $r() {
            const def_name = "?";
            let name = this.im_func.tp$getattr(Sk.builtin.str.$qualname) || this.im_func.tp$getattr(Sk.builtin.str.$name);
            name = (name && name.v) || def_name;
            return new Sk.builtin.str("<bound method " + name + " of " + Sk.misceval.objectRepr(this.im_self) + ">");
        },
        tp$hash() {
            const selfhash = Sk.abstr.objectHash(this.im_self);
            const funchash = Sk.abstr.objectHash(this.im_func);
            return selfhash + funchash;
        },
        tp$call(args, kwargs) {
            var im_call = this.im_call;
            if (im_call === undefined) {
                throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this.im_func) + "' object is not callable");
            }
            args = [this.im_self, ...args];
            return im_call.call(this.im_func, args, kwargs);
        },
        tp$new(args, kwargs) {
            Sk.abstr.checkNoKwargs("method", kwargs);
            Sk.abstr.checkArgsLen("method", args, 2, 2);
            const func = args[0];
            const self = args[1];
            if (!Sk.builtin.checkCallable(func)) {
                throw new Sk.builtin.TypeError("first argument must be callable");
            }
            if (Sk.builtin.checkNone(self)) {
                throw new Sk.builtin.TypeError("self must not be None");
            }
            return new Sk.builtin.method(func, self);
        },
        tp$richcompare(other, op) {
            if ((op != "Eq" && op != "NotEq") || !(other instanceof Sk.builtin.method)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            let eq;
            try {
                eq = Sk.misceval.richCompareBool(this.im_self, other.im_self, "Eq", false) && this.im_func == other.im_func;
            } catch (x) {
                eq = false;
            }
            if (op == "Eq") {
                return eq;
            } else {
                return !eq;
            }
        },
        tp$descr_get(obj, obtype) {
            return this;
        },
        tp$getattr(pyName, canSuspend) {
            const descr = Sk.abstr.lookupSpecial(this, pyName);
            if (descr !== undefined) {
                return descr;
            }
            return this.im_func.tp$getattr(pyName, canSuspend);
        },
    },
    getsets: {
        __func__: {
            $get() {
                return this.im_func;
            },
        },
        __self__: {
            $get() {
                return this.im_self;
            },
        },
        __doc__: {
            $get() {
                return this.im_func.tp$getattr(Sk.builtin.str.$doc);
            },
        },
    },
    flags: { sk$suitable_as_base_class: false },
});


/***/ }),

/***/ "./src/misceval.js":
/*!*************************!*\
  !*** ./src/misceval.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @namespace Sk.misceval
 * 
 * @description
 * Various function protocols that include suspension aware options
 * As well as handling some common pyObject operations to Javascript
 *
 */
Sk.misceval = {};

/** @typedef {Sk.builtin.object}*/ var pyObject;

/*
  Suspension object format:
  {resume: function() {...}, // the continuation - returns either another suspension or the return value
   data: <copied down from innermost level>,
   optional: <if true, can be resumed immediately (eg debug stops)>,
   child: <Suspension, or null if we are the innermost level>,
   $blk: <>, $loc: <>, $gbl: <>, $exc: <>, $err: <>, [$cell: <>],
  }
*/

/**
 * @description
 * Hi kids lets make a suspension...
 * 
 * @constructor
 * @param {function(?)=} resume A function to be called on resume. child is resumed first and its return value is passed to this function.
 * @param {Object=} child A child suspension. 'optional' will be copied from here if supplied.
 * @param {Object=} data Data attached to this suspension. Will be copied from child if not supplied.
 */
Sk.misceval.Suspension = function Suspension(resume, child, data) {
    this.$isSuspension = true;
    if (resume !== undefined && child !== undefined) {
        this.resume = function () {
            return resume(child.resume());
        };
    }
    this.child = child;
    this.optional = child !== undefined && child.optional;
    if (data === undefined && child !== undefined) {
        this.data = child.data;
    } else {
        this.data = data;
    }
};
Sk.exportSymbol("Sk.misceval.Suspension", Sk.misceval.Suspension);

/**
 * @description
 * Well this seems pretty obvious by the name what it should do..
 *
 * @param {Sk.misceval.Suspension} susp
 * @param {string=} message
 */
Sk.misceval.retryOptionalSuspensionOrThrow = function (susp, message) {
    while (susp instanceof Sk.misceval.Suspension) {
        if (!susp.optional) {
            throw new Sk.builtin.SuspensionError(message || "Cannot call a function that blocks or suspends here");
        }
        susp = susp.resume();
    }
    return susp;
};
Sk.exportSymbol("Sk.misceval.retryOptionalSuspensionOrThrow", Sk.misceval.retryOptionalSuspensionOrThrow);

/**
 * @description
 * Check if the given object is valid to use as an index. Only ints, or if the object has an `__index__` method.
 * 
 * @param {pyObject} o - typically an {@link Sk.builtin.int_} legacy code might use a js number
 * @returns {boolean}
 */
Sk.misceval.isIndex = function (o) {
    return o !== null && o !== undefined && (o.nb$index !== undefined || (typeof o === "number" && Number.isInteger(o)));
};
Sk.exportSymbol("Sk.misceval.isIndex", Sk.misceval.isIndex);


function asIndex(index) {
    if (index === null || index === undefined) {
        return;
    } else if (index.nb$index) {
        return index.nb$index(); // this slot will check the return value is a number / JSBI.BigInt.
    } else if (typeof index === "number" && Number.isInteger(index)) {
        return index;
    }
};

function asIndexOrThrow(index, msg) {
    const i = asIndex(index);
    if (i !== undefined) {
        return i;
    }
    msg = msg || "'{tp$name}' object cannot be interpreted as an integer";
    msg = msg.replace("{tp$name}", Sk.abstr.typeName(index));
    throw new Sk.builtin.TypeError(msg);
}

/**
 * 
 * @param {*} index 
 * 
 * @description
 * will return an integer javascript number
 * if the value is larger than Number.MAX_SAFE_INTEGER will return a BigInt
 * if the object passed is not a valid indexable object then it will return undefined
 * if you want to throw an error instead of returning undefined use {@link Sk.misceval.asIndexOrThrow}
 * If you know you want a number and not a BigInt - use {@link Sk.misceval.asIndexSized}
 */
Sk.misceval.asIndex = asIndex;


/**
 * 
 * @param {*} index 
 * @param {Sk.builtin.Exception=} Err provided an excption type if you wish to throw an exception
 * @param {string} msg an option message if the index passed is not a valid indexable object
 * 
 * @description
 * this function will always return a `Number` whose size is less than `Number.MAX_SAFE_INTEGER`
 * If you provide an err then this function will throw an error if the index is larger than `Number.MAX_SAFE_INTEGER`
 */
Sk.misceval.asIndexSized = function (index, Err, msg) {
    const i = asIndexOrThrow(index, msg);
    if (typeof i === "number") {
        return i; // integer v property will by a javascript number if it is index sized
    }
    if (Err == null) {
        return JSBI.lessThan(i, JSBI.__ZERO) ? -Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
    }
    throw new Err("cannot fit '" + Sk.abstr.typeName(index) + "' into an index-sized integer");
};

/**
 * @function
 * 
 * @param {pyObject|number} index - typically an {@link Sk.builtin.int_} legacy code might use a js number 
 * @param {string=} msg - an optional message when throwing the TypeError
 * @throws {Sk.builtin.TypeError}
 *
 * @description
 * requires a pyObject - returns a string or integer depending on the size.
 * throws a TypeError that the object cannot be interpreted as an index
 * can provide a custom message
 * include {tp$name} in the custom message which will be replaced by the typeName of the object
 * 
 * - converts the `Sk.builtin.int_` 
 * - if the number is too large to be safe returns a string
 * @returns {number|BigInt|JSBI} 
 */
Sk.misceval.asIndexOrThrow = asIndexOrThrow;

/**
 * return u[v:w]
 * @ignore
 */
Sk.misceval.applySlice = function (u, v, w, canSuspend) {
    return Sk.abstr.objectGetItem(u, new Sk.builtin.slice(v, w, null), canSuspend);
};
Sk.exportSymbol("Sk.misceval.applySlice", Sk.misceval.applySlice);

/**
 * u[v:w] = x
 * @ignore
 */
Sk.misceval.assignSlice = function (u, v, w, x, canSuspend) {
    const slice = new Sk.builtin.slice(v, w);
    if (x === null) {
        return Sk.abstr.objectDelItem(u, slice);
    } else {
        return Sk.abstr.objectSetItem(u, slice, x, canSuspend);
    }
};
Sk.exportSymbol("Sk.misceval.assignSlice", Sk.misceval.assignSlice);

/**
 * Note that this does no validation, just coercion.
 */
Sk.misceval.arrayFromArguments = function (args) {
    // If args is not a single thing return as is
    var it, i;
    var res;
    var arg;
    if (args.length != 1) {
        return args;
    }
    arg = args[0];
    if (arg instanceof Sk.builtin.set) {
        // this is a Sk.builtin.set
        arg = arg.tp$iter().$obj;
    } else if (arg instanceof Sk.builtin.dict) {
        // this is a Sk.builtin.list
        arg = Sk.builtin.dict.prototype["keys"].func_code(arg);
    }

    // shouldn't else if here as the above may output lists to arg.
    if (arg instanceof Sk.builtin.list || arg instanceof Sk.builtin.tuple) {
        return arg.v;
    } else if (Sk.builtin.checkIterable(arg)) {
        // handle arbitrary iterable (strings, generators, etc.)
        res = [];
        for (it = Sk.abstr.iter(arg), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
            res.push(i);
        }
        return res;
    }

    throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(arg) + "' object is not iterable");
};
Sk.exportSymbol("Sk.misceval.arrayFromArguments", Sk.misceval.arrayFromArguments);


/**
 * 
 * @constructor
 * 
 * @param {Function} fn
 * @param {boolean=} [handlesOwnSuspensions=false] - Does it handle its own suspension?
 * 
 * @description
 * Create a generic Python iterator that repeatedly calls a given JS function
 * until it returns 'undefined'. This function is useful for user defined Native classes
 * 
 * @example
 * // some immutable tuple like class where the v property is an array
 * MyClass.prototype.tp$iter = function() {
 *   let i = 0;
 *   const len = this.v.length;
 *   return new Sk.miscival.iterator(() => i >= len ? this.v[i++] : undefined);
 * }
 * @extends {Sk.builtin.object}
 * 
 */
Sk.misceval.iterator = Sk.abstr.buildIteratorClass("iterator", {
    constructor : function iterator (fn, handlesOwnSuspensions) {
        this.tp$iternext = handlesOwnSuspensions ? fn : function (canSuspend) {
            let x = fn();
            if (canSuspend || !x.$isSuspension) {
                return x;
            } else {
                return Sk.misceval.retryOptionalSuspensionOrThrow(x);
            }
        };
    }, 
    iternext: function (canSuspend) { /* keep slot __next__ happy */
        return this.tp$iternext(canSuspend);
    },
    flags: { sk$acceptable_as_base_class: false },
});

/**
 * for reversed comparison: Gt -> Lt, etc.
 * @ignore
 */
Sk.misceval.swappedOp_ = {
    "Eq"   : "Eq",
    "NotEq": "NotEq",
    "Lt"   : "Gt",
    "LtE"  : "GtE",
    "Gt"   : "Lt",
    "GtE"  : "LtE",
};

Sk.misceval.opSymbols = {
    Eq: "==",
    NotEq: "!=",
    Lt: "<",
    LtE: "<=",
    Gt: ">",
    GtE: ">=",
    Is: "is",
    IsNot: "is not",
    In_: "in",
    NotIn: "not in",
};

/**
 * @function
 * 
 * @param {pyObject} v
 * @param {pyObject} w
 * @param {string} op - `Eq`, `NotEq`, `Lt`, `LtE`, `Gt`, `GtE`, `Is`, `IsNot`, `In_`, `NotIn`
 * @param {boolean=} canSuspend
 * 
 * @returns {boolean}
 * 
 * @todo This implementation overrides the return value from a user defined dunder method since it returns a boolean
 * whereas Python will return the user defined return value.
 * 
 * @throws {Sk.builtin.TypeError}
 */
Sk.misceval.richCompareBool = function (v, w, op, canSuspend) {
    // v and w must be Python objects. will return Javascript true or false for internal use only
    // if you want to return a value from richCompareBool to Python you must wrap as Sk.builtin.bool first
    Sk.asserts.assert(v.sk$object && w.sk$object, "JS object passed to richCompareBool");
    var ret,
        swapped_shortcut,
        shortcut;

    const v_type = v.ob$type;
    const w_type = w.ob$type;
    const w_is_subclass = w_type !== v_type && w_type.sk$baseClass === undefined && w_type.$isSubType(v_type);

    // Python 2 has specific rules when comparing two different builtin types
    // currently, this code will execute even if the objects are not builtin types
    // but will fall through and not return anything in this section
    if (!Sk.__future__.python3 && v_type !== w_type && (op === "GtE" || op === "Gt" || op === "LtE" || op === "Lt")) {
        // note: sets are omitted here because they can only be compared to other sets
        const numeric_types = [Sk.builtin.float_, Sk.builtin.int_, Sk.builtin.lng, Sk.builtin.bool];
        const sequence_types = [
            Sk.builtin.dict,
            Sk.builtin.enumerate,
            Sk.builtin.filter_,
            Sk.builtin.list,
            Sk.builtin.map_,
            Sk.builtin.str,
            Sk.builtin.tuple,
            Sk.builtin.zip_,
        ];

        const v_num_type = numeric_types.indexOf(v_type);
        const v_seq_type = sequence_types.indexOf(v_type);
        const w_num_type = numeric_types.indexOf(w_type);
        const w_seq_type = sequence_types.indexOf(w_type);

        // NoneTypes are considered less than any other type in Python
        // note: this only handles comparing NoneType with any non-NoneType.
        // Comparing NoneType with NoneType is handled further down.
        if (v === Sk.builtin.none.none$) {
            switch (op) {
                case "Lt":
                    return true;
                case "LtE":
                    return true;
                case "Gt":
                    return false;
                case "GtE":
                    return false;
            }
        }

        if (w === Sk.builtin.none.none$) {
            switch (op) {
                case "Lt":
                    return false;
                case "LtE":
                    return false;
                case "Gt":
                    return true;
                case "GtE":
                    return true;
            }
        }

        // numeric types are always considered smaller than sequence types in Python
        if (v_num_type !== -1 && w_seq_type !== -1) {
            switch (op) {
                case "Lt":
                    return true;
                case "LtE":
                    return true;
                case "Gt":
                    return false;
                case "GtE":
                    return false;
            }
        }

        if (v_seq_type !== -1 && w_num_type !== -1) {
            switch (op) {
                case "Lt":
                    return false;
                case "LtE":
                    return false;
                case "Gt":
                    return true;
                case "GtE":
                    return true;
            }
        }

        // in Python, different sequence types are ordered alphabetically
        // by name so that dict < list < str < tuple
        if (v_seq_type !== -1 && w_seq_type !== -1) {
            switch (op) {
                case "Lt":
                    return v_seq_type < w_seq_type;
                case "LtE":
                    return v_seq_type <= w_seq_type;
                case "Gt":
                    return v_seq_type > w_seq_type;
                case "GtE":
                    return v_seq_type >= w_seq_type;
            }
        }
    }

    // handle identity and membership comparisons
    if (op === "Is") {
        if (v_type === w_type) {
            if (v === w) {
                return true;
            } else if (v_type === Sk.builtin.float_) {
                return v.v === w.v;
            } else if (v_type === Sk.builtin.int_) {
                if (typeof v.v === "number" && typeof v.v === "number") {
                    return v.v === w.v;
                }
                return JSBI.equal(JSBI.BigInt(v.v), JSBI.BigInt(w.v));
            }
        }
        return false;
    }

    if (op === "IsNot") {
        if (v_type !== w_type) {
            return true;
        } else if (v_type === Sk.builtin.float_) {
            return v.v !== w.v;
        } else if (v_type === Sk.builtin.int_) {
            if (typeof v.v === "number" && typeof v.v === "number") {
                return v.v !== w.v;
            }
            return JSBI.notEqual(JSBI.BigInt(v.v), JSBI.BigInt(w.v));
        }
        return v !== w;
    }

    if (op === "In") {
        return Sk.misceval.chain(Sk.abstr.sequenceContains(w, v, canSuspend), Sk.misceval.isTrue);
    }
    if (op === "NotIn") {
        return Sk.misceval.chain(Sk.abstr.sequenceContains(w, v, canSuspend), function (x) {
            return !Sk.misceval.isTrue(x);
        });
    }

    // Call Javascript shortcut method if exists for either object

    var op2shortcut = {
        "Eq"   : "ob$eq",
        "NotEq": "ob$ne",
        "Gt"   : "ob$gt",
        "GtE"  : "ob$ge",
        "Lt"   : "ob$lt",
        "LtE"  : "ob$le"
    };

    shortcut = op2shortcut[op];
    // similar rules apply as with binops - prioritize the reflected ops of subtypes
    if (w_is_subclass) {
        swapped_shortcut = op2shortcut[Sk.misceval.swappedOp_[op]];
        if (w[swapped_shortcut] !== v[swapped_shortcut] && (ret = w[swapped_shortcut](v)) !== Sk.builtin.NotImplemented.NotImplemented$) {
            return Sk.misceval.isTrue(ret);
        }
    }
    if ((ret = v[shortcut](w)) !== Sk.builtin.NotImplemented.NotImplemented$) {
        return Sk.misceval.isTrue(ret); 
        // techincally this is not correct along with the compile code 
        // richcompare slots could return any pyObject ToDo - would require changing compile code
    }

    if (!w_is_subclass) {
        swapped_shortcut = op2shortcut[Sk.misceval.swappedOp_[op]];
        if ((ret = w[swapped_shortcut](v)) !== Sk.builtin.NotImplemented.NotImplemented$) {
            return Sk.misceval.isTrue(ret);
        }
    }

    if (!Sk.__future__.python3) {
        const vcmp = Sk.abstr.lookupSpecial(v, Sk.builtin.str.$cmp);
        if (vcmp) {
            try {
                ret = Sk.misceval.callsimArray(vcmp, [w]);
                if (Sk.builtin.checkNumber(ret)) {
                    ret = Sk.builtin.asnum$(ret);
                    if (op === "Eq") {
                        return ret === 0;
                    } else if (op === "NotEq") {
                        return ret !== 0;
                    } else if (op === "Lt") {
                        return ret < 0;
                    } else if (op === "Gt") {
                        return ret > 0;
                    } else if (op === "LtE") {
                        return ret <= 0;
                    } else if (op === "GtE") {
                        return ret >= 0;
                    }
                }

                if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
                    throw new Sk.builtin.TypeError("comparison did not return an int");
                }
            } catch (e) {
                throw new Sk.builtin.TypeError("comparison did not return an int");
            }
        }
        const wcmp = Sk.abstr.lookupSpecial(w, Sk.builtin.str.$cmp);
        if (wcmp) {
            // note, flipped on return value and call
            try {
                ret = Sk.misceval.callsimArray(wcmp, [v]);
                if (Sk.builtin.checkNumber(ret)) {
                    ret = Sk.builtin.asnum$(ret);
                    if (op === "Eq") {
                        return ret === 0;
                    } else if (op === "NotEq") {
                        return ret !== 0;
                    } else if (op === "Lt") {
                        return ret > 0;
                    } else if (op === "Gt") {
                        return ret < 0;
                    } else if (op === "LtE") {
                        return ret >= 0;
                    } else if (op === "GtE") {
                        return ret <= 0;
                    }
                }

                if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
                    throw new Sk.builtin.TypeError("comparison did not return an int");
                }
            } catch (e) {
                throw new Sk.builtin.TypeError("comparison did not return an int");
            }
        }
        // handle special cases for comparing None with None or Bool with Bool
        if (v === Sk.builtin.none.none$ && w === Sk.builtin.none.none$) {
            // Javascript happens to return the same values when comparing null
            // with null or true/false with true/false as Python does when
            // comparing None with None or True/False with True/False

            if (op === "Eq") {
                return v.v === w.v;
            }
            if (op === "NotEq") {
                return v.v !== w.v;
            }
            if (op === "Gt") {
                return v.v > w.v;
            }
            if (op === "GtE") {
                return v.v >= w.v;
            }
            if (op === "Lt") {
                return v.v < w.v;
            }
            if (op === "LtE") {
                return v.v <= w.v;
            }
        }
    }

    // handle equality comparisons for any remaining objects
    if (op === "Eq") {
        return v === w;
    }
    if (op === "NotEq") {
        return v !== w;
    }

    const vname = Sk.abstr.typeName(v);
    const wname = Sk.abstr.typeName(w);
    throw new Sk.builtin.TypeError("'" + Sk.misceval.opSymbols[op] + "' not supported between instances of '" + vname + "' and '" + wname + "'");
    //throw new Sk.builtin.ValueError("don't know how to compare '" + vname + "' and '" + wname + "'");
};
Sk.exportSymbol("Sk.misceval.richCompareBool", Sk.misceval.richCompareBool);


/**
 * @function
 * @description
 * calls the __repr__ of a pyObject or returns `<unknown>` if a JS object was passed
 * @param {*} obj
 * @returns {string}
 * 
 */
Sk.misceval.objectRepr = function (obj) {
    Sk.asserts.assert(obj !== undefined, "trying to repr undefined");
    if (obj !== null && obj.$r) {
        return obj.$r().v;
    } else {
        try {
            // str goes through the common javascript cases or throws a TypeError;
            return new Sk.builtin.str(obj).v;
        } catch (e) {
            if (e instanceof Sk.builtin.TypeError) {
                return "<unknown>";
            } else {
                throw e;
            }
        }
    }
};
Sk.exportSymbol("Sk.misceval.objectRepr", Sk.misceval.objectRepr);

Sk.misceval.opAllowsEquality = function (op) {
    switch (op) {
        case "LtE":
        case "Eq":
        case "GtE":
            return true;
    }
    return false;
};
Sk.exportSymbol("Sk.misceval.opAllowsEquality", Sk.misceval.opAllowsEquality);


/**
 * @function
 * @description
 * Decides whether a pyObject is True or not
 * @returns {boolean}
 * @param {*} x 
 */
Sk.misceval.isTrue = function (x) {
    if (x === true || x === Sk.builtin.bool.true$) {
        return true;
    }
    if (x === false || x === Sk.builtin.bool.false$) {
        return false;
    }
    if (x === null || x === undefined) {
        return false;
    }
    if (x.nb$bool) {
        return x.nb$bool(); // the slot wrapper takes care of converting to js Boolean
    }
    if (x.sq$length) {
        // the slot wrapper takes care of the error message and converting to js int
        return x.sq$length() !== 0;
    }
    return Boolean(x);
};
Sk.exportSymbol("Sk.misceval.isTrue", Sk.misceval.isTrue);

Sk.misceval.softspace_ = false;
Sk.misceval.print_ = function (x) {
    var s;

    function isspace(c) {
        return c === "\n" || c === "\t" || c === "\r";
    }

    if (Sk.misceval.softspace_) {
        if (x !== "\n") {
            Sk.output(" ");
        }
        Sk.misceval.softspace_ = false;
    }

    s = new Sk.builtin.str(x);

    return Sk.misceval.chain(
        Sk.importModule("sys", false, true),
        function (sys) {
            return Sk.misceval.apply(sys["$d"]["stdout"]["write"], undefined, undefined, undefined, [sys["$d"]["stdout"], s]);
        },
        function () {
            if (s.v.length === 0 || !isspace(s.v[s.v.length - 1]) || s.v[s.v.length - 1] === " ") {
                Sk.misceval.softspace_ = true;
            }
        }
    );
};
Sk.exportSymbol("Sk.misceval.print_", Sk.misceval.print_);

/**
 * @function
 * @description 
 * Get a python object from a given namespace
 * @param {string} name
 * @param {Object=} other generally globals
 * @example
 * Sk.misceval.loadname("foo", Sk.globals);
 */
Sk.misceval.loadname = function (name, other) {
    var bi;
    var v = other[name];
    if (v !== undefined) {
        return v;
    }

    bi = Sk.builtins[name];
    if (bi !== undefined) {
        return bi;
    }

    throw new Sk.builtin.NameError("name '" + Sk.unfixReserved(name) + "' is not defined");
};
Sk.exportSymbol("Sk.misceval.loadname", Sk.misceval.loadname);

/**
 *
 * Notes on necessity for 'call()':
 *
 * Classes are callable in python to create an instance of the class. If
 * we're calling "C()" we cannot tell at the call site whether we're
 * calling a standard function, or instantiating a class.
 *
 * JS does not support user-level callables. So, we can't use the normal
 * prototype hierarchy to make the class inherit from a 'class' type
 * where the various tp$getattr, etc. methods would live.
 *
 * Instead, we must copy all the methods from the prototype of our class
 * type onto every instance of the class constructor function object.
 * That way, both "C()" and "C.tp$getattr(...)" can still work. This is
 * of course quite expensive.
 *
 * The alternative would be to indirect all calls (whether classes or
 * regular functions) through something like C.$call(...). In the case
 * of class construction, $call could then call the constructor after
 * munging arguments to pass them on. This would impose a penalty on
 * regular function calls unfortunately, as they would have to do the
 * same thing.
 *
 * Note that the same problem exists for function objects too (a "def"
 * creates a function object that also has properties). It just happens
 * that attributes on classes in python are much more useful and common
 * that the attributes on functions.
 *
 * Also note, that for full python compatibility we have to do the $call
 * method because any python object could have a __call__ method which
 * makes the python object callable too. So, unless we were to make
 * *all* objects simply (function(){...}) and use the dict to create
 * hierarchy, there would be no way to call that python user function. I
 * think I'm prepared to sacrifice __call__ support, or only support it
 * post-ECMA5 or something.
 *
 * Is using (function(){...}) as the only object type too crazy?
 * Probably. Better or worse than having two levels of function
 * invocation for every function call?
 *
 * For a class `C' with instance `inst' we have the following cases:
 *
 * 1. C.attr
 *
 * 2. C.staticmeth()
 *
 * 3. x = C.staticmeth; x()
 *
 * 4. inst = C()
 *
 * 5. inst.attr
 *
 * 6. inst.meth()
 *
 * 7. x = inst.meth; x()
 *
 * 8. inst(), where C defines a __call__
 *
 * Because in general these are accomplished by a helper function
 * (tp$getattr/setattr/slice/ass_slice/etc.) it seems appropriate to add
 * a call that generally just calls through, but sometimes handles the
 * unusual cases. Once ECMA-5 is more broadly supported we can revisit
 * and hopefully optimize.
 *
 * @param {Object} func the thing to call
 * @param {Object=} kwdict **kwargs
 * @param {Object=} varargseq **args
 * @param {Object=} kws keyword args or undef
 * @param {...*} args stuff to pass it
 *
 *
 * @todo I think all the above is out of date.
 * @ignore
 */
Sk.misceval.call = function (func, kwdict, varargseq, kws, args) {
    args = Array.prototype.slice.call(arguments, 4);
    // todo; possibly inline apply to avoid extra stack frame creation
    return Sk.misceval.apply(func, kwdict, varargseq, kws, args);
};
Sk.exportSymbol("Sk.misceval.call", Sk.misceval.call);

/**
 * @param {?Object} suspensionHandlers
 * @param {Object} func the thing to call
 * @param {Object=} kwdict **kwargs
 * @param {Object=} varargseq **args
 * @param {Object=} kws keyword args or undef
 * @param {...*} args stuff to pass it
 *
 *
 * @todo I think all the above is out of date.
 */

Sk.misceval.callAsync = function (suspensionHandlers, func, kwdict, varargseq, kws, args) {
    args = Array.prototype.slice.call(arguments, 5);
    // todo; possibly inline apply to avoid extra stack frame creation
    return Sk.misceval.applyAsync(suspensionHandlers, func, kwdict, varargseq, kws, args);
};
Sk.exportSymbol("Sk.misceval.callAsync", Sk.misceval.callAsync);

Sk.misceval.callOrSuspend = function (func, kwdict, varargseq, kws, args) {
    args = Array.prototype.slice.call(arguments, 4);
    // todo; possibly inline apply to avoid extra stack frame creation
    return Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args);
};
Sk.exportSymbol("Sk.misceval.callOrSuspend", Sk.misceval.callOrSuspend);

/**
 * @param {Object} func the thing to call
 * @param {...*} args stuff to pass it
 * @ignore
 */
Sk.misceval.callsim = function (func, args) {
    args = Array.prototype.slice.call(arguments, 1);
    return Sk.misceval.apply(func, undefined, undefined, undefined, args);
};
Sk.exportSymbol("Sk.misceval.callsim", Sk.misceval.callsim);

/**
 * @param {Object=} func the thing to call
 * @param {Array=} args an array of arguments to pass to the func
 * @param {Array=} kws an array of string/pyObject pairs to pass to the func as kwargs
 * 
 * @description
 * Call a pyObject - if the object is not callable will throw a TypeError
 * Requires args to be a Javascript array.
 * kws should be an array of string/pyObject pairs as key/values
 */
Sk.misceval.callsimArray = function (func, args, kws) {
    args = args || [];
    return Sk.misceval.retryOptionalSuspensionOrThrow(Sk.misceval.callsimOrSuspendArray(func, args, kws));
};
Sk.exportSymbol("Sk.misceval.callsimArray", Sk.misceval.callsimArray);

/**
 * @param {?Object} suspensionHandlers any custom suspension handlers
 * @param {Object} func the thing to call
 * @param {...*} args stuff to pass it
 */
Sk.misceval.callsimAsync = function (suspensionHandlers, func, args) {
    args = Array.prototype.slice.call(arguments, 2);
    return Sk.misceval.applyAsync(suspensionHandlers, func, undefined, undefined, undefined, args);
};
Sk.exportSymbol("Sk.misceval.callsimAsync", Sk.misceval.callsimAsync);

/**
 * @param {Object} func the thing to call
 * @param {...*} args stuff to pass it
 * @deprecated
 * @ignore
 */
Sk.misceval.callsimOrSuspend = function (func, args) {
    args = Array.prototype.slice.call(arguments, 1);
    return Sk.misceval.applyOrSuspend(func, undefined, undefined, undefined, args);
};
Sk.exportSymbol("Sk.misceval.callsimOrSuspend", Sk.misceval.callsimOrSuspend);

/**
 * @description
 * Does the same thing as callsimOrSuspend without expensive call to
 * Array.slice.  Requires args+kws to be Javascript arrays. 
 * The preferred method for calling a pyObject. 
 * 
 * @param {Object=} func the thing to call
 * @param {Array=} args an array of arguments to pass to the func
 * @param {Array=} kws an array of keyword arguments to pass to the func
 *
 */
Sk.misceval.callsimOrSuspendArray = function (func, args, kws) {
    args = args || [];
    if (func !== undefined && func.tp$call) {
        return func.tp$call(args, kws);
    } else {
        // Slow path handles things like calling native JS fns
        // (perhaps we should stop supporting that), and weird
        // detection of the __call__ method (everything should use tp$call)
        return Sk.misceval.applyOrSuspend(func, undefined, undefined, kws, args);
    }
};
Sk.exportSymbol("Sk.misceval.callsimOrSuspendArray", Sk.misceval.callsimOrSuspendArray);

/**
 * Wrap Sk.misceval.applyOrSuspend, but throw an error if we suspend
 * @ignore
 */
Sk.misceval.apply = function (func, kwdict, varargseq, kws, args) {
    var r = Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args);
    if (r instanceof Sk.misceval.Suspension) {
        return Sk.misceval.retryOptionalSuspensionOrThrow(r);
    } else {
        return r;
    }
};
Sk.exportSymbol("Sk.misceval.apply", Sk.misceval.apply);

/**
 * Wraps anything that can return an Sk.misceval.Suspension, and returns a
 * JS Promise with the result. Also takes an object map of suspension handlers:
 * pass in {"suspType": function (susp) {} }, and your function will be called
 * with the Suspension object if susp.type=="suspType". The type "*" will match
 * all otherwise unhandled suspensions.
 *
 * A suspension handler should return a Promise yielding the return value of
 * r.resume() - ie, either the final return value of this call or another
 * Suspension. That is, the null suspension handler is:
 *
 *     function handler(susp) {
 *       return new Promise(function(resolve, reject) {
 *         try {
 *           resolve(susp.resume());
 *         } catch(e) {
 *           reject(e);
 *         }
 *       });
 *     }
 *
 * Alternatively, a handler can return null to perform the default action for
 * that suspension type.
 *
 * (Note: do *not* call asyncToPromise() in a suspension handler; this will
 * create a new Promise object for each such suspension that occurs)
 *
 * asyncToPromise() returns a Promise that will be resolved with the final
 * return value, or rejected with an exception if one is thrown.
 *
 * @param{function()} suspendablefn returns either a result or a Suspension
 * @param{Object=} suspHandlers an object map of suspension handlers
 */
Sk.misceval.asyncToPromise = function (suspendablefn, suspHandlers) {
    return new Promise(function (resolve, reject) {
        try {
            var r = suspendablefn();

            (function handleResponse(r) {
                try {
                    // jsh*nt insists these be defined outside the loop
                    var resume = function () {
                        try {
                            handleResponse(r.resume());
                        } catch (e) {
                            reject(e);
                        }
                    };
                    var resumeWithData = function resolved(x) {
                        try {
                            r.data["result"] = x;
                            resume();
                        } catch (e) {
                            reject(e);
                        }
                    };
                    var resumeWithError = function rejected(e) {
                        try {
                            r.data["error"] = e;
                            resume();
                        } catch (ex) {
                            reject(ex);
                        }
                    };

                    while (r instanceof Sk.misceval.Suspension) {
                        var handler = suspHandlers && (suspHandlers[r.data["type"]] || suspHandlers["*"]);

                        if (handler) {
                            var handlerPromise = handler(r);
                            if (handlerPromise) {
                                handlerPromise.then(handleResponse, reject);
                                return;
                            }
                        }

                        if (r.data["type"] == "Sk.promise") {
                            r.data["promise"].then(resumeWithData, resumeWithError);
                            return;
                        } else if (r.data["type"] == "Sk.yield") {
                            // Assumes all yields are optional, as Sk.setTimeout might
                            // not be able to yield.
                            //Sk.setTimeout(resume, 0);
                            Sk.global["setImmediate"](resume);
                            return;
                        } else if (r.data["type"] == "Sk.delay") {
                            //Sk.setTimeout(resume, 1);
                            Sk.global["setImmediate"](resume);
                            return;
                        } else if (r.optional) {
                            // Unhandled optional suspensions just get
                            // resumed immediately, and we go around the loop again.
                            r = r.resume();
                        } else {
                            // Unhandled, non-optional suspension.
                            throw new Sk.builtin.SuspensionError("Unhandled non-optional suspension of type '" + r.data["type"] + "'");
                        }
                    }

                    resolve(r);
                } catch (e) {
                    reject(e);
                }
            })(r);
        } catch (e) {
            reject(e);
        }
    });
};
Sk.exportSymbol("Sk.misceval.asyncToPromise", Sk.misceval.asyncToPromise);

Sk.misceval.applyAsync = function (suspHandlers, func, kwdict, varargseq, kws, args) {
    return Sk.misceval.asyncToPromise(function () {
        return Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args);
    }, suspHandlers);
};
Sk.exportSymbol("Sk.misceval.applyAsync", Sk.misceval.applyAsync);

/**
 * Chain together a set of functions, each of which might return a value or
 * an Sk.misceval.Suspension. Each function is called with the return value of
 * the preceding function, but does not see any suspensions. If a function suspends,
 * Sk.misceval.chain() returns a suspension that will resume the chain once an actual
 * return value is available.
 *
 * The idea is to allow a Promise-like chaining of possibly-suspending steps without
 * repeating boilerplate suspend-and-resume code.
 *
 * For example, imagine we call Sk.misceval.chain(x, f).
 *  - If x is a value, we return f(x).
 *  - If x is a suspension, we suspend. We will suspend and resume until we get a
 *    return value, and then we will return f(<resumed-value).
 * This can be expanded to an arbitrary number of functions
 * (eg Sk.misceval.chain(x, f, g), which is equivalent to chain(chain(x, f), g).)
 * @template T
 * @param {T}              initialValue
 * @param {...function(T)} chainedFns
 */

Sk.misceval.chain = function (initialValue, chainedFns) {
    // We try to minimse overhead when nothing suspends (the common case)
    var i = 1,
        value = initialValue,
        j,
        fs;

    while (true) {
        if (i == arguments.length) {
            return value;
        }
        if (value && value.$isSuspension) {
            break;
        } // oops, slow case
        value = arguments[i](value);
        i++;
    }

    // Okay, we've suspended at least once, so we're taking the slow(er) path.

    // Copy our remaining arguments into an array (inline, because passing
    // "arguments" out of a function kills the V8 optimiser).
    // (discussion: https://github.com/skulpt/skulpt/pull/552)
    fs = new Array(arguments.length - i);

    for (j = 0; j < arguments.length - i; j++) {
        fs[j] = arguments[i + j];
    }

    j = 0;

    return (function nextStep(r) {
        while (j < fs.length) {
            if (r instanceof Sk.misceval.Suspension) {
                return new Sk.misceval.Suspension(nextStep, r);
            }

            r = fs[j](r);
            j++;
        }

        return r;
    })(value);
};
Sk.exportSymbol("Sk.misceval.chain", Sk.misceval.chain);

/**
 * Catch any exceptions thrown by a function, or by resuming any suspension it
 * returns.
 *
 *     var result = Sk.misceval.tryCatch(asyncFunc, function(err) {
 *       console.log(err);
 *     });
 *
 * Because exceptions are returned asynchronously aswell you can't catch them
 * with a try/catch. That's what this function is for.
 */
Sk.misceval.tryCatch = function (tryFn, catchFn) {
    var r;

    try {
        r = tryFn();
    } catch (e) {
        return catchFn(e);
    }

    if (r instanceof Sk.misceval.Suspension) {
        var susp = new Sk.misceval.Suspension(undefined, r);
        susp.resume = function () {
            return Sk.misceval.tryCatch(r.resume, catchFn);
        };
        return susp;
    } else {
        return r;
    }
};
Sk.exportSymbol("Sk.misceval.tryCatch", Sk.misceval.tryCatch);

/**
 * @function
 * @description
 * Perform a suspension-aware for-each on an iterator, without
 * blowing up the stack.
 * forFn() is called for each element in the iterator, with two
 * arguments: the current element and the previous return value
 * of forFn() (or initialValue on the first call). In this way,
 * iterFor() can be used as a simple for loop, or alternatively
 * as a 'reduce' operation. The return value of the final call to
 * forFn() will be the return value of iterFor() (after all
 * suspensions are resumed, that is; if the iterator is empty then
 * initialValue will be returned.)
 *
 * The iteration can be terminated early, by returning
 * an instance of Sk.misceval.Break. If an argument is given to
 * the Sk.misceval.Break() constructor, that value will be
 * returned from iterFor(). It is therefore possible to use
 * iterFor() on infinite iterators.
 *
 * @param {*} iter
 * @param {function(pyObject,*=)} forFn
 * @param {*=} initialValue
 */
Sk.misceval.iterFor = function (iter, forFn, initialValue) {
    var prevValue = initialValue;

    var breakOrIterNext = function (r) {
        prevValue = r;
        return r instanceof Sk.misceval.Break ? r : iter.tp$iternext(true);
    };

    return (function nextStep(i) {
        while (i !== undefined) {
            if (i instanceof Sk.misceval.Suspension) {
                return new Sk.misceval.Suspension(nextStep, i);
            }

            if (i === Sk.misceval.Break || i instanceof Sk.misceval.Break) {
                return i.brValue;
            }

            i = Sk.misceval.chain(forFn(i, prevValue), breakOrIterNext);
        }
        return prevValue;
    })(iter.tp$iternext(true));
};
Sk.exportSymbol("Sk.misceval.iterFor", Sk.misceval.iterFor);


/**
 * @function
 * @description
 * 
 * As per iterFor but with an array rather than a python iterable
 * Useful for iterating over args where doing so could result in a suspension
 *
 * @param {Array} args
 * @param {function(pyObject,*=)} forFn
 * @param {*=} initialValue
 */
Sk.misceval.iterArray = function (args, forFn, initialValue) {
    Sk.asserts.assert(Array.isArray(args), "iterArgs requires an array");
    let i = 0;
    return Sk.misceval.iterFor({tp$iternext: () => args[i++]}, forFn, initialValue);
};

/**
 * @function
 *
 * @description
 * Convert a Python iterable into a javascript array
 *
 * @param {pyObject} iterable
 * @param {boolean=} canSuspend - Can this function suspend
 *
 * @returns {!Array}
 */
Sk.misceval.arrayFromIterable = function (iterable, canSuspend) {
    if (iterable === undefined) {
        return [];
    }
    if (iterable.hp$type === undefined && iterable.sk$asarray !== undefined) {
        // use sk$asarray only if we're a builtin
        return iterable.sk$asarray();
    }
    const L = [];
    const ret = Sk.misceval.chain(
        Sk.misceval.iterFor(Sk.abstr.iter(iterable), (i) => {
            L.push(i);
        }),
        () => L
    );
    return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
};

/**
 * A special value to return from an iterFor() function,
 * to abort the iteration. Optionally supply a value for iterFor() to return
 * (defaults to 'undefined')
 *
 * @constructor
 * @param {*=}  brValue
 */
Sk.misceval.Break = function (brValue) {
    if (!(this instanceof Sk.misceval.Break)) {
        return new Sk.misceval.Break(brValue);
    }

    this.brValue = brValue;
};
Sk.exportSymbol("Sk.misceval.Break", Sk.misceval.Break);

/**
 * same as Sk.misceval.call except args is an actual array, rather than
 * varargs.
 * @deprecated
 * @ignore
 */
Sk.misceval.applyOrSuspend = function (func, kwdict, varargseq, kws, args) {
    var fcall;
    var it, i;

    if (func == null || func === Sk.builtin.none.none$) {
        throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(func) + "' object is not callable");
    }

    if (typeof func === "function" && func.tp$call === undefined) {
        func = new Sk.builtin.func(func);
    }

    fcall = func.tp$call;
    if (fcall !== undefined) {
        if (varargseq) {
            for (it = varargseq.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
                args.push(i);
            }
        }

        if (kwdict) {
            for (it = Sk.abstr.iter(kwdict), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
                if (!Sk.builtin.checkString(i)) {
                    throw new Sk.builtin.TypeError("Function keywords must be strings");
                }
                kws.push(i.v);
                kws.push(Sk.abstr.objectGetItem(kwdict, i, false));
            }
        }
        return fcall.call(func, args, kws, kwdict);
    }

    // todo; can we push this into a tp$call somewhere so there's
    // not redundant checks everywhere for all of these __x__ ones?
    fcall = func.__call__;
    if (fcall !== undefined) {
        // func is actually the object here because we got __call__
        // from it. todo; should probably use descr_get here
        args.unshift(func);
        return Sk.misceval.apply(fcall, kwdict, varargseq, kws, args);
    }

    throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(func) + "' object is not callable");
};
Sk.exportSymbol("Sk.misceval.applyOrSuspend", Sk.misceval.applyOrSuspend);

/**
 * Do the boilerplate suspension stuff.
 */
Sk.misceval.promiseToSuspension = function (promise) {
    var suspension = new Sk.misceval.Suspension();

    suspension.resume = function () {
        if (suspension.data["error"]) {
            throw suspension.data["error"];
        }

        return suspension.data["result"];
    };

    suspension.data = {
        type: "Sk.promise",
        promise: promise,
    };

    return suspension;
};
Sk.exportSymbol("Sk.misceval.promiseToSuspension", Sk.misceval.promiseToSuspension);

/**
 * @function
 * @description
 * Constructs a class object given a code object representing the body
 * of the class, the name of the class, and the list of bases.
 *
 * There are no "old-style" classes in Skulpt, so use the user-specified
 * metaclass (todo;) if there is one, the type of the 0th base class if
 * there's bases, or otherwise the 'type' type.
 *
 * The func code object is passed a (js) dict for its locals which it
 * stores everything into.
 *
 * The metaclass is then called as metaclass(name, bases, locals) and
 * should return a newly constructed class object.
 *
 */
Sk.misceval.buildClass = function (globals, func, name, bases, cell) {
    // todo; metaclass
    var klass;
    var meta = Sk.builtin.type;

    var l_cell = cell === undefined ? {} : cell;
    var locals = {};

    // init the dict for the class
    func(globals, locals, l_cell);
    // ToDo: check if func contains the __meta__ attribute
    // or if the bases contain __meta__
    // new Syntax would be different

    // file's __name__ is class's __module__
    if (globals["__name__"]) {
        // some js modules haven't set their module name and we shouldn't set a dictionary value to be undefined that should be equivalent to deleting a value;
        locals.__module__ = globals["__name__"];
    }
    var _name = new Sk.builtin.str(name);
    var _bases = new Sk.builtin.tuple(bases);
    var _locals = [];
    var key;

    // build array for python dict
    for (key in locals) {
        if (!locals.hasOwnProperty(key)) {
            //The current property key not a direct property of p
            continue;
        }
        _locals.push(new Sk.builtin.str(key)); // push key
        _locals.push(locals[key]); // push associated value
    }
    _locals = new Sk.builtin.dict(_locals);

    klass = Sk.misceval.callsimArray(meta, [_name, _bases, _locals]);

    return klass;
};
Sk.exportSymbol("Sk.misceval.buildClass", Sk.misceval.buildClass);


/***/ }),

/***/ "./src/module.js":
/*!***********************!*\
  !*** ./src/module.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @extends {Sk.builtin.object}
 */
Sk.builtin.module = Sk.abstr.buildNativeClass("module", {
    constructor: function module_() {
        this.$d = {}; // set this now - we could subclass from Module so override sk$klass $d object
    },
    slots: {
        tp$doc: "Create a module object.\n\nThe name must be a string; the optional doc argument can have any type.",
        tp$getattr(pyName, canSuspend) {
            const jsMangled = pyName.$mangled;
            const ret = this.$d[jsMangled];
            if (ret !== undefined) {
                return ret;
            }
            // technically this is the wrong way round but its seems performance wise better
            // to just return the module elements before checking for descriptors
            const descr = this.ob$type.$typeLookup(pyName);
            if (descr !== undefined) {
                const f = descr.tp$descr_get;
                if (f) {
                    return f.call(descr, this, this.ob$type, canSuspend);
                }
                return descr;
            }
            // ok we've failed to find anything check if there is __getattr__ defined as per pep 562
            const getattr = this.$d.__getattr__;
            if (getattr !== undefined) {
                const res  = Sk.misceval.tryCatch(
                    () => Sk.misceval.callsimOrSuspendArray(getattr, [pyName]),
                    (e) => {
                        if (e instanceof Sk.builtin.AttributeError) {
                            return;
                        }
                        throw e;
                    }
                );
                return canSuspend ? res : Sk.misceval.retryOptionalSuspensionOrThrow(res);
            }
        },
        tp$setattr: Sk.generic.setAttr,
        tp$new: Sk.generic.new,
        tp$init(args, kwargs) {
            const [name, doc] = Sk.abstr.copyKeywordsToNamedArgs("module", ["name", "doc"], args, kwargs, [Sk.builtin.none.none$]);
            Sk.builtin.pyCheckType("module", "string", name);
            this.init$dict(name, doc);
        },
        $r() {
            let name = this.get$name();
            if (name !== undefined) {
                const module_reprf = this.get$mod_reprf();
                if (module_reprf !== undefined) {
                    return Sk.misceval.callsimOrSuspendArray(module_reprf, [this]);
                }
            }
            name = name === undefined ? "'?'" : name;
            let extra = this.from$file();
            extra = extra === undefined ? this.empty_or$loader() : extra;
            return new Sk.builtin.str("<module " + name + extra + ">");
        },
    },
    getsets: {
        __dict__: {
            $get() {
                // modules in skulpt have a $d as a js object so just return it as a mapping proxy;
                // TODO we should really have a dict object
                return new Sk.builtin.mappingproxy(this.$d);
            },
        },
    },
    methods: {
        __dir__: {
            $meth() {
                // could be cleaner but this is inline with cpython's version
                const dict = this.tp$getattr(Sk.builtin.str.$dict);
                if (!Sk.builtin.checkMapping(dict)) {
                    throw new Sk.builtin.TypeError("__dict__ is not a dictionary");
                }
                const dirfunc = dict.mp$lookup(Sk.builtin.str.$dir);
                if (dirfunc !== undefined) {
                    return Sk.misceval.callsimOrSuspendArray(dirfunc, []);
                } else {
                    return new Sk.builtin.list(Sk.misceval.arrayFromIterable(dict));
                }
            },
            $flags: { NoArgs: true },
            $doc: "__dir__() -> list\nspecialized dir() implementation",
        },
    },
    proto: {
        init$dict(name, doc) {
            this.$d.__name__ = name;
            this.$d.__doc__ = doc;
            this.$d.__package__ = Sk.builtin.none.none$;
            this.$d.__spec__ = Sk.builtin.none.none$;
            this.$d.__loader__ = Sk.builtin.none.none$;
        },
        sk$attrError() {
            const name = this.get$name();
            return name === undefined ? "module" : "module " + name;
        },
        get$name() {
            const name = this.tp$getattr(Sk.builtin.str.$name);
            return name && Sk.misceval.objectRepr(name);
        },
        from$file() {
            const file = this.tp$getattr(Sk.builtin.str.$file);
            return file && " from " + Sk.misceval.objectRepr(file);
        },
        empty_or$loader() {
            if (this.$js && this.$js.includes("$builtinmodule")) {
                return " (built-in)";
            }
            const loader = this.tp$getattr(Sk.builtin.str.$loader);
            return loader === undefined || Sk.builtin.checkNone(loader) ? "" : " (" + Sk.misceval.objectRepr(loader) + ")";
        },
        get$mod_reprf() {
            const loader = this.tp$getattr(Sk.builtin.str.$loader);
            return loader && loader.tp$getattr(this.str$mod_repr);
        },
        str$mod_repr: new Sk.builtin.str("module_repr"),
    },
});

Sk.exportSymbol("Sk.builtin.module", Sk.builtin.module);


/***/ }),

/***/ "./src/nonetype.js":
/*!*************************!*\
  !*** ./src/nonetype.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * 
 * @extends {Sk.builtin.object}
 *
 * @description
 * It would be rare to call this as a constructor since it returns {@link Sk.builtin.none.none$}
 */
Sk.builtin.none = Sk.abstr.buildNativeClass("NoneType", {
    constructor: function NoneType() {
        return Sk.builtin.none.none$; // always return the same object
    },
    slots: /**@lends {Sk.builtin.none.prototype}*/ {
        tp$new(args, kwargs) {
            Sk.abstr.checkNoArgs("NoneType", args, kwargs);
            return Sk.builtin.none.none$;
        },
        $r() {
            return new Sk.builtin.str("None");
        },
        tp$as_number: true,
        nb$bool() {
            return false;
        },
    },
    flags: {
        sk$acceptable_as_base_class: false,
    },
});

/**
 * Python None value.
 * @type {Sk.builtin.none}
 * @member {Sk.builtin.none}
 */
Sk.builtin.none.none$ = /** @type {Sk.builtin.none} */ (Object.create(Sk.builtin.none.prototype, {
    v: { value: null, enumerable: true },
}));

/**
 * @constructor
 * Sk.builtin.NotImplemented
 *
 * @extends {Sk.builtin.object}
 */
Sk.builtin.NotImplemented = Sk.abstr.buildNativeClass("NotImplementedType", {
    constructor: function NotImplementedType() {
        return Sk.builtin.NotImplemented.NotImplemented$; // always return the same object
    },
    slots: /**@lends {Sk.builtin.NotImplemented.prototype}*/ {
        $r() {
            return new Sk.builtin.str("NotImplemented");
        },
        tp$new (args, kwargs) {
            Sk.abstr.checkNoArgs("NotImplementedType", args, kwargs);
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    flags: {
        sk$acceptable_as_base_class: false,
    }
});

/**
 * Python NotImplemented constant.
 * @type {Sk.builtin.NotImplemented}
 * @member {Sk.builtin.NotImplemented}
 */
Sk.builtin.NotImplemented.NotImplemented$ = /** @type {Sk.builtin.NotImplemented} */ (Object.create(Sk.builtin.NotImplemented.prototype, {
    v: { value: null, enumerable: true },
}));

/***/ }),

/***/ "./src/object.js":
/*!***********************!*\
  !*** ./src/object.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports) {

const hashMap = new Map();
/**
 *
 * @constructor
 *
 * @description
 * Constructor for Python object. All Python classes (builtin and user-defined)
 * should inherit from this class.
 *
 */
Sk.builtin.object = Sk.abstr.buildNativeClass("object", {
    constructor: function object() {
        Sk.asserts.assert(this instanceof Sk.builtin.object, "bad call to object, use 'new'");
    },
    base: null,
    slots: {
        tp$new(args, kwargs) {
            // see cypthon object_new for algorithm details we do two versions one for prototypical and one for not
            if (args.length || (kwargs && kwargs.length)) {
                if (this.tp$new !== Sk.builtin.object.prototype.tp$new) {
                    throw new Sk.builtin.TypeError("object.__new__() takes exactly one argument (the type to instantiate)");
                }
                if (this.tp$init === Sk.builtin.object.prototype.tp$init) {
                    throw new Sk.builtin.TypeError(Sk.abstr.typeName(this) + "() takes no arguments");
                }
            }
            return new this.constructor();
        },
        tp$init(args, kwargs) {
            // see cypthon object_init for algorithm details
            if (args.length || (kwargs && kwargs.length)) {
                if (this.tp$init !== Sk.builtin.object.prototype.tp$init) {
                    throw new Sk.builtin.TypeError("object.__init__() takes exactly one argument (the instance to initialize)");
                }
                if (this.tp$new === Sk.builtin.object.prototype.tp$new) {
                    throw new Sk.builtin.TypeError(Sk.abstr.typeName(this) + ".__init__() takes exactly one argument (the instance to initialize)");
                }
            }
        },
        tp$getattr: Sk.generic.getAttr,
        tp$setattr: Sk.generic.setAttr,
        $r() {
            const mod = Sk.abstr.lookupSpecial(this, Sk.builtin.str.$module);
            let cname = "";
            if (mod && Sk.builtin.checkString(mod)) {
                cname = mod.v + ".";
            }
            return new Sk.builtin.str("<" + cname + Sk.abstr.typeName(this) + " object>");
        },
        tp$str() {
            // if we're calling this function then the object has no __str__ or tp$str defined
            return this.$r();
        },
        tp$hash() {
            let hash = hashMap.get(this);
            if (hash !== undefined) {
                return hash;
            }
            hash = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER - Number.MAX_SAFE_INTEGER / 2);
            hashMap.set(this, hash);
            return hash;
        },
        tp$richcompare(other, op) {
            let res;
            switch (op) {
                case "Eq":
                    res = this === other || Sk.builtin.NotImplemented.NotImplemented$;
                    break;
                case "NotEq":
                    // use tp$richcompare here... because CPython does. ob$eq breaks some tests for NotEq subclasses
                    res = this.tp$richcompare(other, "Eq");
                    if (res !== Sk.builtin.NotImplemented.NotImplemented$) {
                        res = !Sk.misceval.isTrue(res);
                    }
                    break;
                default:
                    res = Sk.builtin.NotImplemented.NotImplemented$;
            }
            return res;
        },
        tp$doc: "The most base type",
    },
    getsets: {
        __class__: {
            $get() {
                return this.ob$type;
            },
            $set(value) {
                if (value === undefined) {
                    throw new Sk.builtin.TypeError("can't delete __class__ attribute");
                } else if (!Sk.builtin.checkClass(value)) {
                    throw new Sk.builtin.TypeError("__class__ must be set to a class, not '" + Sk.abstr.typeName(value) + "' object");
                }
                const oldto = this.ob$type;
                const newto = value;

                if (
                    !(oldto.$isSubType(Sk.builtin.module) && newto.$isSubType(Sk.builtin.module)) &&
                    (oldto.sk$klass === undefined || newto.sk$klass === undefined)
                ) {
                    throw new Sk.builtin.TypeError(" __class__ assignment only supported for heap types or ModuleType subclasses");
                } else if (value.prototype.sk$builtinBase !== this.sk$builtinBase) {
                    throw new Sk.builtin.TypeError(
                        "__class__ assignment: '" + Sk.abstr.typeName(this) + "' object layout differs from '" + value.prototype.tp$name + "'"
                    );
                }
                Object.setPrototypeOf(this, value.prototype);
                return;
            },
            $doc: "the object's class",
        },
    },
    methods: {
        __dir__: {
            $meth: function __dir__() {
                let dir = [];
                if (this.$d) {
                    if (this.$d instanceof Sk.builtin.dict) {
                        dir = this.$d.sk$asarray();
                    } else {
                        for (let key in this.$d) {
                            dir.push(new Sk.builtin.str(key));
                        }
                    }
                }
                // here we use the type.__dir__ implementation
                const type_dir = Sk.misceval.callsimArray(Sk.builtin.type.prototype.__dir__, [this.ob$type]);
                // put the dict keys before the prototype keys
                dir.push(...type_dir.v);
                type_dir.v = dir;
                return type_dir;
            },
            $flags: { NoArgs: true },
            $doc: "Default dir() implementation.",
        },
        __format__: {
            $meth(format_spec) {
                let formatstr;
                if (!Sk.builtin.checkString(format_spec)) {
                    if (Sk.__future__.exceptions) {
                        throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec));
                    } else {
                        throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not " + Sk.abstr.typeName(format_spec));
                    }
                } else {
                    formatstr = Sk.ffi.remapToJs(format_spec);
                    if (formatstr !== "") {
                        throw new Sk.builtin.NotImplementedError("format spec is not yet implemented");
                    }
                }
                return this.tp$str();
            },
            $flags: { OneArg: true },
            $doc: "Default object formatter.",
        },
    },
    proto: /**@lends {Sk.builtin.object.prototype}*/ {
        valueOf: Object.prototype.valueOf,
        toString: function() {
            return this.tp$str().v;
        },
        hasOwnProperty: Object.prototype.hasOwnProperty,
        hp$type: undefined,
        // private method used for error messages
        sk$attrError() {
            return "'" + this.tp$name + "' object";
        },
    },
});

/**
 * @description
 * We aim to match python and javascript inheritance like
 * type   instanceof object => true
 * object instanceof type   => true
 * type   instanceof type   => true
 * object instanceof object => true
 *
 * type   subclassof object => type.prototype   instanceof object => true
 * object subclassof type   => object.prototype instanceof type   => false
 *
 * this algorithm achieves the equivalent with the following prototypical chains
 * using `Object.setPrototypeOf`
 *
 * ```
 * type.__proto__             = type.prototype   (type   instanceof type  )
 * type.__proto__.__proto__   = object.prototype (type   instanceof object)
 * type.prototype.__proto__   = object.prototype (type   subclassof object)
 * object.__proto__           = type.prototype   (object instanceof type  )
 * object.__proto__.__proto__ = object.prototype (object instanceof object)
 * ```
 *
 * while `Object.setPrototypeOf` is not considered [good practice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)
 * this is a particularly unique use case and creates a lot of prototypical benefits
 * all single inheritance classes (i.e. all builtins) now follow prototypical inheritance
 * similarly it makes metclasses that much easier to implement
 * Object.setPrototypeOf is also a feature built into the javascript language
 *
 * @ignore
 */
(function setUpBaseInheritance() {
    Sk.abstr.setUpInheritance("type", Sk.builtin.type, Sk.builtin.object);
    Sk.abstr.setUpBuiltinMro(Sk.builtin.type);
})();



/***/ }),

/***/ "./src/parser.js":
/*!***********************!*\
  !*** ./src/parser.js ***!
  \***********************/
/*! no static exports found */
/***/ (function(module, exports) {

// low level parser to a concrete syntax tree, derived from cpython's lib2to3

/**
 *
 * @constructor
 * @param {Object} grammar
 *
 * p = new Parser(grammar);
 * p.setup([start]);
 * foreach input token:
 *     if p.addtoken(...):
 *         break
 * root = p.rootnode
 *
 * can throw SyntaxError
 */
function Parser (filename, grammar) {
    this.filename = filename;
    this.grammar = grammar;
    this.p_flags = 0;
    return this;
}

// all possible parser flags
Parser.FUTURE_PRINT_FUNCTION = "print_function";
Parser.FUTURE_UNICODE_LITERALS = "unicode_literals";
Parser.FUTURE_DIVISION = "division";
Parser.FUTURE_ABSOLUTE_IMPORT = "absolute_import";
Parser.FUTURE_WITH_STATEMENT = "with_statement";
Parser.FUTURE_NESTED_SCOPES = "nested_scopes";
Parser.FUTURE_GENERATORS = "generators";
Parser.CO_FUTURE_PRINT_FUNCTION = 0x10000;
Parser.CO_FUTURE_UNICODE_LITERALS = 0x20000;
Parser.CO_FUTURE_DIVISON = 0x2000;
Parser.CO_FUTURE_ABSOLUTE_IMPORT = 0x4000;
Parser.CO_FUTURE_WITH_STATEMENT = 0x8000;

Parser.prototype.setup = function (start) {
    var stackentry;
    var newnode;
    start = start || this.grammar.start;
    //print("START:"+start);

    newnode =
    {
        type    : start,
        value   : null,
        context : null,
        children: []
    };
    stackentry =
    {
        dfa  : this.grammar.dfas[start],
        state: 0,
        node : newnode
    };
    this.stack = [stackentry];
    this.used_names = {};
};

function findInDfa (a, obj) {
    var i = a.length;
    while (i--) {
        if (a[i][0] === obj[0] && a[i][1] === obj[1]) {
            return true;
        }
    }
    return false;
}


// Add a token; return true if we're done
Parser.prototype.addtoken = function (type, value, context) {
    var errline;
    var itsfirst;
    var itsdfa;
    var state;
    var v;
    var t;
    var newstate;
    var i;
    var a;
    var arcs;
    var first;
    var states;
    var tp;
    var ilabel = this.classify(type, value, context);
    //print("ilabel:"+ilabel);

    OUTERWHILE:
    while (true) {
        tp = this.stack[this.stack.length - 1];
        states = tp.dfa[0];
        first = tp.dfa[1];
        arcs = states[tp.state];

        // look for a state with this label
        for (a = 0; a < arcs.length; ++a) {
            i = arcs[a][0];
            newstate = arcs[a][1];
            t = this.grammar.labels[i][0];
            v = this.grammar.labels[i][1];
            if (ilabel === i) {
                // look it up in the list of labels
                Sk.asserts.assert(t < 256);
                // shift a token; we're done with it
                this.shift(type, value, newstate, context);
                // pop while we are in an accept-only state
                state = newstate;
                //print("before:"+JSON.stringify(states[state]) + ":state:"+state+":"+JSON.stringify(states[state]));
                /* jshint ignore:start */
                while (states[state].length === 1
                    && states[state][0][0] === 0
                    && states[state][0][1] === state) {
                    // states[state] == [(0, state)])
                    this.pop();
                    //print("in after pop:"+JSON.stringify(states[state]) + ":state:"+state+":"+JSON.stringify(states[state]));
                    if (this.stack.length === 0) {
                        // done!
                        return true;
                    }
                    tp = this.stack[this.stack.length - 1];
                    state = tp.state;
                    states = tp.dfa[0];
                    first = tp.dfa[1];
                    //print(JSON.stringify(states), JSON.stringify(first));
                    //print("bottom:"+JSON.stringify(states[state]) + ":state:"+state+":"+JSON.stringify(states[state]));
                }
                /* jshint ignore:end */
                // done with this token
                //print("DONE, return false");
                return false;
            } else if (t >= 256) {
                itsdfa = this.grammar.dfas[t];
                itsfirst = itsdfa[1];
                if (itsfirst.hasOwnProperty(ilabel)) {
                    // push a symbol
                    this.push(t, this.grammar.dfas[t], newstate, context);
                    continue OUTERWHILE;
                }
            }
        }

        //print("findInDfa: " + JSON.stringify(arcs)+" vs. " + tp.state);
        if (findInDfa(arcs, [0, tp.state])) {
            // an accepting state, pop it and try somethign else
            //print("WAA");
            this.pop();
            if (this.stack.length === 0) {
                throw new Sk.builtin.SyntaxError("too much input", this.filename);
            }
        } else {
            // no transition
            errline = context[0][0];
            throw new Sk.builtin.SyntaxError("bad input", this.filename, errline, context);
        }
    }
};

// turn a token into a label
Parser.prototype.classify = function (type, value, context) {
    var ilabel;
    if (type === Sk.token.tokens.T_NAME) {
        this.used_names[value] = true;
        ilabel = this.grammar.keywords.hasOwnProperty(value) && this.grammar.keywords[value];

        /* Check for handling print as an builtin function */
        if(value === "print" && (this.p_flags & Parser.CO_FUTURE_PRINT_FUNCTION || Sk.__future__.print_function === true)) {
            ilabel = false; // ilabel determines if the value is a keyword
        }

        if (ilabel) {
            //print("is keyword");
            return ilabel;
        }
    }
    ilabel = this.grammar.tokens.hasOwnProperty(type) && this.grammar.tokens[type];
    if (!ilabel) {
        // throw new Sk.builtin.SyntaxError("bad token", type, value, context);
        // Questionable modification to put line number in position 2
        // like everywhere else and filename in position 1.
        let descr = "#"+type;
        for (let i in Sk.token.tokens) {
            if (Sk.token.tokens[i] == type) {
                descr = i;
                break;
            }
        }

        throw new Sk.builtin.SyntaxError("bad token " + descr, this.filename, context[0][0], context);
    }
    return ilabel;
};

// shift a token
Parser.prototype.shift = function (type, value, newstate, context) {
    var dfa = this.stack[this.stack.length - 1].dfa;
    var state = this.stack[this.stack.length - 1].state;
    var node = this.stack[this.stack.length - 1].node;
    //print("context", context);
    var newnode = {
        type      : type,
        value     : value,
        lineno    : context[0][0],         // throwing away end here to match cpython
        col_offset: context[0][1],
        children  : null
    };
    if (newnode) {
        node.children.push(newnode);
    }
    this.stack[this.stack.length - 1] = {
        dfa  : dfa,
        state: newstate,
        node : node
    };
};

// push a nonterminal
Parser.prototype.push = function (type, newdfa, newstate, context) {
    var dfa = this.stack[this.stack.length - 1].dfa;
    var node = this.stack[this.stack.length - 1].node;
    var newnode = {
        type      : type,
        value     : null,
        lineno    : context[0][0],      // throwing away end here to match cpython
        col_offset: context[0][1],
        children  : []
    };
    this.stack[this.stack.length - 1] = {
        dfa  : dfa,
        state: newstate,
        node : node
    };
    this.stack.push({
        dfa  : newdfa,
        state: 0,
        node : newnode
    });
};

//var ac = 0;
//var bc = 0;

// pop a nonterminal
Parser.prototype.pop = function () {
    var node;
    var pop = this.stack.pop();
    var newnode = pop.node;
    //print("POP");
    if (newnode) {
        //print("A", ac++, newnode.type);
        //print("stacklen:"+this.stack.length);
        if (this.stack.length !== 0) {
            //print("B", bc++);
            node = this.stack[this.stack.length - 1].node;
            node.children.push(newnode);
        } else {
            //print("C");
            this.rootnode = newnode;
            this.rootnode.used_names = this.used_names;
        }
    }
};

/**
 * parser for interactive input. returns a function that should be called with
 * lines of input as they are entered. the function will return false
 * until the input is complete, when it will return the rootnode of the parse.
 *
 * @param {string} filename
 * @param {string=} style root of parse tree (optional)
 */
function makeParser (filename, style) {
    if (style === undefined) {
        style = "file_input";
    }
    var p = new Parser(filename, Sk.ParseTables);
    // for closure's benefit
    if (style === "file_input") {
        p.setup(Sk.ParseTables.sym.file_input);
    } else {
        Sk.asserts.fail("todo;");
    }
    return p;
}

Sk.parse = function parse (filename, input) {
    var T_COMMENT = Sk.token.tokens.T_COMMENT;
    var T_NL = Sk.token.tokens.T_NL;
    var T_OP = Sk.token.tokens.T_OP;
    var T_ENDMARKER = Sk.token.tokens.T_ENDMARKER;
    var T_ENCODING = Sk.token.tokens.T_ENCODING;

    var endmarker_seen = false;
    var parser = makeParser(filename);

    /**
     * takes a string splits it on '\n' and returns a function that returns
     * @param {Array<string>} input
     * @returns {function(): string}
     */
    function readline(input) {
        var lines = input.split("\n").reverse().map(function (l) { return l + "\n"; });

        return function() {
            if (lines.length === 0) {
                throw new Sk.builtin.Exception("EOF");
            }

            return lines.pop();
        };
    }

    Sk._tokenize(filename, readline(input), "utf-8", function (tokenInfo) {
        var s_lineno = tokenInfo.start[0];
        var s_column = tokenInfo.start[1];
        var type = null;
        var prefix, lineno, column;

        /* I don't know
         if (s_lineno !== lineno && s_column !== column)
         {
         // todo; update prefix and line/col
         }
         */

        if (tokenInfo.type === T_COMMENT || tokenInfo.type === T_NL || tokenInfo.type === T_ENCODING) {
            prefix += tokenInfo.value;
            lineno = tokenInfo.end[0];
            column = tokenInfo.end[1];
            if (tokenInfo.string[tokenInfo.string.length - 1] === "\n") {
                lineno += 1;
                column = 0;
            }
        } else {
            if (tokenInfo.type === T_OP) {
                type = Sk.OpMap[tokenInfo.string];
            }

            parser.addtoken(type || tokenInfo.type, tokenInfo.string, [tokenInfo.start, tokenInfo.end, tokenInfo.line]);

            if (tokenInfo.type === T_ENDMARKER) {
                endmarker_seen = true;
            }
        }
    });

    if (!endmarker_seen) {
        throw new Sk.builtin.SyntaxError("incomplete input", this.filename);
    }

    /**
     * Small adjustments here in order to return th flags and the cst
     */
    return {"cst": parser.rootnode, "flags": parser.p_flags};
};

Sk.parseTreeDump = function parseTreeDump (n, indent) {
    //return JSON.stringify(n, null, 2);
    var i;
    var ret;
    indent = indent || "";
    ret = "";
    ret += indent;
    if (n.type >= 256) { // non-term
        ret += Sk.ParseTables.number2symbol[n.type] + "\n";
        for (i = 0; i < n.children.length; ++i) {
            ret += Sk.parseTreeDump(n.children[i], indent + "  ");
        }
    } else {
        ret += Sk.token.tok_name[n.type] + ": " + new Sk.builtin.str(n.value)["$r"]().v + "\n";
    }
    return ret;
};


Sk.exportSymbol("Sk.Parser", Parser);
Sk.exportSymbol("Sk.parse", Sk.parse);
Sk.exportSymbol("Sk.parseTreeDump", Sk.parseTreeDump);


/***/ }),

/***/ "./src/print.js":
/*!**********************!*\
  !*** ./src/print.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

Sk.builtin.print = function print(args, kwargs) {
    /** @todo flush is allowed but has no effect */
    let [sep, end, file] = Sk.abstr.copyKeywordsToNamedArgs("print", ["sep", "end", "file", "flush"], [], kwargs);

    // check for sep; string or None
    if (sep === undefined || Sk.builtin.checkNone(sep)) {
        sep = " ";
    } else if (Sk.builtin.checkString(sep)) {
        sep = sep.$jsstr();
    } else {
        throw new Sk.builtin.TypeError("sep must be None or a string, not " + Sk.abstr.typeName(sep));
    }

    // check for end; string or None
    if (end === undefined || Sk.builtin.checkNone(end)) {
        end = "\n";
    } else if (Sk.builtin.checkString(end)) {
        end = end.$jsstr();
    } else {
        throw new Sk.builtin.TypeError("end must be None or a string, not " + Sk.abstr.typeName(end));
    }

    // check for file and get the file_write function if it exists
    let file_write;
    if (file !== undefined && !Sk.builtin.checkNone(file)) {
        file_write = Sk.abstr.lookupSpecial(file, Sk.builtin.str.$write);
        if (file_write === undefined) {
            throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(file) + "' object has no attribute 'write'");
        }
    }

    // loop through outputs and create output string
    const output = new Sk.builtin.str(args.map((x) => new Sk.builtin.str(x).toString()).join(sep) + end);

    if (file_write !== undefined) {
        // currently not tested, though it seems that we need to see how we should access the write function in a correct manner
        Sk.misceval.callsimArray(file_write, [output]);
    } else {
        return Sk.misceval.chain(Sk.importModule("sys", false, true), (sys) => {
            file_write = Sk.abstr.lookupSpecial(sys.$d.stdout, Sk.builtin.str.$write);
            return file_write && Sk.misceval.callsimOrSuspendArray(file_write, [output]);
        });
    }
};

// add this flag so that if Sk.misceval.call(Sk.builtin.print) is used then it knows how to deal with args
Sk.builtin.print.co_fastcall = 1;

/***/ }),

/***/ "./src/property_class_static.js":
/*!**************************************!*\
  !*** ./src/property_class_static.js ***!
  \**************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Sk.builtin.func} fget
 * @param {Sk.builtin.func} fset
 * @param {Sk.builtin.func} fdel
 * @param {Sk.builtin.str} doc
 */
Sk.builtin.property = Sk.abstr.buildNativeClass("property", {
    constructor: function property(fget, fset, fdel, doc) {
        // this can be uses as an internal function
        // typically these properties will be set in the init method
        this.prop$get = fget || Sk.builtin.none.none$;
        this.prop$set = fset || Sk.builtin.none.none$;
        this.prop$del = fdel || Sk.builtin.none.none$;
        this.prop$doc = doc || (fget && fget.$doc) || Sk.builtin.none.none$;
    },
    slots: {
        tp$getattr: Sk.generic.getAttr,
        tp$new: Sk.generic.new,
        tp$init(args, kwargs) {
            args = Sk.abstr.copyKeywordsToNamedArgs(
                "property",
                ["fget", "fset", "fdel", "doc"],
                args,
                kwargs,
                new Array(4).fill(Sk.builtin.none.none$)
            );

            this.prop$get = args[0];
            this.prop$set = args[1];
            this.prop$del = args[2];
            if (Sk.builtin.checkNone(args[3])) {
                if (!Sk.builtin.checkNone(args[0])) {
                    this.prop$doc = args[0].$doc || args[3];
                }
            } else {
                this.prop$doc = args[3];
            }
        },
        tp$doc:
            "Property attribute.\n\n  fget\n    function to be used for getting an attribute value\n  fset\n    function to be used for setting an attribute value\n  fdel\n    function to be used for del'ing an attribute\n  doc\n    docstring\n\nTypical use is to define a managed attribute x:\n\nclass C(object):\n    def getx(self): return self._x\n    def setx(self, value): self._x = value\n    def delx(self): del self._x\n    x = property(getx, setx, delx, 'I'm the 'x' property.')\n\nDecorators make defining new properties or modifying existing ones easy:\n\nclass C(object):\n    @property\n    def x(self):\n        'I am the 'x' property.'\n        return self._x\n    @x.setter\n    def x(self, value):\n        self._x = value\n    @x.deleter\n    def x(self):\n        del self._x",
        tp$descr_get(obj, type) {
            if (obj === null) {
                return this;
            }
            if (this.prop$get === undefined) {
                throw new Sk.builtin.AttributeError("unreadable attribute");
            }
            return Sk.misceval.callsimOrSuspendArray(this.prop$get, [obj]);
        },
        tp$descr_set(obj, value) {
            let func;
            if (value == null) {
                func = this.prop$del;
            } else {
                func = this.prop$set;
            }
            if (Sk.builtin.checkNone(func)) {
                const msg = value == null ? "delete" : "set";
                throw new Sk.builtin.AttributeError("can't " + msg + " attribute");
            }
            if (!func.tp$call) {
                throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(func) + "' is not callable");
            }

            if (value == null) {
                return func.tp$call([obj]);
            } else {
                return func.tp$call([obj, value]);
            }
        },
    },
    methods: {
        getter: {
            $meth(fget) {
                return new Sk.builtin.property(fget, this.prop$set, this.prop$del, this.prop$doc);
            },
            $flags: { OneArg: true },
        },
        setter: {
            $meth(fset) {
                return new Sk.builtin.property(this.prop$get, fset, this.prop$del, this.prop$doc);
            },
            $flags: { OneArg: true },
        },
        deleter: {
            $meth(fdel) {
                return new Sk.builtin.property(this.prop$get, this.prop$set, fdel, this.prop$doc);
            },
            $flags: { OneArg: true },
        },
    },
    getsets: {
        fget: {
            $get() {
                return this.prop$get;
            },
        },
        fset: {
            $get() {
                return this.prop$set;
            },
        },
        fdel: {
            $get() {
                return this.prop$del;
            },
        },
        __doc__: {
            $get() {
                return this.prop$doc;
            },
            $set(value) {
                value = value || Sk.builtin.none.none$;
                this.prop$doc = value;
            }
        },
    },
});

/**
 * @constructor
 * @param {Sk.builtin.func} callable
 */

Sk.builtin.classmethod = Sk.abstr.buildNativeClass("classmethod", {
    constructor: function classmethod(callable) {
        // this can be used as an internal function
        // typically callable will be set in the init method if being called by python
        this.cm$callable = callable;
        this.$d = new Sk.builtin.dict();
    },
    slots: {
        tp$getattr: Sk.generic.getAttr,
        tp$new: Sk.generic.new,
        tp$init(args, kwargs) {
            Sk.abstr.checkNoKwargs("classmethod", kwargs);
            Sk.abstr.checkArgsLen("classmethod", args, 1, 1);
            this.cm$callable = args[0];
        },
        tp$doc:
            "classmethod(function) -> method\n\nConvert a function to be a class method.\n\nA class method receives the class as implicit first argument,\njust like an instance method receives the instance.\nTo declare a class method, use this idiom:\n\n  class C:\n      @classmethod\n      def f(cls, arg1, arg2, ...):\n          ...\n\nIt can be called either on the class (e.g. C.f()) or on an instance\n(e.g. C().f()).  The instance is ignored except for its class.\nIf a class method is called for a derived class, the derived class\nobject is passed as the implied first argument.\n\nClass methods are different than C++ or Java static methods.\nIf you want those, see the staticmethod builtin.",
        tp$descr_get(obj, type) {
            const callable = this.cm$callable;
            if (callable === undefined) {
                throw new Sk.builtin.RuntimeError("uninitialized classmethod object");
            }
            if (type === undefined) {
                type = obj.ob$type;
            }
            const f = callable.tp$descr_get;
            if (f) {
                return f.call(callable, type);
            }
            return new Sk.builtin.method(callable, type);
        },
    },
    getsets: {
        __func__: {
            $get() {
                return this.cm$callable;
            },
        },
        __dict__: Sk.generic.getSetDict,
    },
});

/**
 * @constructor
 * @param {Sk.builtin.func} callable
 */

Sk.builtin.staticmethod = Sk.abstr.buildNativeClass("staticmethod", {
    constructor: function staticmethod(callable) {
        // this can be used as an internal function
        // typically callable will be set in the init method if being called by python
        this.sm$callable = callable;
        this.$d = new Sk.builtin.dict();
    },
    slots: {
        tp$getattr: Sk.generic.getAttr,
        tp$new: Sk.generic.new,
        tp$init(args, kwargs) {
            Sk.abstr.checkNoKwargs("staticmethod", kwargs);
            Sk.abstr.checkArgsLen("staticmethod", args, 1, 1);
            this.sm$callable = args[0];
        },
        tp$doc:
            "staticmethod(function) -> method\n\nConvert a function to be a static method.\n\nA static method does not receive an implicit first argument.\nTo declare a static method, use this idiom:\n\n     class C:\n         @staticmethod\n         def f(arg1, arg2, ...):\n             ...\n\nIt can be called either on the class (e.g. C.f()) or on an instance\n(e.g. C().f()).  The instance is ignored except for its class.\n\nStatic methods in Python are similar to those found in Java or C++.\nFor a more advanced concept, see the classmethod builtin.",
        tp$descr_get(obj, type) {
            if (this.sm$callable === undefined) {
                throw new Sk.builtin.RuntimeError("uninitialized staticmethod object");
            }
            return this.sm$callable;
        },
    },
    getsets: {
        __func__: {
            $get() {
                return this.sm$callable;
            },
        },
        __dict__: Sk.generic.getSetDict,
    },
});


/***/ }),

/***/ "./src/range.js":
/*!**********************!*\
  !*** ./src/range.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {number} start
 * @param {number} stop
 * @param {number} step
 * @param {Object} lst
 */
Sk.builtin.range_ = Sk.abstr.buildNativeClass("range", {
    constructor: function range(start, stop, step, lst) {
        this.start = start;
        this.stop = stop;
        this.step = step;
        this.v = lst;
    },
    slots: {
        tp$getattr: Sk.generic.getAttr,
        tp$as_sequence_or_mapping: true,
        tp$doc:
            "range(stop) -> range object\nrange(start, stop[, step]) -> range object\n\nReturn an object that produces a sequence of integers from start (inclusive)\nto stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.\nstart defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.\nThese are exactly the valid indices for a list of 4 elements.\nWhen step is given, it specifies the increment (or decrement).",
        tp$new(args, kwargs) {
            Sk.abstr.checkNoKwargs("range", kwargs);
            Sk.abstr.checkArgsLen("range", args, 1, 3);
            return rangeFromPy(args[0], args[1], args[2]);
        },
        $r() {
            let repr = "range(" + this.start + ", " + this.stop;
            if (this.step != 1) {
                repr += ", " + this.step;
            }
            repr += ")";
            return new Sk.builtin.str(repr);
        },
        tp$richcompare(w, op) {
            if ((op !== "Eq" && op !== "NotEq") || w.ob$type !== Sk.builtin.range_) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            w = new Sk.builtin.list(w.v);
            return new Sk.builtin.list(this.v).tp$richcompare(w, op);
        },
        tp$iter() {
            return new range_iter_(this);
        },
        nb$bool() {
            return this.v.length !== 0;
        },
        // sequence and mapping slots
        sq$contains(item) {
            const lst = this.v;
            for (let i = 0; i < lst.length; i++) {
                if (Sk.misceval.richCompareBool(item, lst[i], "Eq")) {
                    return true;
                }
            }
            return false;
        },
        sq$length() {
            return this.v.length;
        },
        mp$subscript(index) {
            if (Sk.misceval.isIndex(index)) {
                let i = Sk.misceval.asIndexSized(index);
                if (i < 0) {
                    i = this.v.length + i;
                }
                if (i < 0 || i >= this.v.length) {
                    throw new Sk.builtin.IndexError("range object index out of range");
                }
                return this.v[i];
            } else if (index.constructor === Sk.builtin.slice) {
                const ret = [];
                const lst = this.v;
                index.sssiter$(lst.length, (i) => {
                    ret.push(lst[i]);
                });
                let { start, stop, step } = index.slice$indices(lst.length);
                start = Sk.misceval.asIndex(lst[start]) || this.start;
                stop = Sk.misceval.asIndex(lst[stop]) || this.stop;
                if (typeof this.step === "number") {
                    step = step * this.step;
                } else {
                    step = JSBI.multiply(this.step, JSBI.BigInt(step));
                }
                return new Sk.builtin.range_(start, stop, step, ret);
            }
            throw new Sk.builtin.TypeError("range indices must be integers or slices, not " + Sk.abstr.typeName(index));
        },
    },
    getsets: {
        start: {
            $get() {
                return new Sk.builtin.int_(this.start);
            },
        },
        step: {
            $get() {
                return new Sk.builtin.int_(this.step);
            },
        },
        stop: {
            $get() {
                return new Sk.builtin.int_(this.stop);
            },
        },
    },
    methods: {
        __reversed__: {
            $meth() {
                return new reverserange_iter_(this);
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Return a reverse iterator.",
        },
        // __reduce__: {
        //     $meth: methods.__reduce__,
        //     $flags:{},
        //     $textsig: null,
        //     $doc: "" },
        count: {
            $meth(item) {
                let count = 0;
                for (let i = 0; i < this.v.length; i++) {
                    if (Sk.misceval.richCompareBool(item, this.v[i], "Eq")) {
                        count++;
                    }
                }
                return new Sk.builtin.int_(count);
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "rangeobject.count(value) -> integer -- return number of occurrences of value",
        },
        index: {
            $meth(item) {
                for (let i = 0; i < this.v.length; i++) {
                    if (Sk.misceval.richCompareBool(item, this.v[i], "Eq")) {
                        return new Sk.builtin.int_(i);
                    }
                }
                throw new Sk.builtin.ValueError(Sk.misceval.objectRepr(item) + "is not in range");
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "rangeobject.index(value, [start, [stop]]) -> integer -- return index of value.\nRaise ValueError if the value is not present.",
        },
    },
    proto: {
        sk$asarray() {
            return this.v.slice(0);
        },
    },
    flags: {
        sk$acceptable_as_base_class: false,
    },
});

function rangeFromPy(start, stop, step) {
    start = start === undefined ? start : Sk.misceval.asIndexOrThrow(start);
    stop = stop === undefined ? stop : Sk.misceval.asIndexOrThrow(stop);
    step = step === undefined ? step : Sk.misceval.asIndexOrThrow(step);
    if (stop === undefined && step === undefined) {
        stop = start;
        start = 0;
        step = 1;
    } else if (step === undefined) {
        step = 1;
    } else if (step === 0) {
        throw new Sk.builtin.ValueError("range() step argument must not be zero");
    }
    const ret = [];
    if (typeof start === "number" && typeof stop === "number" && typeof step === "number") {
        if (step > 0) {
            for (let i = start; i < stop; i += step) {
                ret.push(new Sk.builtin.int_(i));
            }
        } else {
            for (let i = start; i > stop; i += step) {
                ret.push(new Sk.builtin.int_(i));
            }
        }
    } else {
        // This is going to be slow
        let i;
        start = i = JSBI.BigInt(start);
        step = JSBI.BigInt(step);
        stop = JSBI.BigInt(stop);
        if (JSBI.greaterThan(step, JSBI.__ZERO)) {
            while (JSBI.lessThan(i, stop)) {
                ret.push(new Sk.builtin.int_(convertIfSafe(i)));
                i = JSBI.add(i, step);
            }
        } else {
            while (JSBI.greaterThan(i, stop)) {
                ret.push(new Sk.builtin.int_(convertIfSafe(i)));
                i = JSBI.add(i, step);
            }
        }
        start = convertIfSafe(start);
        step = convertIfSafe(step);
        stop = convertIfSafe(stop);
    }
    return new Sk.builtin.range_(start, stop, step, ret);
}

var range_iter_ = Sk.abstr.buildIteratorClass("range_iterator", {
    constructor: function range_iter_(range_obj) {
        this.$index = 0;
        this.$seq = range_obj.v;
    },
    iternext() {
        return this.$seq[this.$index++];
        // we could check that the index is not outside of range
        // but it will still return undefined so no need?
    },
    methods: {
        __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});

var reverserange_iter_ = Sk.abstr.buildIteratorClass("range_reverseiterator", {
    constructor: function range_iter(range_obj) {
        this.$seq = range_obj.v;
        this.$index = this.$seq.length - 1;
    },
    iternext() {
        return this.$seq[this.$index--];
    },
    methods: {
        __length_hint__: Sk.generic.iterReverseLengthHintMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});

function convertIfSafe(v) {
    if (JSBI.lessThan(v, JSBI.__MAX_SAFE) && JSBI.greaterThan(v, JSBI.__MIN_SAFE)) {
        return JSBI.toNumber(v);
    }
    return v;
}

/**
 *
 * @description
 * Python 2 implementations of range and xrange
 *
 * @param {*} start
 * @param {*} stop
 * @param {*} step
 * @ignore
 */
Sk.builtin.range = Sk.builtin.xrange = function range(start, stop, step) {
    const ret = rangeFromPy(start, stop, step);
    return new Sk.builtin.list(ret.v);
};


/***/ }),

/***/ "./src/reversed.js":
/*!*************************!*\
  !*** ./src/reversed.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Object} seq
 * @extends Sk.builtin.object
 */
Sk.builtin.reversed = Sk.abstr.buildIteratorClass("reversed", {
    constructor: function reversed(seq) {
        this.$idx = seq.sq$length() - 1;
        this.$seq = seq;
        return this;
    },
    iternext(canSuspend) {
        if (this.$idx < 0) {
            return undefined;
        }
        const ret = Sk.misceval.tryCatch(
            () => Sk.abstr.objectGetItem(this.$seq, new Sk.builtin.int_(this.$idx--), canSuspend),
            (e) => {
                if (e instanceof Sk.builtin.IndexError) {
                    this.$idx = -1;
                    return undefined;
                } else {
                    throw e;
                }
            }
        );
        return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
    },
    slots: {
        tp$doc: "Return a reverse iterator over the values of the given sequence.",
        tp$new(args, kwargs) {
            if (this === Sk.builtin.reversed.prototype) {
                Sk.abstr.checkNoKwargs("reversed", kwargs);
            }
            Sk.abstr.checkArgsLen("reversed", args, 1, 1);
            let seq = args[0];
            const special = Sk.abstr.lookupSpecial(seq, Sk.builtin.str.$reversed);
            if (special !== undefined) {
                return Sk.misceval.callsimArray(special, []);
            } else if (!Sk.builtin.checkSequence(seq) || Sk.abstr.lookupSpecial(seq, Sk.builtin.str.$len) === undefined) {
                throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq) + "' object is not a sequence");
            }
            if (this === Sk.builtin.reversed.prototype) {
                return new Sk.builtin.reversed(seq);
            } else {
                const instance = new this.constructor();
                Sk.builtin.reversed.call(instance, seq);
                return instance;
            }
        },
    },
    methods: {
        __length_hint__: {
            $meth: function __length_hint__() {
                return this.$idx >= 0 ? new Sk.builtin.int_(this.$idx) : new Sk.builtin.int_(0);
            },
            $flags: { NoArgs: true },
        },
    },
});


/***/ }),

/***/ "./src/set.js":
/*!********************!*\
  !*** ./src/set.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

var set_private_ = {};

/**
 *
 * @constructor
 * @param {Array} S
 *
 * @description
 * internally call new Sk.builtin.set with an array of python objects
 */
Sk.builtin.set = Sk.abstr.buildNativeClass("set", {
    constructor: function set(S) {
        if (S === undefined) {
            S = [];
        } else if (!Array.isArray(S)) {
            S = Sk.misceval.arrayFromIterable(S); // internal calls to constructor can't suspend;
        }
        Sk.asserts.assert(this instanceof Sk.builtin.set, "Bad call to set - must be called with an Array and 'new'");
        const L = [];
        for (let i = 0; i < S.length; i++) {
            L.push(S[i]);
            L.push(true);
        }
        this.v = new Sk.builtin.dict(L);
        this.in$repr = false;
    },
    slots: /**@lends {Sk.builtin.set.prototype}*/ {
        tp$getattr: Sk.generic.getAttr,
        tp$as_number: true,
        tp$as_sequence_or_mapping: true,
        tp$hash: Sk.builtin.none.none$,
        tp$doc: "set() -> new empty set object\nset(iterable) -> new set object\n\nBuild an unordered collection of unique elements.",
        tp$init(args, kwargs) {
            Sk.abstr.checkNoKwargs("set", kwargs);
            Sk.abstr.checkArgsLen("set", args, 0, 1);
            this.set$clear();
            const iterable = args[0];
            return iterable && this.set$update(iterable);
        },
        tp$new: Sk.generic.new,
        $r() {
            if (this.in$repr) {
                return new Sk.builtin.str(Sk.abstr.typeName(this) + "(...)");
            }
            this.in$repr = true;
            const ret = this.sk$asarray().map((x) => Sk.misceval.objectRepr(x));
            this.in$repr = false;
            if (Sk.__future__.python3) {
                if (ret.length === 0) {
                    return new Sk.builtin.str(Sk.abstr.typeName(this) + "()");
                } else if (this.ob$type !== Sk.builtin.set) {
                    // then we are a subclass of set
                    return new Sk.builtin.str(Sk.abstr.typeName(this) + "({" + ret.join(", ") + "})");
                } else {
                    return new Sk.builtin.str("{" + ret.join(", ") + "}");
                }
            } else {
                return new Sk.builtin.str(Sk.abstr.typeName(this) + "([" + ret.join(", ") + "])");
            }
        },
        tp$iter() {
            return new set_iter_(this);
        },
        tp$richcompare(other, op) {
            if (!Sk.builtin.checkAnySet(other)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            switch (op) {
                case "NotEq":
                case "Eq":
                    let res;
                    if (this === other) {
                        res = true;
                    } else if (this.get$size() !== other.get$size()) {
                        res = false;
                    } else {
                        res = Sk.misceval.isTrue(this.set$issubset(other));
                    }
                    return op === "Eq" ? res : !res;
                case "LtE":
                    return this === other || Sk.misceval.isTrue(this.set$issubset(other));
                case "GtE":
                    return this === other || Sk.misceval.isTrue(other.set$issubset(this));
                case "Lt":
                    return this.get$size() < other.get$size() && Sk.misceval.isTrue(this.set$issubset(other));
                case "Gt":
                    return this.get$size() > other.get$size() && Sk.misceval.isTrue(other.set$issubset(this));
            }
        },
        // number slots
        nb$subtract: numberSlot(function (other) {
            return this.difference.$meth.call(this, other);
        }),
        nb$and: numberSlot(function (other) {
            return this.intersection.$meth.call(this, other);
        }),
        nb$or: numberSlot(function (other) {
            return this.union.$meth.call(this, other);
        }),
        nb$xor: numberSlot(function (other) {
            return this.symmetric_difference.$meth.call(this, other);
        }),
        nb$inplace_subtract: numberSlot(function (other) {
            if (other === this) {
                other = other.set$copy();
            }
            return Sk.misceval.chain(this.difference_update.$meth.call(this, other), () => this);
        }),
        nb$inplace_and: numberSlot(function (other) {
            return Sk.misceval.chain(this.intersection_update.$meth.call(this, other), () => this);
        }),
        nb$inplace_or: numberSlot(function (other) {
            return Sk.misceval.chain(this.update.$meth.call(this, other), () => this);
        }),
        nb$inplace_xor: numberSlot(function (other) {
            if (other === this) {
                other = other.set$copy();
            }
            return Sk.misceval.chain(this.symmetric_difference_update.$meth.call(this, other), () => this);
        }),
        // sequence or mapping slots
        sq$length() {
            return this.get$size();
        },
        sq$contains(entry) {
            entry = entry_or_hashable_set(entry);
            return this.v.sq$contains(entry);
        },
    },
    methods: /**@lends {Sk.builtin.set.prototype}*/ {
        add: {
            $meth(item) {
                this.set$add(item);
                return Sk.builtin.none.none$;
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Add an element to a set.\n\nThis has no effect if the element is already present.",
        },
        clear: {
            $meth() {
                this.set$clear();
                return Sk.builtin.none.none$;
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Remove all elements from this set.",
        },
        copy: {
            $meth() {
                return this.set$copy();
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Return a shallow copy of a set.",
        },
        discard: {
            $meth(entry) {
                entry = entry_or_hashable_set(entry);
                this.set$discard(entry);
                return Sk.builtin.none.none$;
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Remove an element from a set if it is a member.\n\nIf the element is not a member, do nothing.",
        },
        difference: {
            $meth(...args) {
                const result = this.set$copy();
                return Sk.misceval.chain(
                    Sk.misceval.iterArray(args, (arg) => result.set$difference_update(arg)),
                    () => result
                );
            },
            $flags: { MinArgs: 0 },
            $textsig: null,
            $doc: "Return the difference of two or more sets as a new set.\n\n(i.e. all elements that are in this set but not the others.)",
        },
        difference_update: {
            $meth(...args) {
                return Sk.misceval.chain(
                    Sk.misceval.iterArray(args, (arg) => this.set$difference_update(arg)),
                    () => Sk.builtin.none.none$
                );
            },
            $flags: { MinArgs: 0 },
            $textsig: null,
            $doc: "Remove all elements of another set from this set.",
        },
        intersection: {
            $meth(...args) {
                return this.set$intersection_multi(...args);
            },
            $flags: { MinArgs: 0 },
            $textsig: null,
            $doc: "Return the intersection of two sets as a new set.\n\n(i.e. all elements that are in both sets.)",
        },
        intersection_update: {
            $meth(...args) {
                return Sk.misceval.chain(this.set$intersection_multi(...args), (res) => {
                    this.swap$bodies(res);
                    return Sk.builtin.none.none$;
                });
            },
            $flags: { MinArgs: 0 },
            $textsig: null,
            $doc: "Update a set with the intersection of itself and another.",
        },
        isdisjoint: {
            $meth(other) {
                // requires all items in this to not be in other
                return Sk.misceval.chain(
                    Sk.misceval.iterFor(Sk.abstr.iter(other), (i) => {
                        if (this.sq$contains(i)) {
                            return new Sk.misceval.Break(Sk.builtin.bool.false$);
                        }
                    }),
                    (res) => res || Sk.builtin.bool.true$
                );
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Return True if two sets have a null intersection.",
        },
        issubset: {
            $meth(other) {
                if (!Sk.builtin.checkAnySet(other)) {
                    other = this.set$make_basetype(other);
                }
                return Sk.misceval.chain(other, (other_set) => this.set$issubset(other_set));
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Report whether another set contains this set.",
        },
        issuperset: {
            $meth(other) {
                if (!Sk.builtin.checkAnySet(other)) {
                    other = this.set$make_basetype(other);
                }
                return Sk.misceval.chain(other, (other_set) => other_set.set$issubset(this));
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Report whether this set contains another set.",
        },
        pop: {
            $meth() {
                if (this.get$size() === 0) {
                    throw new Sk.builtin.KeyError("pop from an empty set");
                }
                const item = Sk.misceval.callsimArray(this.v.popitem, [this.v]);
                return item.v[0];
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: "Remove and return an arbitrary set element.\nRaises KeyError if the set is empty.",
        },
        // __reduce__: {
        //     $meth: methods.$__reduce__,
        //     $flags:{},
        //     $textsig: null,
        //     $doc: "Return state information for pickling." },
        remove: {
            $meth(entry) {
                const tmp = entry_or_hashable_set(entry);
                if (this.v.mp$lookup(tmp)) {
                    this.v.mp$ass_subscript(tmp);
                    return Sk.builtin.none.none$;
                }
                throw new Sk.builtin.KeyError(entry);
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Remove an element from a set; it must be a member.\n\nIf the element is not a member, raise a KeyError.",
        },
        // __sizeof__: {
        //     $meth: methods.$__sizeof__,
        //     $flags:{},
        //     $textsig: null,
        //     $doc: "S.__sizeof__() -> size of S in memory, in bytes" },
        symmetric_difference: {
            $meth(other) {
                let other_set;
                return Sk.misceval.chain(
                    this.set$make_basetype(other),
                    (os) => {
                        other_set = os;
                        return other_set.set$symmetric_diff_update(this);
                    },
                    () => other_set
                );
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Return the symmetric difference of two sets as a new set.\n\n(i.e. all elements that are in exactly one of the sets.)",
        },
        symmetric_difference_update: {
            $meth(other) {
                if (!Sk.builtin.checkAnySet(other)) {
                    other = this.set$make_basetype(other);
                }
                return Sk.misceval.chain(
                    other,
                    (other_set) => this.set$symmetric_diff_update(other_set),
                    () => Sk.builtin.none.none$
                );
            },
            $flags: { OneArg: true },
            $textsig: null,
            $doc: "Update a set with the symmetric difference of itself and another.",
        },
        union: {
            $meth(...args) {
                const result = this.set$copy();
                return Sk.misceval.chain(
                    Sk.misceval.iterArray(args, (arg) => result.set$update(arg)),
                    () => result
                );
            },
            $flags: { MinArgs: 0 },
            $textsig: null,
            $doc: "Return the union of sets as a new set.\n\n(i.e. all elements that are in either set.)",
        },
        update: {
            $meth(...args) {
                return Sk.misceval.chain(
                    Sk.misceval.iterArray(args, (arg) => this.set$update(arg)),
                    () => Sk.builtin.none.none$
                );
            },
            $flags: { MinArgs: 0 },
            $textsig: null,
            $doc: "Update a set with the union of itself and others.",
        },
    },
    proto: /**@lends {Sk.builtin.set.prototype}*/ Object.assign(set_private_, {
        sk$asarray() {
            return this.v.sk$asarray();
        },
        get$size() {
            // this method cannot be overriden by subclasses
            return this.v.sq$length();
        },
        set$add(entry) {
            this.v.mp$ass_subscript(entry, true);
        },
        set$make_basetype(other) {
            return Sk.misceval.chain(Sk.misceval.arrayFromIterable(other, true), (S) => new this.sk$builtinBase(S));
        },
        set$discard(entry) {
            return this.v.pop$item(entry);
        },
        set$clear() {
            this.v = new Sk.builtin.dict([]);
        },
        set$copy() {
            const setCopy = new this.sk$builtinBase();
            setCopy.v = this.v.dict$copy();
            return setCopy;
        },
        set$difference_update(other) {
            return Sk.misceval.iterFor(Sk.abstr.iter(other), (entry) => {
                this.set$discard(entry);
            });
        },
        set$intersection(other) {
            const res = new this.sk$builtinBase();
            return Sk.misceval.chain(
                Sk.misceval.iterFor(Sk.abstr.iter(other), (entry) => {
                    if (this.sq$contains(entry)) {
                        res.set$add(entry);
                    }
                }),
                () => res
            );
        },
        set$intersection_multi(...args) {
            if (!args.length) {
                return this.set$copy();
            }
            let result = this;
            return Sk.misceval.chain(
                Sk.misceval.iterArray(args, (arg) => {
                    return Sk.misceval.chain(result.set$intersection(arg), (res) => {
                        result = res;
                    });
                }),
                () => result
            );
        },
        set$issubset(other_set) {
            const thisLength = this.get$size();
            const otherLength = other_set.get$size();
            if (thisLength > otherLength) {
                // every item in this set can't be in other if it's shorter!
                return Sk.builtin.bool.false$;
            }
            for (let it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
                if (!other_set.sq$contains(i)) {
                    return Sk.builtin.bool.false$;
                }
            }
            return Sk.builtin.bool.true$;
        },
        set$symmetric_diff_update(other) {
            return Sk.misceval.iterFor(Sk.abstr.iter(other), (entry) => {
                const discarded = this.set$discard(entry);
                if (discarded === undefined) {
                    this.set$add(entry);
                }
            });
        },
        set$update(other) {
            return Sk.misceval.iterFor(Sk.abstr.iter(other), (entry) => {
                this.set$add(entry);
            });
        },
        swap$bodies(other) {
            this.v = other.v;
        },
    }),
});

Sk.exportSymbol("Sk.builtin.set", Sk.builtin.set);

const set_proto = Sk.builtin.set.prototype;
/**
 * @constructor
 * @param {Array.<Object>} S
 */
Sk.builtin.frozenset = Sk.abstr.buildNativeClass("frozenset", {
    constructor: function frozenset(S) {
        // takes in an array of py objects
        if (S === undefined) {
            S = [];
        }
        Sk.asserts.assert(
            Array.isArray(S) && this instanceof Sk.builtin.frozenset,
            "bad call to frozen set - must be called with an Array and 'new'"
        );
        const L = [];
        for (let i = 0; i < S.length; i++) {
            L.push(S[i]);
            L.push(true);
        }
        this.v = new Sk.builtin.dict(L);
        this.in$repr = false;
    },
    slots: /**@lends {Sk.builtin.frozenset.prototype}*/ {
        tp$getattr: Sk.generic.getAttr,
        tp$as_number: true,
        tp$as_sequence_or_mapping: true,
        tp$doc:
            "frozenset() -> empty frozenset object\nfrozenset(iterable) -> frozenset object\n\nBuild an immutable unordered collection of unique elements.",
        tp$hash() {
            // numbers taken from Cpython 2.7 hash function
            let hash = 1927868237;
            const entries = this.sk$asarray();
            hash *= entries.length + 1;
            for (let i = 0; i < entries.length; i++) {
                const h = Sk.abstr.objectHash(entries[i]);
                hash ^= (h ^ (h << 16) ^ 89869747) * 3644798167;
            }
            hash = hash * 69069 + 907133923;
            return hash;
        },
        /**
         * @param {Array} args
         * @param {Array=} kwargs
         * @ignore
         */
        tp$new(args, kwargs) {
            if (this !== Sk.builtin.frozenset.prototype) {
                return this.$subtype_new(args, kwargs);
            }
            Sk.abstr.checkNoKwargs("frozenset", kwargs);
            Sk.abstr.checkArgsLen("frozenset", args, 0, 1);
            const arg = args[0];
            if (arg !== undefined && arg.ob$type === Sk.builtin.frozenset) {
                return arg;
            }
            return Sk.misceval.chain(Sk.misceval.arrayFromIterable(arg, true), (S) => {
                if (!S.length) {
                    // in cpython frozenset() on an empty set returns the same object
                    return Sk.builtin.frozenset.$emptyset;
                }
                return new Sk.builtin.frozenset(S);
            });
        },
        $r: set_proto.$r,
        tp$iter: set_proto.tp$iter,
        tp$richcompare: set_proto.tp$richcompare,
        // number slots
        nb$subtract: set_proto.nb$subtract,
        nb$and: set_proto.nb$and,
        nb$or: set_proto.nb$or,
        nb$xor: set_proto.nb$xor,
        // as mapping
        sq$length: set_proto.sq$length,
        sq$contains: set_proto.sq$contains,
    },
    methods: /**@lends {Sk.builtin.frozenset.prototype}*/ {
        copy: Object.assign({}, set_proto.copy.d$def, {
            $meth() {
                return this.constructor === this.sk$builtinBase ? this : new Sk.builtin.frozenset(this.sk$asarray());
            },
        }),
        difference: set_proto.difference.d$def,
        intersection: set_proto.intersection.d$def,
        isdisjoint: set_proto.isdisjoint.d$def,
        issubset: set_proto.issubset.d$def,
        issuperset: set_proto.issuperset.d$def,
        // __reduce__: set_proto.__reduce__,
        // __sizeof__: set_proto.__sizeof__,
        symmetric_difference: set_proto.symmetric_difference.d$def,
        union: set_proto.union.d$def,
    },
    proto: /**@lends {Sk.builtin.frozenset.prototype}*/ Object.assign(
        {
            $subtype_new(args, kwargs) {
                const instance = new this.constructor();
                // pass the args but ignore the kwargs for subtyping
                return Sk.misceval.chain(Sk.builtin.frozenset.prototype.tp$new(args), (frozenset) => {
                    instance.v = frozenset.v;
                    return instance;
                });
            },
        },
        set_private_
    ),
});

Sk.builtin.frozenset.$emptyset = Object.create(Sk.builtin.frozenset.prototype, {
    v: { value: new Sk.builtin.dict([]), enumerable: true },
    in$repr: { value: false, enumerable: true },
});

Sk.exportSymbol("Sk.builtin.frozenset", Sk.builtin.frozenset);

function numberSlot(f) {
    return function (other) {
        if (!Sk.builtin.checkAnySet(other)) {
            return Sk.builtin.NotImplemented.NotImplemented$;
        }
        return f.call(this, other);
    };
}

function entry_or_hashable_set(entry) {
    if (entry instanceof Sk.builtin.set && entry.tp$hash === Sk.builtin.none.none$) {
        // cpython nuance
        entry = new Sk.builtin.frozenset(Sk.misceval.arrayFromIterable(entry));
    }
    return entry;
}

/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {Sk.builtin.set|Sk.builtin.frozenset} set or frozenset
 * @private
 */
var set_iter_ = Sk.abstr.buildIteratorClass("set_iterator", {
    constructor: function set_iter_(set) {
        this.$index = 0;
        this.$seq = set.sk$asarray();
        this.$orig = set;
    },
    iternext: Sk.generic.iterNextWithArrayCheckSize,
    methods: {
        __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});


/***/ }),

/***/ "./src/simple_iterators.js":
/*!*********************************!*\
  !*** ./src/simple_iterators.js ***!
  \*********************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * 
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {Sk.builtin.func} callable
 * @param {Sk.builtin.object} sentinel - if reached returns undefined
 * @private
 */
Sk.builtin.callable_iter_ = Sk.abstr.buildIteratorClass("callable_iterator", {
    constructor: function callable_iter (callable, sentinel) {
        if (!Sk.builtin.checkCallable(callable)) {
            throw new Sk.builtin.TypeError("iter(v, w): v must be callable");
        }
        this.$callable = callable;
        this.$sentinel = sentinel;
        this.$flag = false;
    },
    iternext(canSuspend) {
        let ret;
        if (this.$flag === true) {
            // Iterator has already completed
            return undefined;
        }
        if (canSuspend) {
            ret = Sk.misceval.callsimOrSuspendArray(this.$callable, []);
            return Sk.misceval.chain(ret, (r) => {
                if (Sk.misceval.richCompareBool(r, this.$sentinel, "Eq", true)) {
                    this.$flag = true;
                    return undefined;
                } else {
                    return r;
                }
            });
        } else {
            ret = Sk.misceval.callsimArray(this.$callable, []);
            if (Sk.misceval.richCompareBool(ret, this.$sentinel, "Eq", false)) {
                this.$flag = true;
                return undefined;
            } else {
                return ret;
            }
        }
    },
    flags: { sk$acceptable_as_base_class: false },
});




/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {Sk.builtin.object} seq
 * @private
 */
Sk.builtin.seq_iter_ = Sk.abstr.buildIteratorClass("iterator", {
    constructor: function seq_iter (seq) {
        this.$index = 0;
        this.$seq = seq;
    },
    iternext(canSuspend) {
        let ret;
        ret = Sk.misceval.tryCatch(
            () => {
                return this.$seq.mp$subscript(new Sk.builtin.int_(this.$index++), canSuspend);
            },
            (e) => {
                if (e instanceof Sk.builtin.IndexError || e instanceof Sk.builtin.StopIteration) {
                    return undefined;
                } else {
                    throw e;
                }
            }
        );
        return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
    },
    methods: {
        __length_hint__: {
            $flags: { NoArgs: true },
            $meth() {
                if (this.$seq.sq$length) {
                    // sq$length will return Sk.miseval.asIndex
                    return this.$seq.sq$length() - this.$index;
                } else {
                    throw new Sk.builtin.NotImplementedError(
                        "len is not implemented for " + Sk.abstr.typeName(this.$seq)
                    );
                }
            },
        },
    },
    flags: { sk$acceptable_as_base_class: false },
});


Sk.exportSymbol("Sk.builtin.callable_iter_", Sk.builtin.callable_iter_);


/***/ }),

/***/ "./src/sk_method.js":
/*!**************************!*\
  !*** ./src/sk_method.js ***!
  \**************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * Sk.builtin.sk_method
 *
 * @description
 * this constructor is used by all builtin functions or methods
 * the tp$call method is defined based on the flags
 *
 * A good way to determine the flags is to look at the textsignature of a function
 * or find the equivalent function in CPython and map the flags to skulpt flags
 * flags: {
 * NoArgs: true, raises exception if there are args or kwargs (METH_NOARGS)
 * OneArg: true, raises exception if there is more than one Arg (METH_O)
 *
 * MinArgs: int (also assumes noKwargs)
 * MaxArgs: int optional (used in conjuntiontion with MinArgs)
 *
 * NamedArgs: Array e.g. [null, null, "name1", "name2"]
 *            use null for posonly args
 *            ensures that the total number of args (including kwargs) equals the named args
 *            the call sig will check the kwarg names are valid
 *            the call sig applies Defaults (if set) to any named args
 * Defaults: Array (used in conjunction with NamedArgs, can use [undefined] see dict.pop for use case)
 *
 * FastCall && NoKwargs: true, check NoKewords and pass args the function will handle these (METH_FASTCALL)
 * FastCall: pass args, kwargs - the function will handle this (METH_FASTCALL || KEYWORDS)
 *
 * if no flags are set then the tp$call = function.prototype.tp$call
 *
 * @param {Object} method_def
 * @param {*} self
 * @param {string=} module
 */
Sk.builtin.sk_method = Sk.abstr.buildNativeClass("builtin_function_or_method", {
    constructor: function builtin_function_or_method(method_def, self, module) {
        // here we set this.$meth binding it's call signature to self
        this.$meth = method_def.$meth.bind(self);
        this.$doc = method_def.$doc;
        this.$self = self || null;
        this.$module = module ? new Sk.builtin.str(module) : null;
        this.$name = method_def.$name || method_def.$meth.name || "<native JS>";
        this.m$def = method_def;

        // useful to set the $textsig to determine the correct flags
        this.$textsig = method_def.$textsig;

        // override the default tp$call method if there is a valid flag
        const flags = method_def.$flags || {};
        this.$flags = flags;

        if (flags.FastCall && flags.NoKwargs) {
            this.tp$call = this.$fastCallNoKwargs;
        } else if (flags.FastCall) {
            this.tp$call = this.$meth;
        } else if (flags.NoArgs) {
            this.tp$call = this.$callNoArgs;
        } else if (flags.OneArg) {
            this.tp$call = this.$callOneArg;
        } else if (flags.NamedArgs) {
            this.tp$call = this.$callNamedArgs;
        } else if (flags.MinArgs !== undefined) {
            this.tp$call = this.$callMinArgs;
        } else {
            this.func_code = this.$meth;
            this.tp$call = this.$defaultCallMethod;
        }
    },
    proto: {
        $fastCallNoKwargs(args, kwargs) {
            Sk.abstr.checkNoKwargs(this.$name, kwargs);
            return this.$meth(args);
        },
        $callNoArgs(args, kwargs) {
            Sk.abstr.checkNoArgs(this.$name, args, kwargs);
            return this.$meth();
        },
        $callOneArg(args, kwargs) {
            Sk.abstr.checkOneArg(this.$name, args, kwargs);
            return this.$meth(args[0]);
        },
        $callNamedArgs(args, kwargs) {
            args = Sk.abstr.copyKeywordsToNamedArgs(this.$name, this.$flags.NamedArgs, args, kwargs, this.$flags.Defaults);
            return this.$meth(...args);
        },
        $callMinArgs(args, kwargs) {
            Sk.abstr.checkNoKwargs(this.$name, kwargs);
            Sk.abstr.checkArgsLen(this.$name, args, this.$flags.MinArgs, this.$flags.MaxArgs);
            return this.$meth(...args);
        },
        $defaultCallMethod(args, kwargs) {
            // default implementation for all currently created functions that have yet to be be converted
            // and don't utilise flagged calls
            if (this.$self !== null) {
                return Sk.builtin.func.prototype.tp$call.call(this, [this.$self, ...args], kwargs);
            }
            return Sk.builtin.func.prototype.tp$call.call(this, args, kwargs);
        },
        $memoiseFlags() {
            return Sk.builtin.func.prototype.$memoiseFlags.call(this);
        },
        $resolveArgs() {
            return Sk.builtin.func.prototype.$resolveArgs.call(this);
        },
    },
    flags: { sk$acceptable_as_base_class: false },
    slots: {
        tp$getattr: Sk.generic.getAttr,
        $r() {
            if (this.$self === null) {
                return new Sk.builtin.str("<built-in function " + this.$name + ">");
            }
            return new Sk.builtin.str("<built-in method " + this.$name + " of " + Sk.abstr.typeName(this.$self) + " object>");
        },
        tp$call(args, kwargs) {
            return this.tp$call(args, kwargs);
        },
        tp$richcompare(other, op) {
            if ((op !== "Eq" && op !== "NotEq") || !(other instanceof Sk.builtin.sk_method)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            let eq = this.$self === other.$self && this.m$def.$meth === other.m$def.$meth;
            return op === "Eq" ? eq : !eq;
        },
    },
    getsets: {
        __module__: {
            $get() {
                return this.$module || Sk.builtin.none.none$;
            },
            $set(value) {
                value = value || Sk.builtin.none.none$;
                this.$module = value;
            },
        },
        __doc__: {
            $get() {
                return this.$doc ? new Sk.builtin.str(this.$doc) : Sk.builtin.none.none$;
            },
        },
        __name__: {
            $get() {
                return new Sk.builtin.str(this.$name);
            },
        },
        __text_signature__: {
            $get() {
                return new Sk.builtin.str(this.$textsig);
            },
        },
        __self__: {
            $get() {
                // self might be a module object - which means it was created inside a module before the module existed
                // so look the name up in sysmodules
                return this.$self || Sk.sysModules.mp$lookup(this.$module) || Sk.builtin.none.none$;
            },
        },
    },
});


/***/ }),

/***/ "./src/slice.js":
/*!**********************!*\
  !*** ./src/slice.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

/** @typedef {Sk.builtin.object} */ var pyObject;

/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {pyObject} start
 * @param {pyObject=} stop
 * @param {pyObject=} step
 */
Sk.builtin.slice = Sk.abstr.buildNativeClass("slice", {
    constructor: function slice(start, stop, step) {
        if (stop === undefined && step === undefined) {
            stop = start;
            start = Sk.builtin.none.none$;
        }
        if (stop === undefined) {
            stop = Sk.builtin.none.none$;
        }
        if (step === undefined) {
            step = Sk.builtin.none.none$;
        }
        this.start = start;
        this.stop = stop;
        this.step = step;
    },
    slots: /**@lends {Sk.builtin.slice.prototype} */ {
        tp$getattr: Sk.generic.getAttr,
        tp$doc: "slice(stop)\nslice(start, stop[, step])\n\nCreate a slice object.  This is used for extended slicing (e.g. a[0:10:2]).",
        tp$hash: Sk.builtin.none.none$,
        tp$new(args, kwargs) {
            Sk.abstr.checkNoKwargs("slice", kwargs);
            Sk.abstr.checkArgsLen("slice", args, 1, 3);
            return new Sk.builtin.slice(...args);
        },
        $r() {
            const a = Sk.misceval.objectRepr(this.start);
            const b = Sk.misceval.objectRepr(this.stop);
            const c = Sk.misceval.objectRepr(this.step);
            return new Sk.builtin.str("slice(" + a + ", " + b + ", " + c + ")");
        },
        tp$richcompare(w, op) {
            // w not a slice - it's not subclassable so no need to use instanceof here
            if (w.ob$type !== Sk.builtin.slice) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            // This is how CPython does it
            const t1 = new Sk.builtin.tuple([this.start, this.stop, this.step]);
            const t2 = new Sk.builtin.tuple([w.start, w.stop, w.step]);
            return t1.tp$richcompare(t2, op);
        },
    },
    getsets: /**@lends {Sk.builtin.slice.prototype} */ {
        start: {
            $get() {
                return this.start;
            },
        },
        step: {
            $get() {
                return this.step;
            },
        },
        stop: {
            $get() {
                return this.stop;
            },
        },
    },
    methods: /**@lends {Sk.builtin.slice.prototype} */ {
        indices: {
            $meth: function indices(length) {
                length = Sk.misceval.asIndexSized(length, Sk.builtin.OverflowError); // let's not support lengths larger than this.
                // don't support large lengths here which seems fair. 
                if (length < 0) {
                    throw new Sk.builtin.TypeError("length should not be negative");
                }
                const {start, stop, step} = this.slice$indices(length);
                return new Sk.builtin.tuple([new Sk.builtin.int_(start), new Sk.builtin.int_(stop), new Sk.builtin.int_(step)]);
            },
            $doc:
                "S.indices(len) -> (start, stop, stride)\n\nAssuming a sequence of length len, calculate the start and stop\nindices, and the stride length of the extended slice described by\nS. Out of bounds indices are clipped in a manner consistent with the\nhandling of normal slices.",
            $textsig: null,
            $flags: { OneArg: true },
        },
    },
    proto: /**@lends {Sk.builtin.slice.prototype} */ {
        slice$as_indices (sized) {
            let start, stop, step;
            const msg = "slice indices must be integers or None or have an __index__ method";
            let getIndex;
            if (sized) {
                getIndex = (idx) => Sk.misceval.asIndexSized(idx, null, msg);
            } else {
                getIndex = (idx) => Sk.misceval.asIndexOrThrow(idx, msg);
            }
            if (Sk.builtin.checkNone(this.step)) {
                step = 1;
            } else {
                step = getIndex(this.step);
                if (step === 0) {
                    throw new Sk.builtin.ValueError("slice step cannot be zero");
                }
            }
            if (Sk.builtin.checkNone(this.start)) {
                start = null;
            } else {
                start = getIndex(this.start);
            }
            if (Sk.builtin.checkNone(this.stop)) {
                stop = null;
            } else {
                stop = getIndex(this.stop);
            }
            return {start: start, stop: stop, step: step};
        },
        $wrt(length, start, stop, step, sized) {
            let idxFromNeg;
            if (sized) {
                idxFromNeg = (idx) => JSBI.__isBigInt(idx) ? JSBI.add(idx, JSBI.BigInt(length)) : idx + length;
            } else {
                idxFromNeg = (idx) => idx + length;
            }

            if (step > 0) {
                if (start === null) {
                    start = 0;
                } else if (start < 0) {
                    start = idxFromNeg(start);
                    if (start < 0) {
                        start = 0;
                    }
                }
                if (stop === null) {
                    stop = length;
                } else if (stop > length) {
                    stop = length;
                } else if (stop < 0) {
                    stop = idxFromNeg(stop);
                }
            } else {
                if (start === null) {
                    start = length - 1;
                } else if (start >= length) {
                    start = length - 1;
                } else if (start < 0) {
                    start = idxFromNeg(start);
                }
                if (stop === null) {
                    stop = -1;
                } else if (stop < 0) {
                    stop = idxFromNeg(stop);
                    if (stop < 0) {
                        stop = -1;
                    }
                }
            }

            return {start: start, stop: stop, step: step};
        },
        slice$indices(length, sized) {
            let {start, stop, step} = this.slice$as_indices(true, sized);
            return this.$wrt(length, start, stop, step, sized);
        },
        /**
         * used by objects like str, list, tuple that can return a slice
         * @param {number} len
         * @param {Function} f
         */
        sssiter$(len, f) {
            let {start, stop, step} = this.slice$indices(len, true);
            if (step > 0) {
                for (let i = start; i < stop; i += step) {
                    f(i);
                }
            } else {
                for (let i = start; i > stop; i += step) {
                    f(i);
                }
            }
        },
    },
    flags: {
        sk$acceptable_as_base_class: false,
    },
});

/**
 * 
 * @param {*} pyObj 
 * @param {*} start 
 * @param {*} end 
 * 
 * @private
 * 
 * @description
 * helper function for methods that adjust their start, end arguments with respect to
 * a python sequence type object
 */
Sk.builtin.slice.startEnd$wrt = function (pyObj, start, end) {
    const len = pyObj.sq$length();
    const msg = "slice indices must be integers or have an __index__ method";
    if (start === undefined || Sk.builtin.checkNone(start)) {
        start = 0;
    } else {
        start = Sk.misceval.asIndexSized(start, null, msg);
        if (start < 0) {
            start = start + len;
            if (start < 0) {
                start = 0;
            }
        }
    }

    if (end === undefined || Sk.builtin.checkNone(end)) {
        end = len;
    } else {
        end = Sk.misceval.asIndexSized(end, null, msg);
        if (end < 0) {
            end = end + len;
            if (end < 0) {
                end = 0;
            }
        } else if (end > len) {
            end = len;
        }
    }
    return { start: start, end: end };
};


/***/ }),

/***/ "./src/slotdefs.js":
/*!*************************!*\
  !*** ./src/slotdefs.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/** @typedef {Sk.builtin.object} */ var pyObject;
/** @typedef {Sk.builtin.type|Function} */ var typeObject;

/**
 * @description
 * Wrappers and slot functions
 *
 * A wrapper function wrapper a slot defined on the prototype of a builtin type object
 * typically a a slot wrapper will be called with a self argument and args and kwargs
 *
 * self becomes this in when the slot wrapper is called
 * the slot wrapper_descriptor object takes care of checking that self is an instance of the type object
 * @param {*} self
 * @param {Array} args
 * @param {Array=} kwargs
 * @ignore
 */
function wrapperCallNoArgs(self, args, kwargs) {
    // this = the wrapped function
    Sk.abstr.checkNoArgs(this.$name, args, kwargs);
    const res = this.call(self);
    if (res === undefined) {
        return Sk.builtin.none.none$;
    }
    return res;
}
/**
 * @param {*} self
 * @param {Array} args
 * @param {Array=} kwargs
 * @ignore
 */
function wrapperFastCall(self, args, kwargs) {
    // this = the wrapped function
    const res = this.call(self, args, kwargs);
    if (res === undefined) {
        return Sk.builtin.none.none$;
    }
    return res;
}

/**
 * @param {*} self
 * @param {Array} args
 * @param {Array=} kwargs
 * @ignore
 */
function wrapperCallOneArg(self, args, kwargs) {
    // this = the wrapped function
    Sk.abstr.checkOneArg(this.$name, args, kwargs);
    const res = this.call(self, args[0]);
    if (res === undefined) {
        return Sk.builtin.none.none$;
    }
    return res;
}

/**
 * @param {*} self
 * @param {!Array} args
 * @param {Array=} kwargs
 * @ignore
 */
function wrapperCallTernary(self, args, kwargs) {
    // this = the wrapped function
    // only used for __pow__
    Sk.abstr.checkNoKwargs(this.$name, kwargs);
    Sk.abstr.checkArgsLen(this.$name, args, 1, 2);
    const res = this.call(self, ...args);
    if (res === undefined) {
        return Sk.builtin.none.none$;
    }
    return res;
}
/**
 * @param {*} self
 * @param {Array} args
 * @param {Array=} kwargs
 * @ignore
 */
function wrapperSet(self, args, kwargs) {
    Sk.abstr.checkNoKwargs(this.$name, kwargs);
    Sk.abstr.checkArgsLen(this.$name, args, 2, 2);
    this.call(self, args[0], args[1]);
    return Sk.builtin.none.none$;
}

/**
 * @param {*} self
 * @param {Array} args
 * @param {Array=} kwargs
 * @ignore
 */
function wrapperRichCompare(self, args, kwargs) {
    const res = wrapperCallOneArg.call(this, self, args, kwargs);
    if (res === Sk.builtin.NotImplemented.NotImplemented$) {
        return res;
    }
    return new Sk.builtin.bool(res);
}

function wrapperCallBack(wrapper, callback) {
    return function (self,args, kwargs) {
        const res = wrapper.call(this, self, args, kwargs);
        return callback(res);
    };
}

/**
 * @description
 * Slot functions are wrappers around an Sk.builtin.func
 * if skulpt calls tp$init on a type object the slotFunc will call the Sk.builtin.func
 *
 * with most slots we take the approach that we know which dunderFunc will be called
 * However some slots currently double up
 * e.g. mp$ass_subscript is called by both __setitem__ and __delitem__
 * for these dual slots we need to do a typelookup
 *
 * __getattr__ is another complicated case and the algorithm largely follows Cpython's algorithm
 * @ignore
 */
function slotFuncNoArgs(dunderFunc) {
    return function () {
        const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc;
        return Sk.misceval.callsimArray(func, []);
    };
}

/**
 * @param {string} dunderName
 * @param {Function} checkFunc
 * @param {string} checkMsg
 * @param {Function=} f
 * @ignore
 */
function slotFuncNoArgsWithCheck(dunderName, checkFunc, checkMsg, f) {
    return function (dunderFunc) {
        return function () {
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc;
            let res = Sk.misceval.callsimArray(func, []);
            if (!checkFunc(res)) {
                throw new Sk.builtin.TypeError(dunderName + " should return " + checkMsg + " (returned " + Sk.abstr.typeName(res) + ")");
            }
            // f is might be a function that changes the result to a js object like for nb$bool which returns a Boolean
            if (f !== undefined) {
                return f(res);
            }
            return res;
        };
    };
}

function slotFuncOneArg(dunderFunc) {
    return function (value) {
        const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc;
        return Sk.misceval.callsimArray(func, [value]);
    };
}

function slotFuncGetAttribute(pyName, canSuspend) {
    let getattributeFn = this.ob$type.$typeLookup(Sk.builtin.str.$getattribute);
    if (getattributeFn instanceof Sk.builtin.wrapper_descriptor) {
        // we're assuming here that internal tp$getattr won't raise an exception
        return getattributeFn.d$wrapped.call(this, pyName, canSuspend);
    }
    if (getattributeFn.tp$descr_get) {
        getattributeFn = getattributeFn.tp$descr_get(this);
    }
    const ret = Sk.misceval.tryCatch(
        () => Sk.misceval.callsimOrSuspendArray(getattributeFn, [pyName]),
        (e) => {
            if (e instanceof Sk.builtin.AttributeError) {
                return undefined;
            } else {
                throw e;
            }
        }
    );
    return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
}

function slotFuncFastCall(dunderFunc) {
    return function (args, kwargs) {
        const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc;
        return Sk.misceval.callsimOrSuspendArray(func, args, kwargs);
    };
}

/**
 * this is currently a bit of a hack
 * in attempting to maintain dual slots like mp$ass_subscript for assigning and deleting
 * this function has to do a type lookup... since it doesn't know in advance if it is being asked to set or delete
 * @ignore
 */
function slotFuncSetDelete(set_name, del_name, error_msg) {
    return function (dunderFunc) {
        return function (pyObject, value, canSuspend) {
            let res, dunderName;
            if (value === undefined) {
                dunderName = del_name;
                error_msg = null;
            } else {
                dunderName = set_name;
            }
            // do a type lookup and a wrapped function directly
            let func = this.ob$type.$typeLookup(new Sk.builtin.str(dunderName));
            if (func instanceof Sk.builtin.wrapper_descriptor) {
                return func.d$wrapped.call(this, pyObject, value);
            }
            if (func.tp$descr_get) {
                func = func.tp$descr_get(this);
            }
            
            if (func !== undefined) {
                const args = value === undefined ? [pyObject] : [pyObject, value];
                res = Sk.misceval.callsimOrSuspendArray(func, args);
            } else if (error_msg) {
                throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object " + error_msg);
            } else {
                throw new Sk.builtin.AttributeError(dunderName);
            }
            return canSuspend ? res : Sk.misceval.retryOptionalSuspensionOrThrow(res);
        };
    };
}

/**
 * @namespace
 *
 * @description
 * If you want to build a skulpt native class you need to understand slots
 * Each dunder method in python is matched to a slot in skulpt {@link Sk.dunderToSkulpt} which is closely aligned to a Cpython slot
 *
 * Plenty examples exist in {@link  Sk.builtin}
 *
 * If a user builds a `nativeClass` using {@link Sk.abstr.buildNativeClass } they define slots as javascript function
 * Dunder Methods will be created as `slot_wrappers`
 *
 * If a user defines a class in Python or using {@link Sk.misceval.buildClass}
 * Dunder Functions should be defined and slot funcs will be added
 *
 * Below is information about each slot function, should you decide to build a native class
 *
 * For mappings of slots to dunders see source code for {@link Sk.dunderToSkulpt} or [Sk.subSlots]{@link Sk.slots.subSlots}
 *
 */
Sk.slots = Object.create(null);
const slots = Sk.slots;

/**
 *
 * @memberof Sk.slots
 * @member tp$doc
 * @implements __doc__
 * @suppress {checkTypes}
 * @type {string}
 */

/**
 * @memberof Sk.slots
 * @method tp$init
 * @implements __init__
 * @suppress {checkTypes}
 * @param {Array} args
 * @param {Array=} kwargs
 * @returns {Sk.builtin.none}
 */
Sk.slots.__init__ = {
    $name: "__init__",
    $slot_name: "tp$init",
    $slot_func: function (dunderFunc) {
        return function tp$init(args, kwargs) {
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc;
            let ret = Sk.misceval.callsimOrSuspendArray(func, args, kwargs);
            return Sk.misceval.chain(ret, (r) => {
                if (!Sk.builtin.checkNone(r) && r !== undefined) {
                    throw new Sk.builtin.TypeError("__init__() should return None, not " + Sk.abstr.typeName(r));
                }
                return;
            });
        };
    },
    $wrapper: function (self, args, kwargs) {
        // this = the wrapped function
        this.call(self, args, kwargs);
        return Sk.builtin.none.none$;
    },
    $textsig: "($self, /, *args, **kwargs)",
    $flags: { FastCall: true },
    $doc: "Initialize self.  See help(type(self)) for accurate signature.",
};

/**
 * @memberof Sk.slots
 * @method tp$new
 * @implements __new__
 * @suppress {checkTypes}
 * @returns {pyObject}
 * @param {Array} args
 * @param {Array=} kwargs
 * @description
 * {@link Sk.generic.new} {@link Sk.generic.newMethodDef} are related implementations of `tp$mew` and `__new__`
 * unusually `this = typeobject.prototype` since it is typically called like `typeobj.prototype.tp$new` and must
 * be taken into when writing an implementation of `tp$new`
 */
slots.__new__ = {
    $name: "__new__",
    $slot_name: "tp$new",
    $slot_func: function (dunderFunc) {
        const tp$new = function (args, kwargs) {
            let func = dunderFunc;
            if (dunderFunc.tp$descr_get) {
                func = dunderFunc.tp$descr_get(null, this.constructor);
            } // weird behaviour ignore staticmethods bascically
            return Sk.misceval.callsimOrSuspendArray(func, [this.constructor, ...args], kwargs);
        };
        tp$new.sk$static_new = false; // this is a flag used in the __new__ algorithm
        return tp$new;
    },
    $wrapper: null, // handled separately since it's not a slot wrapper but an sk_method
    $textsig: "($self, /, *args, **kwargs)",
    $flags: { FastCall: true },
    $doc: "Create and return a new object.",
};

/**
 * @memberof Sk.slots
 * @method tp$call
 * @implements __call__
 * @suppress {checkTypes}
 * @param {Array} args
 * @param {Array=} kwargs
 *
 */
slots.__call__ = {
    $name: "__call__",
    $slot_name: "tp$call",
    $slot_func: slotFuncFastCall,
    $wrapper: function __call__(self, args, kwargs) {
        // function fast call objects override the prototype.tp$call
        // so use self.tp$call instead of this.call(self)
        const res = self.tp$call(args, kwargs);
        if (res === undefined) {
            return Sk.builtin.none.none$;
        }
        return res;
    }
    ,
    $textsig: "($self, /, *args, **kwargs)",
    $flags: { FastCall: true },
    $doc: "Call self as a function.",
};

/**
 * @memberof Sk.slots
 * @method $r
 * @implements __repr__
 * @suppress {checkTypes}
 * @returns {Sk.builtin.str}
 */
slots.__repr__ = {
    $name: "__repr__",
    $slot_name: "$r",
    $slot_func: slotFuncNoArgsWithCheck("__repr__", Sk.builtin.checkString, "str"),
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "Return repr(self).",
};

/**
 * @memberof Sk.slots
 * @method tp$str
 * @implements `__str__`
 * @suppress {checkTypes}
 * @returns {Sk.builtin.str}
 */
slots.__str__ = {
    $name: "__str__",
    $slot_name: "tp$str",
    $slot_func: slotFuncNoArgsWithCheck("__str__", Sk.builtin.checkString, "str"),
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "Return str(self).",
};

var hash_slot = slotFuncNoArgsWithCheck("__hash__", Sk.builtin.checkInt, "int", (res) => typeof res.v === "number" ? res.v : res.tp$hash());
/**
 * @memberof Sk.slots
 * @method tp$hash
 * @implements __hash__
 * @suppress {checkTypes}
 * @returns {Sk.builtin.int_}
 * @description
 * To be unhashable set this slot to {@link Sk.builtin.none.none$} or call {@link Sk.abstr.markUnhashable}
 */
slots.__hash__ = {
    $name: "__hash__",
    $slot_name: "tp$hash",
    $slot_func: function (dunder_func) {
        if (dunder_func === Sk.builtin.none.none$) {
            return Sk.builtin.none.none$;
        }
        return hash_slot(dunder_func);
    },
    $wrapper: wrapperCallBack(wrapperCallNoArgs, (res) => new Sk.builtin.int_(res)),
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "Return hash(self).",
};

// getters/setters/deletters

/**
 * @memberof Sk.slots
 * @method tp$getattr
 * @implements __getattribute__
 * @suppress {checkTypes}
 *
 * @param {Sk.builtin.str} pyName
 * @param {boolean=} canSuspend
 *
 * @returns {pyObject|undefined}
 * @description
 * This slot will also be given to a pyObject which defines `__getattr__`
 */
slots.__getattribute__ = {
    $name: "__getattribute__",
    $slot_name: "tp$getattr",
    $slot_func: function (dunderFunc) {
        return function tp$getattr(pyName, canSuspend) {
            let getattrFn = this.ob$type.$typeLookup(Sk.builtin.str.$getattr);
            if (getattrFn === undefined) {
                return slotFuncGetAttribute.call(this, pyName, canSuspend);
            }
            const ret = Sk.misceval.chain(slotFuncGetAttribute.call(this, pyName, canSuspend), (val) =>
                Sk.misceval.tryCatch(
                    () => {
                        if (val !== undefined) {
                            return val;
                        }
                        if (getattrFn.tp$descr_get) {
                            getattrFn = getattrFn.tp$descr_get(this);
                        }
                        return Sk.misceval.callsimOrSuspendArray(getattrFn, [pyName]);
                    },
                    function (e) {
                        if (e instanceof Sk.builtin.AttributeError) {
                            return undefined;
                        } else {
                            throw e;
                        }
                    }
                )
            );
            return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
        };
    },
    $wrapper: function (self, args, kwargs) {
        // this = the wrapped function
        Sk.abstr.checkOneArg(this.$name, args, kwargs);
        const pyName = args[0];
        if (!Sk.builtin.checkString(pyName)) {
            throw new Sk.builtin.TypeError("attribute name must be string, not '" + Sk.abstr.typeName(pyName) + "'");
        }
        const res = this.call(self, pyName);
        if (res === undefined) {
            throw new Sk.builtin.AttributeError(Sk.abstr.typeName(self) + " has no attribute " + pyName.$jsstr());
        }
        return res;
    },
    $textsig: "($self, name, /)",
    $flags: { OneArg: true },
    $doc: "Return getattr(self, name).",
};

slots.__getattr__ = {
    $name: "__getattr__",
    $slot_name: "tp$getattr",
    $slot_func: slots.__getattribute__.$slot_func,
    $wrapper: null,
    $textsig: "($self, name, /)",
    $flags: { OneArg: true },
    $doc: "Return getattr(self, name).",
};


/* Helper to check for object.__setattr__ or __delattr__ applied to a type.
   This is called the Carlo Verre hack after its discoverer. */
function hackcheck(obj, func) {
    let type = obj.ob$type;
    while (type && type.sk$klass !== undefined) {
        type = type.prototype.tp$base;
    }
    if (type && type.prototype.tp$setattr !== func) {
        throw new Sk.builtin.TypeError("can't apply this " + func.$name + " to " + Sk.abstr.typeName(obj) + " object");
    }
}

/**
 * @suppress {checkTypes}
 * @memberof Sk.slots
 * @method tp$setattr
 * @implements __setattr__
 * @param {Sk.builtin.str} pyName
 * @param {pyObject|undefined} value undefined indicates the attribute is to be deleted
 * @param {boolean=} canSuspend
 * @description
 * `tp$setattr` is responsible for throwing its own exceptions. It also implements __delattr__
 */
slots.__setattr__ = {
    $name: "__setattr__",
    $slot_name: "tp$setattr",
    $slot_func: slotFuncSetDelete("__setattr__", "__delattr__"),
    // not need for an error message setattr is always defined on object
    $wrapper: function(self, args, kwargs) {
        Sk.abstr.checkNoKwargs(this.$name, kwargs);
        Sk.abstr.checkArgsLen(this.$name, args, 2, 2);
        hackcheck(self, this);
        this.call(self, args[0], args[1]);
        return Sk.builtin.none.none$; 
    },
    $textsig: "($self, name, value, /)",
    $flags: { MinArgs: 2, MaxArgs: 2 },
    $doc: "Implement setattr(self, name, value).",
};

slots.__delattr__ = {
    $name: "__delattr__",
    $slot_name: "tp$setattr",
    $slot_func: slots.__setattr__.$slot_func,
    $wrapper: function(self, args, kwargs) {
        Sk.abstr.checkOneArg(this.$name, args, kwargs);
        hackcheck(self, this);
        this.call(self, args[0]);
        return Sk.builtin.none.none$;
    },
    $textsig: "($self, name, /)",
    $flags: { OneArg: true },
    $doc: "Implement delattr(self, name).",
};

/**
 * @memberof Sk.slots
 * @method tp$descr_get
 * @implements __get__
 * @suppress {checkTypes}
 * @param {pyObject} obj
 * @param {typeObject=} obtype
 * @param {boolean=} canSuspend
 */
slots.__get__ = {
    $name: "__get__",
    $slot_name: "tp$descr_get",
    $slot_func: function (dunderFunc) {
        return function tp$descr_get(obj, obtype, canSuspend) {
            if (obj === null) {
                obj = Sk.builtin.none.none$;
            }
            if (obtype == null) {
                obtype = Sk.builtin.none.none$;
            }
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) :  dunderFunc;
            const ret = Sk.misceval.callsimOrSuspendArray(func, [obj, obtype]);
            return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
        };
    },
    $wrapper: function (self, args, kwargs) {
        Sk.abstr.checkNoKwargs(this.$name, kwargs);
        Sk.abstr.checkArgsLen(this.$name, args, 1, 2);
        let obj = args[0];
        let obtype = args[1];
        if (obj === Sk.builtin.none.none$) {
            obj = null;
        }
        if (obtype === Sk.builtin.none.none$) {
            obtype = null;
        }
        if (obtype === null && obj === null) {
            throw new Sk.builtin.TypeError("__get__(None, None) is invalid");
        }
        return this.call(self, obj, obtype);
    },
    $textsig: "($self, instance, owner, /)",
    $flags: { MinArgs: 2, MaxArgs: 2 },
    $doc: "Return an attribute of instance, which is of type owner.",
};
/**
 * @memberof Sk.slots
 * @method tp$descr_set
 * @implements __set__
 * @suppress {checkTypes}
 * @param {pyObject} obj
 * @param {pyObject|undefined} value undefined will signals __delete__
 * @param {boolean=} canSuspend
 * @description
 * Also implements __delete__
 */
slots.__set__ = {
    $name: "__set__",
    $slot_name: "tp$descr_set",
    $slot_func: slotFuncSetDelete("__set__", "__delete__"),
    $wrapper: wrapperSet,
    $textsig: "($self, instance, value, /)",
    $flags: { MinArgs: 2, MaxArgs: 2 },
    $doc: "Set an attribute of instance to value.",
};

slots.__delete__ = {
    $name: "__delete__",
    $slot_name: "tp$descr_set",
    $slot_func: slots.__set__.$slot_func,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, instance, /)",
    $flags: { OneArg: true },
    $doc: "Delete an attribute of instance.",
};

/**
 * @memberof Sk.slots
 * @method tp$richcompare
 * @implements __eq__
 * @suppress {checkTypes}
 * @param {pyObject} other
 * @param {string} opname "Eq", "NotEq", "Lt", "LtE", "Gt", "GtE"
 * @returns {boolean}
 * @description
 * __eq__/__ne__/__lt__/__le__/__gt__/__ge__
 * Either define tp$richcompare or any of the `ob$*` slots
 * If `tp$richcompare` is defined then the `nativeClass` will get wrapper functions into each `ob$*` slot
 */

/**
 * @memberof Sk.slots
 * @method ob$eq
 * @implements __eq__
 * @suppress {checkTypes}
 * @returns {boolean}
 */
slots.__eq__ = {
    $name: "__eq__",
    $slot_name: "ob$eq",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperRichCompare,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self==value.",
};

/**
 * @memberof Sk.slots
 * @method ob$ge
 * @implements __ge__
 * @suppress {checkTypes}
 * @returns {boolean}
 */
slots.__ge__ = {
    $name: "__ge__",
    $slot_name: "ob$ge",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperRichCompare,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self>=value.",
};
/**
 * @memberof Sk.slots
 * @method ob$gt
 * @implements __gt__
 * @suppress {checkTypes}
 * @returns {boolean}
 */
slots.__gt__ = {
    $name: "__gt__",
    $slot_name: "ob$gt",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperRichCompare,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self>value.",
};
/**
 * @memberof Sk.slots
 * @method ob$le
 * @implements __le__
 * @suppress {checkTypes}
 * @returns {boolean}
 */
slots.__le__ = {
    $name: "__le__",
    $slot_name: "ob$le",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperRichCompare,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self<=value.",
};
/**
 * @memberof Sk.slots
 * @method ob$lt
 * @implements __lt__
 * @suppress {checkTypes}
 * @returns {boolean}
 */
slots.__lt__ = {
    $name: "__lt__",
    $slot_name: "ob$lt",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperRichCompare,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self<value.",
};
/**
 * @memberof Sk.slots
 * @method ob$ne
 * @implements __ne__
 * @suppress {checkTypes}
 * @returns {boolean}
 */
slots.__ne__ = {
    $name: "__ne__",
    $slot_name: "ob$ne",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperRichCompare,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self!=value.",
};

// iters

/**
 * @memberof Sk.slots
 * @method tp$iter
 * @implements __iter__
 * @suppress {checkTypes}
 * @returns {pyObject} must have a valid `tp$iternext` slot
 * See {@link Sk.abstr.buildIteratorClass} and {@link Sk.misceval.iterator}
 */
slots.__iter__ = {
    $name: "__iter__",
    $slot_name: "tp$iter",
    $slot_func: slotFuncNoArgs,
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "Implement iter(self).",
};

/**
 * @memberof Sk.slots
 * @method tp$iternext
 * @param {boolean=} canSuspend
 * @implements __next__
 * @suppress {checkTypes}
 * @returns {pyObject|undefined} Do not raise a StopIteration error instead return undefined
 */
slots.__next__ = {
    $name: "__next__",
    $slot_name: "tp$iternext",
    $slot_func: function (dunderFunc) {
        return function tp$iternext(canSuspend) {
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) :  dunderFunc;
            const ret = Sk.misceval.tryCatch(
                () => Sk.misceval.callsimOrSuspendArray(func, []),
                (e) => {
                    if (e instanceof Sk.builtin.StopIteration) {
                        return undefined;
                    } else {
                        throw e;
                    }
                }
            );
            return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
        };
    },
    /**
     *
     * @param {*} self
     * @param {Array} args
     * @param {Array|undefined=} kwargs
     */
    $wrapper: function (self, args, kwargs) {
        // this = the wrapped function
        Sk.abstr.checkNoArgs(this.$name, args, kwargs);
        // the first tp$iternext is sometimes different from the prototype.tp$iternext
        // so instead of this.call(self) use self.tp$iternext
        return Sk.misceval.chain(self.tp$iternext(true), (res) => {
            if (res === undefined) {
                throw new Sk.builtin.StopIteration();
            }
            return res;
        });
    },
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "Implement next(self).",
};

// sequence and mapping
/**
 * @memberof Sk.slots
 * @member tp$as_sequence_or_mapping
 * @type {boolean}
 * @description
 * set `tp$as_sequence_or_mapping` to `true` in order for for {@link Sk.abstr.buildNativeClass}
 * to acquire appropriate `slot_wrappers` for the slots
 * - [sq$length]{@link Sk.slots.sq$length}
 * - [sq$concat]{@link Sk.slots.sq$concat}
 * - [sq$contains]{@link Sk.slots.sq$contains}
 * - [sq$repeat]{@link Sk.slots.sq$repeat}
 * - [mp$subscript]{@link Sk.slots.mp$subscript}
 * - [mp$ass_subscript]{@link Sk.slots.mp$ass_subscript}
 */

/**
 * @memberof Sk.slots
 * @method sq$concat
 * @implements __add__
 * @suppress {checkTypes}
 * @description defining `sq$concat` along with {@link Sk.slots.tp$as_sequence_or_mapping} will gain the slot
 * `__add__`.
 * note that this slot will be equivalent to the [nb$add]{@link Sk.slots.nb$add} slot
 */

/**
 * @memberof Sk.slots
 * @method sq$repeat
 * @implements __mul__/__rmul__
 * @suppress {checkTypes}
 * @description defining `sq$repeat` along with {@link Sk.slots.tp$as_sequence_or_mapping} will gain the slots
 * `__mul__` and `__rmul__`
 * note that this slot will be equivalent to the [nb$multiply]{@link Sk.slots.nb$multiply} slot
 */

/**
 * @memberof Sk.slots
 * @method sq$length
 * @param {boolean=} canSuspend
 * @implements __len__
 * @suppress {checkTypes}
 * @returns {number}
 */
slots.__len__ = {
    $name: "__len__",
    $slot_name: "sq$length",
    $slot_func: function (dunderFunc) {
        return function sq$length(canSuspend) {
            let res;
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) :  dunderFunc;
            if (canSuspend) {
                res = Sk.misceval.callsimOrSuspendArray(func, []);
                return Sk.misceval.chain(res, (r) => {
                    return Sk.misceval.asIndexOrThrow(r);
                });
            } else {
                res = Sk.misceval.callsimArray(func, []);
                return Sk.misceval.asIndexOrThrow(res);
            }
        };
    },
    $wrapper: wrapperCallBack(wrapperCallNoArgs, (res) => new Sk.builtin.int_(res)),
    $flags: { NoArgs: true },
    $textsig: "($self, /)",
    $doc: "Return len(self).",
};

/**
 * @suppress {checkTypes}
 * @memberof Sk.slots
 * @method sq$contains
 *
 * @param {pyObject} key
 * @param {boolean=} canSuspend
 *
 * @implements __contains__
 * @returns {boolean}
 */
slots.__contains__ = {
    $name: "__contains__",
    $slot_name: "sq$contains",
    $slot_func: function (dunderFunc) {
        return function sq$contains(key, canSuspend) {
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) :  dunderFunc;
            let res = Sk.misceval.callsimOrSuspendArray(func, [key]);
            res = Sk.misceval.chain(res, (r) => Sk.misceval.isTrue(r));
            if (res.$isSuspension) {
                return canSuspend ? res : Sk.misceval.retryOptionalSuspensionOrThrow(res);
            }
            return res;
        };
    },
    // todo - allow for suspensions - but no internal functions suspend here
    $wrapper: wrapperCallBack(wrapperCallOneArg, (res) => new Sk.builtin.bool(res)),
    $textsig: "($self, key, /)",
    $flags: { OneArg: true },
    $doc: "Return key in self.",
};

/**
 * @memberof Sk.slots
 * @method mp$subscript
 * @param {pyObject} key - might be a pyStr, pyInt or pySlice
 * @param {boolean=} canSuspend
 * @implements __getitem__
 * @suppress {checkTypes}
 * @returns {pyObject}
 * @throws {Sk.builtin.TypeError}
 */
slots.__getitem__ = {
    $name: "__getitem__",
    $slot_name: "mp$subscript",
    $slot_func: function (dunderFunc) {
        return function mp$subscript(key, canSuspend) {
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) :  dunderFunc;
            const ret = Sk.misceval.callsimOrSuspendArray(func, [key]);
            return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
        };
    },
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, key, /)",
    $flags: { OneArg: true },
    $doc: "Return self[key].",
};

/**
 * @memberof Sk.slots
 * @method mp$ass_subscript
 * @param {pyObject} item - might be a pyStr, pyInt or pySlice
 * @param {pyObject|undefined} value - undefined indicates the item should be deleted
 * @param {boolean=} canSuspend
 * @implements __setitem__
 * @suppress {checkTypes}
 * @returns {pyObject}
 * @throws {Sk.builtin.TypeError}
 * @description
 * Also implements __delitem__
 */
slots.__setitem__ = {
    $name: "__setitem__",
    $slot_name: "mp$ass_subscript",
    $slot_func: slotFuncSetDelete("__setitem__", "__delitem__", "does not support item assignment"),
    $wrapper: wrapperSet,
    $textsig: "($self, key, value, /)",
    $flags: { MinArgs: 2, MaxArgs: 2 },
    $doc: "Set self[key] to value.",
};

slots.__delitem__ = {
    $name: "__delitem__",
    $slot_name: "mp$ass_subscript",
    $slot_func: slots.__setitem__.$slot_func,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, key, /)",
    $flags: { OneArg: true },
    $doc: "Delete self[key].",
};

// number slots
/**
 * @memberof Sk.slots
 * @member tp$as_number
 * @type {boolean}
 * @description
 * set `tp$as_number` to `true` in order for for {@link Sk.abstr.buildNativeClass}
 * to acquire appropriate `slot_wrappers` for number slots
 * You can find an exhaustive list in the source code {@link Sk.slots}
 *
 * Examples:
 * - [nb$add]{@link Sk.slots.nb$add}
 * - [nb$int]{@link Sk.slots.nb$int}
 * - [nb$divide]{@link Sk.slots.nb$divide} - note we do not use `nb$true_divide`
 * - [nb$bool]{@link Sk.slots.nb$bool} - should return a js boolean
 *
 * You need not define `nb$reflected_*` slots unless your implementation is different from the default implementation
 * Similarly `nb$inplace_` need not be defined unless the implementation is different from the usual slot.
 *
 */

/**
 * @memberof Sk.slots
 * @method nb$add
 * @implements __add__
 * @suppress {checkTypes}
 * @description
 * the reflected slot will be defined if not set
 *
 */
slots.__add__ = {
    $name: "__add__",
    $slot_name: "nb$add",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self+value.",
};
/**
 * @memberof Sk.slots
 * @method nb$relfceted_add
 * @implements __radd__
 * @suppress {checkTypes}
 * @description
 * the reflected slot will be defined if not set
 *
 */
slots.__radd__ = {
    $name: "__radd__",
    $slot_name: "nb$reflected_add",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value+self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_add
 * @implements __iadd__
 * @suppress {checkTypes}
 * @description
 * Only define this if your implementation is different from `nb$add`
 *
 */
slots.__iadd__ = {
    $name: "__iadd__",
    $slot_name: "nb$inplace_add",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self+=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$subtract
 * @implements __sub__
 * @suppress {checkTypes}
 *
 */
slots.__sub__ = {
    $name: "__sub__",
    $slot_name: "nb$subtract",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self-value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_subtract
 * @implements __rsub__
 * @suppress {checkTypes}
 */
slots.__rsub__ = {
    $name: "__rsub__",
    $slot_name: "nb$reflected_subtract",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value-self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_multiply
 * @implements __imul__
 * @suppress {checkTypes}
 */
slots.__imul__ = {
    $name: "__imul__",
    $slot_name: "nb$inplace_multiply",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self*=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$multiply
 * @implements __mul__
 * @suppress {checkTypes}
 */
slots.__mul__ = {
    $name: "__mul__",
    $slot_name: "nb$multiply",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self*value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_multiply
 * @implements __rmul__
 * @suppress {checkTypes}
 */
slots.__rmul__ = {
    $name: "__rmul__",
    $slot_name: "nb$reflected_multiply",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value*self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_subtract
 * @implements __isub__
 * @suppress {checkTypes}
 */
slots.__isub__ = {
    $name: "__isub__",
    $slot_name: "nb$inplace_subtract",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self-=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$remainder
 * @implements __mod__
 * @suppress {checkTypes}
 */
slots.__mod__ = {
    $name: "__mod__",
    $slot_name: "nb$remainder",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self%value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_remainder
 * @implements __rmod__
 * @suppress {checkTypes}
 */
slots.__rmod__ = {
    $name: "__rmod__",
    $slot_name: "nb$reflected_remainder",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value%self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_remainder
 * @implements __imod__
 * @suppress {checkTypes}
 */
slots.__imod__ = {
    $name: "__imod__",
    $slot_name: "nb$inplace_remainder",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement value%=self.",
};
/**
 * @memberof Sk.slots
 * @method nb$divmod
 * @implements __divmod__
 * @suppress {checkTypes}
 */
slots.__divmod__ = {
    $name: "__divmod__",
    $slot_name: "nb$divmod",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return divmod(self, value).",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_divmod
 * @implements __rdivmod__
 * @suppress {checkTypes}
 */
slots.__rdivmod__ = {
    $name: "__rdivmod__",
    $slot_name: "nb$reflected_divmod",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return divmod(value, self)",
};
/**
 * @memberof Sk.slots
 * @method nb$positive
 * @implements __pos__
 * @suppress {checkTypes}
 */
slots.__pos__ = {
    $name: "__pos__",
    $slot_name: "nb$positive",
    $slot_func: slotFuncNoArgs,
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "+self",
};
/**
 * @memberof Sk.slots
 * @method nb$negative
 * @implements __neg__
 * @suppress {checkTypes}
 */
slots.__neg__ = {
    $name: "__neg__",
    $slot_name: "nb$negative",
    $slot_func: slotFuncNoArgs,
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "-self",
};
/**
 * @memberof Sk.slots
 * @method nb$abs
 * @implements __abs__
 * @suppress {checkTypes}
 */
slots.__abs__ = {
    $name: "__abs__",
    $slot_name: "nb$abs",
    $slot_func: slotFuncNoArgs,
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "abs(self)",
};
/**
 * @memberof Sk.slots
 * @method nb$bool
 * @implements __bool__
 * @suppress {checkTypes}
 * @returns {boolean}
 */
slots.__bool__ = {
    $name: "__bool__",
    $slot_name: "nb$bool",
    $slot_func: slotFuncNoArgsWithCheck("__bool__", Sk.builtin.checkBool, "bool", (res) => res.v !== 0),
    $wrapper: wrapperCallBack(wrapperCallNoArgs, (res) => new Sk.builtin.bool(res)),
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "self != 0",
};
/**
 * @memberof Sk.slots
 * @method nb$invert
 * @implements __invert__
 * @suppress {checkTypes}
 */
slots.__invert__ = {
    $name: "__invert__",
    $slot_name: "nb$invert",
    $slot_func: slotFuncNoArgs,
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "~self",
};
/**
 * @memberof Sk.slots
 * @method nb$lshift
 * @implements __lshift__
 * @suppress {checkTypes}
 */
slots.__lshift__ = {
    $name: "__lshift__",
    $slot_name: "nb$lshift",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self<<value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_lshift
 * @implements __rlshift__
 * @suppress {checkTypes}
 */
slots.__rlshift__ = {
    $name: "__rlshift__",
    $slot_name: "nb$reflected_lshift",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value<<self.",
};
/**
 * @memberof Sk.slots
 * @method nb$rshift
 * @implements __rshift__
 * @suppress {checkTypes}
 */
slots.__rshift__ = {
    $name: "__rshift__",
    $slot_name: "nb$rshift",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self>>value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_rshift
 * @implements __rrshift__
 * @suppress {checkTypes}
 */
slots.__rrshift__ = {
    $name: "__rrshift__",
    $slot_name: "nb$reflected_rshift",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value>>self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_lshift
 * @implements __ilshift__
 * @suppress {checkTypes}
 */
slots.__ilshift__ = {
    $name: "__ilshift__",
    $slot_name: "nb$inplace_lshift",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self<<=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_rshift
 * @implements __irshift__
 * @suppress {checkTypes}
 */
slots.__irshift__ = {
    $name: "__irshift__",
    $slot_name: "nb$inplace_rshift",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self=>>value.",
};
/**
 * @memberof Sk.slots
 * @method nb$and
 * @implements __and__
 * @suppress {checkTypes}
 */
slots.__and__ = {
    $name: "__and__",
    $slot_name: "nb$and",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self&value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_and
 * @implements __rand__
 * @suppress {checkTypes}
 */
slots.__rand__ = {
    $name: "__rand__",
    $slot_name: "nb$refelcted_and",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value&self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_and
 * @implements __iand__
 * @suppress {checkTypes}
 */
slots.__iand__ = {
    $name: "__iand__",
    $slot_name: "nb$and",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self&=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$xor
 * @implements __xor__
 * @suppress {checkTypes}
 */
slots.__xor__ = {
    $name: "__xor__",
    $slot_name: "nb$xor",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self^value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_xor
 * @implements __rxor__
 * @suppress {checkTypes}
 */
slots.__rxor__ = {
    $name: "__rxor__",
    $slot_name: "nb$reflected_xor",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value^self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_xor
 * @implements __ixor__
 * @suppress {checkTypes}
 */
slots.__ixor__ = {
    $name: "__ixor__",
    $slot_name: "nb$inplace_xor",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self^=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$or
 * @implements __or__
 * @suppress {checkTypes}
 */
slots.__or__ = {
    $name: "__or__",
    $slot_name: "nb$or",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self|value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_or
 * @implements __ror__
 * @suppress {checkTypes}
 */
slots.__ror__ = {
    $name: "__ror__",
    $slot_name: "nb$reflected_or",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value|self.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_ior
 * @implements __ior__
 * @suppress {checkTypes}
 */
slots.__ior__ = {
    $name: "__ior__",
    $slot_name: "nb$inplace_or",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self|=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$int
 * @implements __int__
 * @suppress {checkTypes}
 */
slots.__int__ = {
    $name: "__int__",
    $slot_name: "nb$int",
    $slot_func: slotFuncNoArgsWithCheck("__int__", Sk.builtin.checkInt, "int"),
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "int(self)",
};
/**
 * @memberof Sk.slots
 * @method nb$float
 * @implements __float__
 * @suppress {checkTypes}
 */
slots.__float__ = {
    $name: "__float__",
    $slot_name: "nb$float",
    $slot_func: slotFuncNoArgsWithCheck("__float__", Sk.builtin.checkFloat, "float"),
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "float(self)",
};
/**
 * @memberof Sk.slots
 * @method nb$floor_divide
 * @implements __floordiv__
 * @suppress {checkTypes}
 */
slots.__floordiv__ = {
    $name: "__floordiv__",
    $slot_name: "nb$floor_divide",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self//value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_floor_divide
 * @implements __rfloordiv__
 * @suppress {checkTypes}
 */
slots.__rfloordiv__ = {
    $name: "__rfloordiv__",
    $slot_name: "nb$reflected_floor_divide",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value//self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_floor_divide
 * @implements __ifloordiv__
 * @suppress {checkTypes}
 */
slots.__ifloordiv__ = {
    $name: "__ifloordiv__",
    $slot_name: "nb$inplace_floor_divide",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self//=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$divide
 * @implements __truediv__
 * @suppress {checkTypes}
 */
slots.__truediv__ = {
    $name: "__truediv__",
    $slot_name: "nb$divide",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self/value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_divide
 * @implements __rtruediv__
 * @suppress {checkTypes}
 */
slots.__rtruediv__ = {
    $name: "__rtruediv__",
    $slot_name: "nb$reflected_divide",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value/self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_divide
 * @implements __itruediv__
 * @suppress {checkTypes}
 */
slots.__itruediv__ = {
    $name: "__itruediv__",
    $slot_name: "nb$inplace_divide",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self/=value.",
};
/**
 * @memberof Sk.slots
 * @method nb$index
 * @implements __index__
 * @suppress {checkTypes}
 */
slots.__index__ = {
    $name: "__index__",
    $slot_name: "nb$index",
    $slot_func: slotFuncNoArgsWithCheck("__index__", Sk.builtin.checkInt, "int", (res) => res.v),
    $wrapper: wrapperCallBack(wrapperCallNoArgs, (res) => new Sk.builtin.int_(res)),
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "Return self converted to an integer, if self is suitable for use as an index into a list.",
};
/**
 * @memberof Sk.slots
 * @method nb$power
 * @implements __pow__
 * @suppress {checkTypes}
 */
slots.__pow__ = {
    $name: "__pow__",
    $slot_name: "nb$power",
    $slot_func: function (dunderFunc) {
        return function (value, mod) {
            const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) :  dunderFunc;
            if (mod == undefined) {
                return Sk.misceval.callsimArray(func, [value]);
            } else {
                return Sk.misceval.callsimArray(func, [value, mod]);
            }
        };
    },
    $wrapper: wrapperCallTernary,
    $textsig: "($self, value, mod=None, /)",
    $flags: { MinArgs: 1, MaxArgs: 2 },
    $doc: "Return pow(self, value, mod).",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_power
 * @implements __rpow__
 * @suppress {checkTypes}
 */
slots.__rpow__ = {
    $name: "__rpow__",
    $slot_name: "nb$reflected_power",
    $slot_func: slots.__pow__.$slot_func,
    $wrapper: wrapperCallTernary,
    $textsig: "($self, value, mod=None, /)",
    $flags: { MinArgs: 1, MaxArgs: 2 },
    $doc: "Return pow(value, self, mod).",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_power
 * @implements __ipow__
 * @suppress {checkTypes}
 */
slots.__ipow__ = {
    $name: "__ipow__",
    $slot_name: "nb$inplace_power",
    $slot_func: slots.__pow__.$slot_func,
    $wrapper: wrapperCallTernary,
    $textsig: "($self, value, mod=None, /)",
    $flags: { MinArgs: 1, MaxArgs: 2 },
    $doc: "Implement **=",
};
/**
 * @memberof Sk.slots
 * @method nb$matrix_multiply
 * @implements __matmul__
 * @suppress {checkTypes}
 */
slots.__matmul__ = {
    $name: "__matmul__",
    $slot_name: "nb$matrix_multiply",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return self@value.",
};
/**
 * @memberof Sk.slots
 * @method nb$reflected_matrix_multiply
 * @implements __rmatmul__
 * @suppress {checkTypes}
 */
slots.__rmatmul__ = {
    $name: "__rmatmul__",
    $slot_name: "nb$reflected_matrix_multiply",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Return value@self.",
};
/**
 * @memberof Sk.slots
 * @method nb$inplace_matrix_multiply
 * @implements __imatmul__
 * @suppress {checkTypes}
 */
slots.__imatmul__ = {
    $name: "__imatmul__",
    $slot_name: "nb$inplace_matrix_multiply",
    $slot_func: slotFuncOneArg,
    $wrapper: wrapperCallOneArg,
    $textsig: "($self, value, /)",
    $flags: { OneArg: true },
    $doc: "Implement self@=value.",
};

// py2 ONLY slots
slots.__long__ = {
    $name: "__long__",
    $slot_name: "nb$long",
    $slot_func: slotFuncNoArgsWithCheck("__long__", Sk.builtin.checkInt, "int"),
    $wrapper: wrapperCallNoArgs,
    $textsig: "($self, /)",
    $flags: { NoArgs: true },
    $doc: "int(self)",
};

var py3$slots;
var py2$slots = {
    next: {
        $name: "next",
        $slot_name: "tp$iternext",
        $slot_func: slots.__next__.$slot_func,
        $wrapper: slots.__next__.$wrapper,
        $textsig: slots.__next__.$textsig,
        $flags: slots.__next__.$flags,
    },
    __nonzero__: {
        $name: "__nonzero__",
        $slot_name: "nb$bool",
        $slot_func: slotFuncNoArgsWithCheck("__nonzero__", Sk.builtin.checkInt, "int", (res) => res.v !== 0),
        $wrapper: wrapperCallBack(wrapperCallNoArgs, (res) => new Sk.builtin.bool(res)),
        $textsig: "($self, /)",
        $flags: { NoArgs: true },
        $doc: "x.__nonzero__() <==> x != 0",
    },
    __div__: {
        $name: "__div__",
        $slot_name: "nb$divide",
        $slot_func: slotFuncOneArg,
        $wrapper: wrapperCallOneArg,
        $textsig: "($self, other/)",
        $flags: { OneArg: true },
        $doc: "x.__div__(y) <==> x/y",
    },
    __rdiv__: {
        $name: "__rdiv__",
        $slot_name: "nb$reflected_divide",
        $slot_func: slotFuncOneArg,
        $wrapper: wrapperCallOneArg,
        $textsig: "($self, other/)",
        $flags: { OneArg: true },
        $doc: "x.__rdiv__(y) <==> x/y",
    },
    __idiv__: {
        $name: "__idiv__",
        $slot_name: "nb$inplace_divide",
        $slot_func: slotFuncOneArg,
        $wrapper: wrapperCallOneArg,
        $textsig: "($self, other/)",
        $flags: { OneArg: true },
        $doc: "implement self /= other",
    },
};
/**
 * @memberof Sk.slots
 * @member subSlots
 * @description
 * See the source code for a full list of slots split into apprpriate categories
 */
Sk.subSlots = {
    main_slots: Object.entries({
        // nb we handle tp$new differently
        // tp_slots
        tp$init: "__init__",
        tp$call: "__call__",
        $r: "__repr__",
        // tp$hash: "__hash__", // do tp$hash separately since it could be None
        tp$str: "__str__",

        // getattribute, setattr, delattr
        tp$getattr: "__getattribute__",
        tp$setattr: ["__setattr__", "__delattr__"],

        // tp$richcompare
        ob$eq: "__eq__",
        ob$ne: "__ne__",
        ob$lt: "__lt__",
        ob$le: "__le__",
        ob$gt: "__gt__",
        ob$ge: "__ge__",

        // getters and setters
        tp$descr_get: "__get__",
        tp$descr_set: ["__set__", "__delete__"],

        // iter
        tp$iter: "__iter__",
        tp$iternext: "__next__",
    }),

    number_slots: Object.entries({
        nb$abs: "__abs__",
        nb$negative: "__neg__",
        nb$positive: "__pos__",
        nb$int: "__int__",
        nb$long: "__long__",
        nb$float: "__float__",
        nb$add: "__add__",
        nb$reflected_add: "__radd__",
        nb$inplace_add: "__iadd__",
        nb$subtract: "__sub__",
        nb$reflected_subtract: "__rsub__",
        nb$inplace_subtract: "__isub__",
        nb$multiply: "__mul__",
        nb$reflected_multiply: "__rmul__",
        nb$inplace_multiply: "__imul__",
        nb$floor_divide: "__floordiv__",
        nb$reflected_floor_divide: "__rfloordiv__",
        nb$inplace_floor_divide: "__ifloordiv__",
        nb$invert: "__invert__",
        nb$remainder: "__mod__",
        nb$reflected_remainder: "__rmod__",
        nb$inplace_remainder: "__imod__",
        nb$divmod: "__divmod__",
        nb$reflected_divmod: "__rdivmod__",
        nb$power: "__pow__",
        nb$reflected_power: "__rpow__",
        nb$inplace_power: "__ipow__",
        nb$divide: "__truediv__", // TODO: think about py2 vs py3 truediv vs div
        nb$reflected_divide: "__rtruediv__",
        nb$inplace_divide: "__itruediv__",

        nb$bool: "__bool__",

        nb$and: "__and__",
        nb$reflected_and: "__rand__",
        nb$inplace_and: "__iand__",
        nb$or: "__or__",
        nb$reflected_or: "__ror__",
        nb$inplace_or: "__ior__",
        nb$xor: "__xor__",
        nb$reflected_xor: "__rxor__",
        nb$inplace_xor: "__ixor__",

        nb$lshift: "__lshift__",
        nb$reflected_lshift: "__rlshift__",
        nb$rshift: "__rshift__",
        nb$reflected_rshift: "__rrshift__",
        nb$inplace_lshift: "__ilshift__",
        nb$inplace_rshift: "__irshift__",

        nb$matrix_multiply: "__matmul__",
        nb$reflected_matrix_multiply: "__rmatmul__",
        nb$inplace_matrix_multiply: "__imatmul__",
    }),

    sequence_and_mapping_slots: Object.entries({
        // sequence and mapping slots
        sq$length: "__len__",
        sq$contains: "__contains__",
        mp$subscript: "__getitem__",
        mp$ass_subscript: ["__setitem__", "__delitem__"],
        nb$add: "__add__",
        nb$multiply: "__mul__",
        nb$reflected_multiply: "__rmul__",
        nb$inplace_add: "__iadd__",
        nb$inplace_multiply: "__imul__",
    }),
};

Sk.reflectedNumberSlots = {
    nb$add: { reflected: "nb$reflected_add" },
    nb$subtract: {
        reflected: "nb$reflected_subtract",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$subtract(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$multiply: { reflected: "nb$reflected_multiply" },
    nb$divide: {
        reflected: "nb$reflected_divide",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$divide(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$floor_divide: {
        reflected: "nb$reflected_floor_divide",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$floor_divide(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$remainder: {
        reflected: "nb$reflected_remainder",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$remainder(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$divmod: {
        reflected: "nb$reflected_divmod",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$divmod(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$power: {
        reflected: "nb$reflected_power",
        slot: function (other, mod) {
            if (other instanceof this.constructor) {
                return other.nb$power(this, mod);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$and: { reflected: "nb$reflected_and" },
    nb$or: { reflected: "nb$reflected_or" },
    nb$xor: { reflected: "nb$reflected_xor" },
    nb$lshift: {
        reflected: "nb$reflected_lshift",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$lshift(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$rshift: {
        reflected: "nb$reflected_rshift",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$rshift(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
    nb$matrix_multiply: {
        reflected: "nb$reflexted_matrix_multiply",
        slot: function (other) {
            if (other instanceof this.constructor) {
                return other.nb$matrix_multiply(this);
            }
            return Sk.builtin.NotImplemented.NotImplemented$;
        },
    },
};

Sk.sequenceAndMappingSlots = {
    sq$concat: ["nb$add"],
    sq$repeat: ["nb$multiply", "nb$reflected_multiply"],
    mp$length: ["sq$length"],
    sq$inplace_repeat: ["nb$inplace_multiply"],
    sq$inplace_concat: ["nb$inplace_add"],
};

/**
 *
 *
 * @member Sk.dunderToSkulpt
 *
 * Maps Python dunder names to the Skulpt Javascript function names that
 * implement them.
 *
 * Note: __add__, __mul__, and __rmul__ can be used for either numeric or
 * sequence types. Here, they default to the numeric versions (i.e. nb$add,
 * nb$multiply, and nb$reflected_multiply). This works because Sk.abstr.numberBinOp
 * checks for the numeric shortcuts and not the sequence shortcuts when computing
 * a binary operation.
 *
 * Because many of these functions are used in contexts in which Skulpt does not
 * [yet] handle suspensions, the assumption is that they must not suspend. However,
 * some of these built-in functions are acquiring "canSuspend" arguments to signal
 * where this is not the case. These need to be spliced out of the argument list before
 * it is passed to python. Array values in this map contain [dunderName, argumentIdx],
 * where argumentIdx specifies the index of the "canSuspend" boolean argument.
 *
 * @description
 * A mapping of dunder names to skulpt slots
 *
 * @type {Object}
 */
Sk.dunderToSkulpt = {
    __repr__: "$r",
    __str__: "tp$str",
    __init__: "tp$init",
    __new__: "tp$new",
    __hash__: "tp$hash",
    __call__: "tp$call",
    __iter__: "tp$iter",
    __next__: "tp$iternext",

    __eq__: "ob$eq",
    __ne__: "ob$ne",
    __lt__: "ob$lt",
    __le__: "ob$le",
    __gt__: "ob$gt",
    __ge__: "ob$ge",

    __abs__: "nb$abs",
    __neg__: "nb$negative",
    __pos__: "nb$positive",
    __int__: "nb$int",
    __float__: "nb$float",

    __add__: "nb$add",
    __radd__: "nb$reflected_add",
    __iadd__: "nb$inplace_add",
    __sub__: "nb$subtract",
    __rsub__: "nb$reflected_subtract",
    __isub__: "nb$inplace_subtract",
    __mul__: "nb$multiply",
    __rmul__: "nb$reflected_multiply",
    __imul__: "nb$inplace_multiply",
    __truediv__: "nb$divide",
    __rtruediv__: "nb$reflected_divide",
    __itruediv__: "nb$inplace_divide",
    __floordiv__: "nb$floor_divide",
    __rfloordiv__: "nb$reflected_floor_divide",
    __ifloordiv__: "nb$inplace_floor_divide",
    __invert__: "nb$invert",
    __mod__: "nb$remainder",
    __rmod__: "nb$reflected_remainder",
    __imod__: "nb$inplace_remainder",
    __divmod__: "nb$divmod",
    __rdivmod__: "nb$reflected_divmod", //no inplace divmod
    __pow__: "nb$power",
    __rpow__: "nb$reflected_power",
    __ipow__: "nb$inplace_power",

    __bool__: "nb$bool",
    // py2 only
    __long__: "nb$long",

    __lshift__: "nb$lshift",
    __rlshift__: "nb$reflected_lshift",
    __ilshift__: "nb$inplace_lshift",
    __rshift__: "nb$rshift",
    __rrshift__: "nb$reflected_rshift",
    __irshift__: "nb$inplace_rshift",

    __and__: "nb$and",
    __rand__: "nb$reflected_and",
    __iand__: "nb$inplace_and",
    __or__: "nb$or",
    __ror__: "nb$reflected_or",
    __ior__: "nb$inplace_or",
    __xor__: "nb$xor",
    __rxor__: "nb$reflected_xor",
    __ixor__: "nb$inplace_xor",

    __matmul__: "nb$matrix_multiply",
    __rmatmul__: "nb$reflected_matrix_multiply",
    __imatmul__: "nb$inplace_matrix_multiply",

    __get__: "tp$descr_get",
    __set__: "tp$descr_set",
    __delete__: "tp$descr_set",

    __getattribute__: "tp$getattr",
    __getattr__: "tp$getattr",
    __setattr__: "tp$setattr",
    __delattr__: "tp$setattr",

    __len__: "sq$length",
    __contains__: "sq$contains",
    __getitem__: "mp$subscript",
    __setitem__: "mp$ass_subscript",
    __delitem__: "mp$ass_subscript",
};

Sk.exportSymbol("Sk.setupDunderMethods", Sk.setupDunderMethods);

Sk.setupDunderMethods = function (py3) {
    const slots = Sk.slots;
    if (py3 && py3$slots === undefined) {
        // assume python3 switch version if we have to
        return;
    }
    const classes_with_next = Sk.abstr.built$iterators;
    const classes_with_bool = [Sk.builtin.int_, Sk.builtin.lng, Sk.builtin.float_, Sk.builtin.complex];
    const classes_with_divide = classes_with_bool;
    const number_slots = Sk.subSlots.number_slots;
    const main_slots = Sk.subSlots.main_slots;
    const indexofnext = main_slots.findIndex((x) => x[0] === "tp$iternext");
    const indexofbool = number_slots.findIndex((x) => x[0] === "nb$bool");
    const dunderToSkulpt = Sk.dunderToSkulpt;

    function switch_version(classes_with, old_meth, new_meth) {
        for (let i = 0; i < classes_with.length; i++) {
            const cls_proto = classes_with[i].prototype;
            if (cls_proto.hasOwnProperty(new_meth)) {
                continue;
            }
            cls_proto[new_meth] = cls_proto[old_meth];
            delete cls_proto[old_meth];
        }
    }

    if (py3) {
        dunderToSkulpt.__bool__ = "nb$bool";
        dunderToSkulpt.__next__ = "tp$iternext";

        delete dunderToSkulpt.__nonzero__;
        delete dunderToSkulpt.__div__;
        delete dunderToSkulpt.__rdiv__;
        delete dunderToSkulpt.__idiv__;
        delete dunderToSkulpt.next;

        for (let slot_name in py3$slots) {
            slots[slot_name] = py3$slots[slot_name];
        }
        for (let slot_name in py2$slots) {
            delete slots[slot_name];
        }
        for (let i = 0; i < classes_with_divide.length; i++) {
            const cls_proto = classes_with_divide[i].prototype;
            delete cls_proto.__div__;
            delete cls_proto.__rdiv__;
        }

        main_slots[indexofnext][1] = "__next__";
        number_slots[indexofbool][1] = "__bool__";
        switch_version(classes_with_next, "next", "__next__");
        switch_version(classes_with_bool, "__bool__", "__nonzero__");
    } else {
        if (py3$slots === undefined) {
            slots.py3$slots = {
                __next__: slots.__next__,
            };
            py3$slots = slots.py3$slots;
        }
        dunderToSkulpt.next = "tp$iternext";
        dunderToSkulpt.__nonzero__ = "nb$bool";
        dunderToSkulpt.__div__ = "nb$divide";
        dunderToSkulpt.__rdiv__ = "nb$reflected_divide";
        dunderToSkulpt.__idiv__ = "nb$inplace_divide";
        delete dunderToSkulpt.__bool__;
        delete dunderToSkulpt.__next__;

        for (let slot_name in py2$slots) {
            slots[slot_name] = py2$slots[slot_name];
        }
        for (let slot_name in py3$slots) {
            delete slots[slot_name];
        }

        main_slots[indexofnext][1] = "next";
        number_slots[indexofbool][1] = "__nonzero__";
        switch_version(classes_with_next, "__next__", "next");
        switch_version(classes_with_bool, "__nonzero__", "__bool__");

        for (let i = 0; i < classes_with_divide.length; i++) {
            const cls = classes_with_divide[i];
            const cls_proto = cls.prototype;
            if (cls_proto.hasOwnProperty("__div__")) {
                continue;
            }
            cls_proto.__div__ = new Sk.builtin.wrapper_descriptor(cls, py2$slots.__div__, cls_proto.nb$divide);
            cls_proto.__rdiv__ = new Sk.builtin.wrapper_descriptor(cls, py2$slots.__rdiv__, Sk.reflectedNumberSlots.nb$divide.slot);
        }
    }
};


/***/ }),

/***/ "./src/str.js":
/*!********************!*\
  !*** ./src/str.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

var keyhash_regex = /^[0-9!#_]/;
var interned = Object.create(null); // avoid name conflicts with Object.prototype

function getInterned(x) {
    return interned[x];
}

function setInterned(x, pyStr) {
    interned[x] = pyStr;
}

/**
 * @constructor
 * @param {*} x
 * @extends Sk.builtin.object
 */
Sk.builtin.str = Sk.abstr.buildNativeClass("str", {
    constructor: function str(x) {
        // new Sk.builtin.str is an internal function called with a JS value x
        // occasionally called with a python object and returns tp$str() or $r();
        Sk.asserts.assert(this instanceof Sk.builtin.str, "bad call to str - use 'new'");
        let ret;
        if (typeof x === "string") {
            ret = x;
        } else if (x === undefined) {
            ret = "";
        } else if (x === null) { 
            ret = "None";
        } else if (x.tp$str !== undefined) {
            // then we're a python object - all objects inherit from object which has tp$str
            return x.tp$str();
        } else if (typeof x === "number") {
            ret = Number.isFinite(x) ? String(x) : String(x).replace("Infinity", "inf").replace("NaN", "nan");
        } else {
            throw new Sk.builtin.TypeError("could not convert object of type '" + Sk.abstr.typeName(x) + "' to str");
        }

        const interned = getInterned(ret);
        // interning required for strings in py
        if (interned !== undefined) {
            return interned;
        } else {
            setInterned(ret, this);
        }

        this.$mangled = fixReserved(ret);
        // used by dict key hash function $savedKeyHash
        this.$savedKeyHash = ret.replace(keyhash_regex, "!$&");
        this.v = ret;
    },
    slots: /**@lends {Sk.builtin.str.prototype} */ {
        tp$getattr: Sk.generic.getAttr,
        tp$as_sequence_or_mapping: true,
        tp$doc:
            "str(object='') -> str\nstr(bytes_or_buffer[, encoding[, errors]]) -> str\n\nCreate a new string object from the given object. If encoding or\nerrors is specified, then the object must expose a data buffer\nthat will be decoded using the given encoding and error handler.\nOtherwise, returns the result of object.__str__() (if defined)\nor repr(object).\nencoding defaults to sys.getdefaultencoding().\nerrors defaults to 'strict'.",
        tp$new(args, kwargs) {
            kwargs = kwargs || [];
            if (this !== Sk.builtin.str.prototype) {
                return this.$subtype_new(args, kwargs);
            }
            if (args.length <= 1 && !kwargs.length) {
                return new Sk.builtin.str(args[0]);
            } else if (!Sk.__future__.python3) {
                throw new Sk.builtin.TypeError("str takes at most one argument (" + (args.length + kwargs.length) + " given)");
            } else {
                const [x, encoding, errors] = Sk.abstr.copyKeywordsToNamedArgs("str", ["object", "encoding", "errors"], args, kwargs);
                if (x === undefined || (encoding === undefined && errors === undefined)) {
                    return new Sk.builtin.str(x);
                }
                // check the types of encoding and errors
                Sk.builtin.bytes.check$encodeArgs("str", encoding, errors);
                if (!Sk.builtin.checkBytes(x)) {
                    throw new Sk.builtin.TypeError("decoding to str: need a bytes-like object, " + Sk.abstr.typeName(x) + " found");
                }
                return Sk.builtin.bytes.$decode.call(x, encoding, errors);
            }
        },
        $r() {
            // single is preferred
            let quote = "'";
            if (this.v.indexOf("'") !== -1 && this.v.indexOf('"') === -1) {
                quote = '"';
            }
            //jshint ignore:end
            const len = this.v.length;
            let c,
                cc,
                ret = quote;
            for (let i = 0; i < len; i++) {
                c = this.v.charAt(i);
                cc = this.v.charCodeAt(i);
                if (c === quote || c === "\\") {
                    ret += "\\" + c;
                } else if (c === "\t") {
                    ret += "\\t";
                } else if (c === "\n") {
                    ret += "\\n";
                } else if (c === "\r") {
                    ret += "\\r";
                } else if (((cc > 0xff && cc < 0xd800) || cc >= 0xe000) && !Sk.__future__.python3) {
                    // BMP
                    ret += "\\u" + ("000" + cc.toString(16)).slice(-4);
                } else if (cc >= 0xd800 && !Sk.__future__.python3) {
                    // Surrogate pair stuff
                    let val = this.v.codePointAt(i);
                    i++;

                    val = val.toString(16);
                    let s = "0000000" + val.toString(16);
                    if (val.length > 4) {
                        ret += "\\U" + s.slice(-8);
                    } else {
                        ret += "\\u" + s.slice(-4);
                    }
                } else if (cc > 0xff && !Sk.__future__.python3) {
                    // Invalid!
                    ret += "\\ufffd";
                } else if (c < " " || (cc >= 0x7f && !Sk.__future__.python3)) {
                    let ashex = c.charCodeAt(0).toString(16);
                    if (ashex.length < 2) {
                        ashex = "0" + ashex;
                    }
                    ret += "\\x" + ashex;
                } else {
                    ret += c;
                }
            }
            ret += quote;
            return new Sk.builtin.str(ret);
        },
        tp$str() {
            if (this.constructor === Sk.builtin.str) {
                return this;
            } else {
                return new Sk.builtin.str(this.v);
            }
        },
        tp$iter() {
            return new str_iter_(this);
        },
        tp$richcompare(other, op) {
            if (!(other instanceof Sk.builtin.str)) {
                return Sk.builtin.NotImplemented.NotImplemented$;
            }
            switch (op) {
                case "Lt":
                    return this.v < other.v;
                case "LtE":
                    return this.v <= other.v;
                case "Eq":
                    return this.v === other.v;
                case "NotEq":
                    return this.v !== other.v;
                case "Gt":
                    return this.v > other.v;
                case "GtE":
                    return this.v >= other.v;
            }
        },
        mp$subscript(index) {
            let len;
            if (Sk.misceval.isIndex(index)) {
                index = Sk.misceval.asIndexSized(index, Sk.builtin.OverflowError);
                len = this.sq$length();
                if (index < 0) {
                    index = index + len;
                }
                if (index < 0 || index >= len) {
                    throw new Sk.builtin.IndexError("string index out of range");
                }
                if (this.codepoints) {
                    return new Sk.builtin.str(this.v.substring(this.codepoints[index], this.codepoints[index + 1]));
                } else {
                    return new Sk.builtin.str(this.v.charAt(index));
                }
            } else if (index instanceof Sk.builtin.slice) {
                let ret = "";
                len = this.sq$length();
                if (this.codepoints) {
                    index.sssiter$(len, (i) => {
                        ret += this.v.substring(this.codepoints[i], this.codepoints[i + 1]);
                    });
                } else {
                    index.sssiter$(len, (i) => {
                        ret += this.v.charAt(i);
                    });
                }
                return new Sk.builtin.str(ret);
            }
            throw new Sk.builtin.TypeError("string indices must be integers, not " + Sk.abstr.typeName(index));
        },
        sq$length() {
            return this.$hasAstralCodePoints() ? this.codepoints.length : this.v.length;
        },
        sq$concat(other) {
            if (!(other instanceof Sk.builtin.str)) {
                throw new Sk.builtin.TypeError("cannot concatenate 'str' and '" + Sk.abstr.typeName(other) + "' objects");
            }
            return new Sk.builtin.str(this.v + other.v);
        },
        sq$repeat(n) {
            if (!Sk.misceval.isIndex(n)) {
                throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'");
            }
            n = Sk.misceval.asIndexSized(n, Sk.builtin.OverflowError);
            if (n * this.v.length > Number.MAX_SAFE_INTEGER) {
                throw new Sk.builtin.OverflowError();
            }
            let ret = "";
            for (let i = 0; i < n; i++) {
                ret += this.v;
            }
            return new Sk.builtin.str(ret);
        },
        sq$contains(ob) {
            if (!(ob instanceof Sk.builtin.str)) {
                throw new Sk.builtin.TypeError("'in <string>' requires string as left operand not " + Sk.abstr.typeName(ob));
            }
            return this.v.indexOf(ob.v) !== -1;
        },
        tp$as_number: true,
        nb$remainder: strBytesRemainder,
    },
    proto: /**@lends {Sk.builtin.str.prototype} */ {
        toString() {
            return this.v;
        },
        $subtype_new(args, kwargs) {
            const instance = new this.constructor();
            // we call str new method with all the args and kwargs
            const str_instance = Sk.builtin.str.prototype.tp$new(args, kwargs);
            instance.$mangled = str_instance.$mangled;
            instance.$savedKeyHash = str_instance.$savedKeyHash ;
            instance.v = str_instance.v;
            return instance;
        },
        $jsstr() {
            return this.v;
        },
        $hasAstralCodePoints() {
            // If a string has astral code points, we have to work out where they are before
            // we can do things like slicing, computing length, etc. We work this out when we need to.
            if (this.codepoints === null) {
                return false;
            } else if (this.codepoints !== undefined) {
                return true;
            }
            // Does this string contain astral code points? If so, we have to do things the slow way.
            for (let i = 0; i < this.v.length; i++) {
                let cc = this.v.charCodeAt(i);
                if (cc >= 0xd800 && cc < 0xe000) {
                    // Yep, it's a surrogate pair. Mark off the
                    // indices of all the code points for O(1) seeking later
                    this.codepoints = [];
                    for (let j = 0; j < this.v.length; j++) {
                        this.codepoints.push(j);
                        cc = this.v.charCodeAt(j);
                        if (cc >= 0xd800 && cc < 0xdc00) {
                            j++; // High surrogate. Skip next char
                        }
                    }
                    return true;
                }
            }
            this.codepoints = null;
            return false;
        },
        sk$asarray() {
            const ret = [];
            if (this.$hasAstralCodePoints()) {
                const codepoints = this.codepoints;
                for (let i = 0; i < codepoints.length; i++) {
                    ret.push(new Sk.builtin.str(this.v.substring(codepoints[i], codepoints[i + 1])));
                }
            } else {
                for (let i = 0; i < this.v.length; i++) {
                    ret.push(new Sk.builtin.str(this.v[i]));
                }
            }
            return ret;
        },
        find$left: mkFind(false),
        find$right: mkFind(true),
        get$tgt(tgt) {
            if (tgt instanceof Sk.builtin.str) {
                return tgt.v;
            }
            throw new Sk.builtin.TypeError("a str instance is required not '" + Sk.abstr.typeName(tgt) + "'");
        },
    },
    methods: /**@lends {Sk.builtin.str.prototype} */ {
        encode: {
            $meth: function encode(encoding, errors) {
                ({ encoding, errors } = Sk.builtin.bytes.check$encodeArgs("encode", encoding, errors));
                const pyBytes = Sk.builtin.bytes.str$encode(this, encoding, errors);
                return Sk.__future__.python3 ? pyBytes : new Sk.builtin.str(pyBytes.$jsstr());
            },
            $flags: { NamedArgs: ["encoding", "errors"] },
            $textsig: "($self, /, encoding='utf-8', errors='strict')",
            $doc:
                "Encode the string using the codec registered for encoding.\n\n  encoding\n    The encoding in which to encode the string.\n  errors\n    The error handling scheme to use for encoding errors.\n    The default is 'strict' meaning that encoding errors raise a\n    UnicodeEncodeError.  Other possible values are 'ignore', 'replace' and\n    'xmlcharrefreplace' as well as any other name registered with\n    codecs.register_error that can handle UnicodeEncodeErrors.",
        },
        replace: {
            $meth(oldS, newS, count) {
                oldS = this.get$tgt(oldS);
                newS = this.get$tgt(newS);
                count = count === undefined ? -1 : Sk.misceval.asIndexSized(count, Sk.builtin.OverflowError);
                const patt = new RegExp(re_escape_(oldS), "g");
                if (count < 0) {
                    return new Sk.builtin.str(this.v.replace(patt, newS));
                }
                let c = 0;
                const ret = this.v.replace(patt, (match) => (c++ < count ? newS : match));
                return new Sk.builtin.str(ret);
            },
            $flags: { MinArgs: 2, MaxArgs: 3 },
            $textsig: "($self, old, new, count=-1, /)",
            $doc:
                "Return a copy with all occurrences of substring old replaced by new.\n\n  count\n    Maximum number of occurrences to replace.\n    -1 (the default value) means replace all occurrences.\n\nIf the optional argument count is given, only the first count occurrences are\nreplaced.",
        },
        split: {
            $meth: function split(sep, maxsplit) {
                maxsplit = Sk.misceval.asIndexSized(maxsplit, Sk.builtin.OverflowError);
                const codepoints = splitPoints(this, sep, maxsplit);
                const ret = [];
                for (let i = 0; i < codepoints.length; i++) {
                    ret.push(new Sk.builtin.str(this.v.substring(codepoints[i], codepoints[++i])));
                }
                return new Sk.builtin.list(ret);
            },
            $flags: { NamedArgs: ["sep", "maxsplit"], Defaults: [Sk.builtin.none.none$, -1] },
            $textsig: "($self, /, sep=None, maxsplit=-1)",
            $doc:
                "Return a list of the words in the string, using sep as the delimiter string.\n\n  sep\n    The delimiter according which to split the string.\n    None (the default value) means split according to any whitespace,\n    and discard empty strings from the result.\n  maxsplit\n    Maximum number of splits to do.\n    -1 (the default value) means no limit.",
        },
        rsplit: {
            $meth: function rsplit(sep, maxsplit) {
                // do a full split and then slice the string accordingly;
                maxsplit = Sk.misceval.asIndexSized(maxsplit, Sk.builtin.OverflowError);
                const codepoints = splitPoints(this, sep, -1);
                let from = maxsplit < 0 ? 0 : (codepoints.length / 2 - maxsplit) * 2;
                const ret = [];
                if (from <= 0) {
                    from = 0;
                } else {
                    ret.push(new Sk.builtin.str(this.v.slice(0, codepoints[from - 1])));
                }
                for (let i = from; i < codepoints.length; i++) {
                    ret.push(new Sk.builtin.str(this.v.substring(codepoints[i], codepoints[++i])));
                }
                return new Sk.builtin.list(ret);
            },
            $flags: { NamedArgs: ["sep", "maxsplit"], Defaults: [Sk.builtin.none.none$, -1] },
            $textsig: "($self, /, sep=None, maxsplit=-1)",
            $doc:
                "Return a list of the words in the string, using sep as the delimiter string.\n\n  sep\n    The delimiter according which to split the string.\n    None (the default value) means split according to any whitespace,\n    and discard empty strings from the result.\n  maxsplit\n    Maximum number of splits to do.\n    -1 (the default value) means no limit.\n\nSplits are done starting at the end of the string and working to the front.",
        },
        join: {
            $meth(seq) {
                const arrOfStrs = [];
                return Sk.misceval.chain(
                    Sk.misceval.iterFor(Sk.abstr.iter(seq), (i) => {
                        if (!(i instanceof Sk.builtin.str)) {
                            throw new Sk.builtin.TypeError(
                                "sequence item " + arrOfStrs.length + ": expected str, " + Sk.abstr.typeName(i) + " found"
                            );
                        }
                        arrOfStrs.push(i.v);
                    }),
                    () => new Sk.builtin.str(arrOfStrs.join(this.v))
                );
            },
            $flags: { OneArg: true },
            $textsig: "($self, iterable, /)",
            $doc:
                "Concatenate any number of strings.\n\nThe string whose method is called is inserted in between each given string.\nThe result is returned as a new string.\n\nExample: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'",
        },
        capitalize: {
            $meth: function capitalize() {
                return new Sk.builtin.str(this.v.charAt(0).toUpperCase() + this.v.slice(1).toLowerCase());
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return a capitalized version of the string.\n\nMore specifically, make the first character have upper case and the rest lower\ncase.",
        },
        // casefold: {
        //     $meth: methods.casefold,
        //     $flags: { NoArgs: true },
        //     $textsig: "($self, /)",
        //     $doc: "Return a version of the string suitable for caseless comparisons.",
        // },
        title: {
            $meth: function title() {
                const ret = this.v.replace(/[a-z][a-z]*/gi, (str) => str[0].toUpperCase() + str.substr(1).toLowerCase());
                return new Sk.builtin.str(ret);
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return a version of the string where each word is titlecased.\n\nMore specifically, words start with uppercased characters and all remaining\ncased characters have lower case.",
        },
        center: {
            $meth: mkJust(false, true),
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: "($self, width, fillchar=' ', /)",
            $doc: "Return a centered string of length width.\n\nPadding is done using the specified fill character (default is a space).",
        },
        count: {
            $meth: function count(pat, start, end) {
                pat = this.get$tgt(pat);
                ({ start, end } = indices(this, start, end));
                if (end < start) {
                    return new Sk.builtin.int_(0);
                }
                const normaltext = pat.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
                const m = new RegExp(normaltext, "g");
                const slice = this.v.slice(start, end);
                const ctl = slice.match(m);
                if (!ctl) {
                    return new Sk.builtin.int_(0);
                } else {
                    return new Sk.builtin.int_(ctl.length);
                }
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "S.count(sub[, start[, end]]) -> int\n\nReturn the number of non-overlapping occurrences of substring sub in\nstring S[start:end].  Optional arguments start and end are\ninterpreted as in slice notation.",
        },
        expandtabs: {
            $meth: function expandtabs(tabsize) {
                if (Sk.builtin.checkInt(tabsize)) {
                    tabsize = Sk.builtin.asnum$(tabsize);
                } else {
                    throw new Sk.builtin.TypeError("an integer is required, got type" + Sk.abstr.typeName(tabsize));
                }
                const spaces = new Array(tabsize + 1).join(" ");
                const expanded = this.v.replace(/([^\r\n\t]*)\t/g, (a, b) => b + spaces.slice(b.length % tabsize));
                return new Sk.builtin.str(expanded);
            },
            $flags: { NamedArgs: ["tabsize"], Defaults: [8] },
            $textsig: "($self, /, tabsize=8)",
            $doc:
                "Return a copy where all tab characters are expanded using spaces.\n\nIf tabsize is not given, a tab size of 8 characters is assumed.",
        },
        find: {
            $meth: function find(tgt, start, end) {
                return new Sk.builtin.int_(this.find$left(tgt, start, end));
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "S.find(sub[, start[, end]]) -> int\n\nReturn the lowest index in S where substring sub is found,\nsuch that sub is contained within S[start:end].  Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.",
        },
        partition: {
            $meth: mkPartition(false),
            $flags: { OneArg: true },
            $textsig: "($self, sep, /)",
            $doc:
                "Partition the string into three parts using the given separator.\n\nThis will search for the separator in the string.  If the separator is found,\nreturns a 3-tuple containing the part before the separator, the separator\nitself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing the original string\nand two empty strings.",
        },
        index: {
            $meth: function index(tgt, start, end) {
                const val = this.find$left(tgt, start, end);
                if (val === -1) {
                    throw new Sk.builtin.ValueError("substring not found");
                } else {
                    return new Sk.builtin.int_(val);
                }
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "S.index(sub[, start[, end]]) -> int\n\nReturn the lowest index in S where substring sub is found, \nsuch that sub is contained within S[start:end].  Optional\narguments start and end are interpreted as in slice notation.\n\nRaises ValueError when the substring is not found.",
        },
        ljust: {
            $meth: mkJust(false, false),
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: "($self, width, fillchar=' ', /)",
            $doc: "Return a left-justified string of length width.\n\nPadding is done using the specified fill character (default is a space).",
        },
        lower: {
            $meth() {
                return new Sk.builtin.str(this.v.toLowerCase());
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Return a copy of the string converted to lowercase.",
        },
        lstrip: {
            $meth: mkStrip(/^\s+/g, (regex) => "^[" + regex + "]+"),
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, chars=None, /)",
            $doc:
                "Return a copy of the string with leading whitespace removed.\n\nIf chars is given and not None, remove characters in chars instead.",
        },
        rfind: {
            $meth(tgt, start, end) {
                return new Sk.builtin.int_(this.find$right(tgt, start, end));
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "S.rfind(sub[, start[, end]]) -> int\n\nReturn the highest index in S where substring sub is found,\nsuch that sub is contained within S[start:end].  Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.",
        },
        rindex: {
            $meth: function rindex(tgt, start, end) {
                const val = this.find$right(tgt, start, end);
                if (val === -1) {
                    throw new Sk.builtin.ValueError("substring not found");
                } else {
                    return new Sk.builtin.int_(val);
                }
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "S.rindex(sub[, start[, end]]) -> int\n\nReturn the highest index in S where substring sub is found,\nsuch that sub is contained within S[start:end].  Optional\narguments start and end are interpreted as in slice notation.\n\nRaises ValueError when the substring is not found.",
        },
        rjust: {
            $meth: mkJust(true, false),
            $flags: { MinArgs: 1, MaxArgs: 2 },
            $textsig: "($self, width, fillchar=' ', /)",
            $doc: "Return a right-justified string of length width.\n\nPadding is done using the specified fill character (default is a space).",
        },
        rstrip: {
            $meth: mkStrip(/\s+$/g, (regex) => "[" + regex + "]+$"),
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, chars=None, /)",
            $doc:
                "Return a copy of the string with trailing whitespace removed.\n\nIf chars is given and not None, remove characters in chars instead.",
        },
        rpartition: {
            $meth: mkPartition(true),
            $flags: { OneArg: true },
            $textsig: "($self, sep, /)",
            $doc:
                "Partition the string into three parts using the given separator.\n\nThis will search for the separator in the string, starting at the end. If\nthe separator is found, returns a 3-tuple containing the part before the\nseparator, the separator itself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing two empty strings\nand the original string.",
        },
        splitlines: {
            $meth: function splitlines(keepends) {
                keepends = Sk.misceval.isTrue(keepends);
                const data = this.v;
                const final = [];
                const len = data.length;
                let slice,
                    ch,
                    eol,
                    sol = 0;
                for (let i = 0; i < len; i++) {
                    ch = data.charAt(i);
                    if (data.charAt(i + 1) === "\n" && ch === "\r") {
                        eol = i + 2;
                        slice = data.slice(sol, eol);
                        if (!keepends) {
                            slice = slice.replace(/(\r|\n)/g, "");
                        }
                        final.push(new Sk.builtin.str(slice));
                        sol = eol;
                    } else if ((ch === "\n" && data.charAt(i - 1) !== "\r") || ch === "\r") {
                        eol = i + 1;
                        slice = data.slice(sol, eol);
                        if (!keepends) {
                            slice = slice.replace(/(\r|\n)/g, "");
                        }
                        final.push(new Sk.builtin.str(slice));
                        sol = eol;
                    }
                }
                if (sol < len) {
                    eol = len;
                    slice = data.slice(sol, eol);
                    if (!keepends) {
                        slice = slice.replace(/(\r|\n)/g, "");
                    }
                    final.push(new Sk.builtin.str(slice));
                }
                return new Sk.builtin.list(final);
            },
            $flags: { NamedArgs: ["keepends"], Defaults: [false] },
            $textsig: "($self, /, keepends=False)",
            $doc:
                "Return a list of the lines in the string, breaking at line boundaries.\n\nLine breaks are not included in the resulting list unless keepends is given and\ntrue.",
        },
        strip: {
            $meth: mkStrip(/^\s+|\s+$/g, (regex) => "^[" + regex + "]+|[" + regex + "]+$"),
            $flags: { MinArgs: 0, MaxArgs: 1 },
            $textsig: "($self, chars=None, /)",
            $doc:
                "Return a copy of the string with leading and trailing whitespace remove.\n\nIf chars is given and not None, remove characters in chars instead.",
        },
        swapcase: {
            $meth() {
                const ret = this.v.replace(/[a-z]/gi, (c) => {
                    const lc = c.toLowerCase();
                    return lc === c ? c.toUpperCase() : lc;
                });
                return new Sk.builtin.str(ret);
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Convert uppercase characters to lowercase and lowercase characters to uppercase.",
        },
        // translate: {
        //     $meth: methods.translate,
        //     $flags: {},
        //     $textsig: "($self, table, /)",
        //     $doc:
        //         "Replace each character in the string using the given translation table.\n\n  table\n    Translation table, which must be a mapping of Unicode ordinals to\n    Unicode ordinals, strings, or None.\n\nThe table must implement lookup/indexing via __getitem__, for instance a\ndictionary or list.  If this operation raises LookupError, the character is\nleft untouched.  Characters mapped to None are deleted.",
        // },
        upper: {
            $meth() {
                return new Sk.builtin.str(this.v.toUpperCase());
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: "Return a copy of the string converted to uppercase.",
        },
        startswith: {
            $meth: mkStartsEndswith("startswith", (substr, i) => substr.indexOf(i) === 0),
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "S.startswith(prefix[, start[, end]]) -> bool\n\nReturn True if S starts with the specified prefix, False otherwise.\nWith optional start, test S beginning at that position.\nWith optional end, stop comparing S at that position.\nprefix can also be a tuple of strings to try.",
        },
        endswith: {
            $meth: mkStartsEndswith("endswith", (substr, i) => substr.indexOf(i, substr.length - i.length) !== -1),
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: null,
            $doc:
                "S.endswith(suffix[, start[, end]]) -> bool\n\nReturn True if S ends with the specified suffix, False otherwise.\nWith optional start, test S beginning at that position.\nWith optional end, stop comparing S at that position.\nsuffix can also be a tuple of strings to try.",
        },
        isascii: {
            $meth() {
                return new Sk.builtin.bool(/^[\x00-\x7F]*$/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if all characters in the string are ASCII, False otherwise.\n\nASCII characters have code points in the range U+0000-U+007F.\nEmpty string is ASCII too.",
        },
        islower: {
            $meth: function islower() {
                return new Sk.builtin.bool(this.v.length && /[a-z]/.test(this.v) && !/[A-Z]/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is a lowercase string, False otherwise.\n\nA string is lowercase if all cased characters in the string are lowercase and\nthere is at least one cased character in the string.",
        },
        isupper: {
            $meth: function islower() {
                return new Sk.builtin.bool(this.v.length && !/[a-z]/.test(this.v) && /[A-Z]/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is an uppercase string, False otherwise.\n\nA string is uppercase if all cased characters in the string are uppercase and\nthere is at least one cased character in the string.",
        },
        istitle: {
            $meth: function istitle() {
                // Comparing to str.title() seems the most intuitive thing, but it fails on "",
                // Other empty-ish strings with no change.
                const input = this.v;
                let cased = false;
                let previous_is_cased = false;
                let ch;
                for (let pos = 0; pos < input.length; pos++) {
                    ch = input.charAt(pos);
                    if (!/[a-z]/.test(ch) && /[A-Z]/.test(ch)) {
                        if (previous_is_cased) {
                            return Sk.builtin.bool.false$;
                        }
                        previous_is_cased = true;
                        cased = true;
                    } else if (/[a-z]/.test(ch) && !/[A-Z]/.test(ch)) {
                        if (!previous_is_cased) {
                            return Sk.builtin.bool.false$;
                        }
                        cased = true;
                    } else {
                        previous_is_cased = false;
                    }
                }
                return new Sk.builtin.bool(cased);
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is a title-cased string, False otherwise.\n\nIn a title-cased string, upper- and title-case characters may only\nfollow uncased characters and lowercase characters only cased ones.",
        },
        isspace: {
            $meth: function isspace() {
                return new Sk.builtin.bool(/^\s+$/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is a whitespace string, False otherwise.\n\nA string is whitespace if all characters in the string are whitespace and there\nis at least one character in the string.",
        },
        // isdecimal: {
        //     $meth: methods.isdecimal,
        //     $flags: { NoArgs: true },
        //     $textsig: "($self, /)",
        //     $doc:
        //         "Return True if the string is a decimal string, False otherwise.\n\nA string is a decimal string if all characters in the string are decimal and\nthere is at least one character in the string.",
        // },
        isdigit: {
            $meth: function isdigit() {
                return new Sk.builtin.bool(/^\d+$/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is a digit string, False otherwise.\n\nA string is a digit string if all characters in the string are digits and there\nis at least one character in the string.",
        },
        isnumeric: {
            $meth: function isnumeric() {
                return new Sk.builtin.bool(this.v.length && !/[^0-9]/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is a numeric string, False otherwise.\n\nA string is numeric if all characters in the string are numeric and there is at\nleast one character in the string.",
        },
        isalpha: {
            $meth: function isalpha() {
                return new Sk.builtin.bool(this.v.length && !/[^a-zA-Z]/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is an alphabetic string, False otherwise.\n\nA string is alphabetic if all characters in the string are alphabetic and there\nis at least one character in the string.",
        },
        isalnum: {
            $meth: function isalnum() {
                return new Sk.builtin.bool(this.v.length && !/[^a-zA-Z0-9]/.test(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc:
                "Return True if the string is an alpha-numeric string, False otherwise.\n\nA string is alpha-numeric if all characters in the string are alpha-numeric and\nthere is at least one character in the string.",
        },
        // isidentifier: {
        //     $meth: methods.isidentifier,
        //     $flags: {},
        //     $textsig: "($self, /)",
        //     $doc:
        //         'Return True if the string is a valid Python identifier, False otherwise.\n\nUse keyword.iskeyword() to test for reserved identifiers such as "def" and\n"class".',
        // },
        // isprintable: {
        //     $meth: methods.isprintable,
        //     $flags: {},
        //     $textsig: "($self, /)",
        //     $doc:
        //         "Return True if the string is printable, False otherwise.\n\nA string is printable if all of its characters are considered printable in\nrepr() or if it is empty.",
        // },
        zfill: {
            $meth: function zfill(len) {
                len = Sk.misceval.asIndexSized(len, Sk.builtin.OverflowError);
                let pad = "";
                // figure out how many zeroes are needed to make the proper length
                const zeroes = len - this.v.length; // techinally this should sq$length ?
                // offset by 1 if there is a +/- at the beginning of the string
                const offset = this.v[0] === "+" || this.v[0] === "-" ? 1 : 0;
                for (let i = 0; i < zeroes; i++) {
                    pad += "0";
                }
                // combine the string and the zeroes
                return new Sk.builtin.str(this.v.substr(0, offset) + pad + this.v.substr(offset));
            },
            $flags: { OneArg: true },
            $textsig: "($self, width, /)",
            $doc: "Pad a numeric string with zeros on the left, to fill a field of the given width.\n\nThe string is never truncated.",
        },
        format: {
            $meth: Sk.formatting.format,
            $flags: { FastCall: true },
            $textsig: null,
            $doc:
                "S.format(*args, **kwargs) -> str\n\nReturn a formatted version of S, using substitutions from args and kwargs.\nThe substitutions are identified by braces ('{' and '}').",
        },
        // format_map: {
        //     $meth: methods.format_map,
        //     $flags: {},
        //     $textsig: null,
        //     $doc:
        //         "S.format_map(mapping) -> str\n\nReturn a formatted version of S, using substitutions from mapping.\nThe substitutions are identified by braces ('{' and '}').",
        // },
        __format__: {
            $meth: Sk.formatting.formatString,
            $flags: { OneArg: true },
            $textsig: "($self, format_spec, /)",
            $doc: "Return a formatted version of the string as described by format_spec.",
        },
        // __sizeof__: {
        //     $meth: methods.__sizeof__,
        //     $flags: {},
        //     $textsig: "($self, /)",
        //     $doc: "Return the size of the string in memory, in bytes.",
        // },
        __getnewargs__: {
            $meth() {
                return new Sk.builtin.tuple(new Sk.builtin.str(this.v));
            },
            $flags: { NoArgs: true },
            $textsig: null,
            $doc: null,
        },
    },
});

Sk.exportSymbol("Sk.builtin.str", Sk.builtin.str);

var re = /^[A-Za-z0-9]+$/;
function re_escape_(s) {
    let c;
    const ret = [];
    for (let i = 0; i < s.length; i++) {
        c = s.charAt(i);
        if (re.test(c)) {
            ret.push(c);
        } else {
            if (c === "\\000") {
                ret.push("\\000");
            } else {
                ret.push("\\" + c);
            }
        }
    }
    return ret.join("");
}

// methods
var special_chars = /([.*+?=|\\\/()\[\]\{\}^$])/g;
var leading_whitespace = /^[\s\xa0]+/;

function splitPoints(self, sep, maxsplit) {
    sep = Sk.builtin.checkNone(sep) ? null : self.get$tgt(sep);
    if (sep !== null && !sep.length) {
        throw new Sk.builtin.ValueError("empty separator");
    }
    let jsstr = self.v;
    let offset = 0;
    let regex;
    if (sep === null) {
        // Remove leading whitespace
        regex = /[\s\xa0]+/g;
        const len = jsstr.length;
        jsstr = jsstr.replace(leading_whitespace, "");
        offset = len - jsstr.length;
    } else {
        // Escape special characters in null so we can use a regexp
        const s = sep.replace(special_chars, "\\$1");
        regex = new RegExp(s, "g");
    }
    // This is almost identical to re.split,
    // except how the regexp is constructed
    const pairs = [];
    let index = 0;
    let splits = 0;
    let match;
    maxsplit = maxsplit < 0 ? Infinity : maxsplit;
    while ((match = regex.exec(jsstr)) != null && splits < maxsplit) {
        if (match.index === regex.lastIndex) {
            // empty match
            break;
        }
        pairs.push(index + offset);
        pairs.push(match.index + offset);
        index = regex.lastIndex;
        splits += 1;
    }
    if (sep !== null || jsstr.length - index) {
        pairs.push(index + offset);
        pairs.push(jsstr.length + offset);
    }
    return pairs;
}

function mkStrip(pat, regf) {
    return function strip(chars) {
        let pattern;
        if (chars === undefined || Sk.builtin.checkNone(chars)) {
            pattern = pat;
        } else if (chars instanceof Sk.builtin.str) {
            const regex = re_escape_(chars.v);
            pattern = new RegExp(regf(regex), "g");
        } else {
            throw new Sk.builtin.TypeError("strip arg must be None or str");
        }
        return new Sk.builtin.str(this.v.replace(pattern, ""));
    };
}

function mkPartition(isReversed) {
    return function partition(sep) {
        const sepStr = this.get$tgt(sep);
        const jsstr = this.v;
        let pos;
        if (isReversed) {
            pos = jsstr.lastIndexOf(sepStr);
            if (pos < 0) {
                return new Sk.builtin.tuple([new Sk.builtin.str(""), new Sk.builtin.str(""), new Sk.builtin.str(jsstr)]);
            }
        } else {
            pos = jsstr.indexOf(sepStr);
            if (pos < 0) {
                return new Sk.builtin.tuple([new Sk.builtin.str(jsstr), new Sk.builtin.str(""), new Sk.builtin.str("")]);
            }
        }

        return new Sk.builtin.tuple([
            new Sk.builtin.str(jsstr.substring(0, pos)),
            new Sk.builtin.str(sepStr),
            new Sk.builtin.str(jsstr.substring(pos + sepStr.length)),
        ]);
    };
}

function mkJust(isRight, isCenter) {
    return function strJustify(len, fillchar) {
        len = Sk.misceval.asIndexSized(len, Sk.builtin.OverflowError);
        if (fillchar === undefined) {
            fillchar = " ";
        } else if (!(fillchar instanceof Sk.builtin.str) || fillchar.sq$length() !== 1) {
            throw new Sk.builtin.TypeError("the fill character must be a str of length 1");
        } else {
            fillchar = fillchar.v;
        }

        const mylen = this.sq$length();
        let newstr;
        if (mylen >= len) {
            return new Sk.builtin.str(this.v);
        } else if (isCenter) {
            newstr = fillchar.repeat(Math.floor((len - mylen) / 2));
            newstr = newstr + this.v + newstr;

            if ((len - mylen) % 2) {
                newstr += fillchar;
            }

            return new Sk.builtin.str(newstr);
        } else {
            newstr = fillchar.repeat(len - mylen);
            return new Sk.builtin.str(isRight ? newstr + this.v : this.v + newstr);
        }
    };
}

function indices(self, start, end) {
    ({ start, end } = Sk.builtin.slice.startEnd$wrt(self, start, end));
    if (self.$hasAstralCodePoints()) {
        const tmp = self.codepoints[start];
        start = tmp === undefined ? start + self.v.length - self.codepoints.length : tmp;
        end = self.codepoints[end];
        end = end === undefined ? self.v.length : end;
    }
    return {
        start: start,
        end: end,
    };
}

function mkFind(isReversed) {
    return function (tgt, start, end) {
        tgt = this.get$tgt(tgt);

        ({ start, end } = indices(this, start, end));
        if (end < start) {
            return -1;
        }
        // ...do the search..
        end -= tgt.length;
        let jsidx = isReversed ? this.v.lastIndexOf(tgt, end) : this.v.indexOf(tgt, start);
        jsidx = jsidx >= start && jsidx <= end ? jsidx : -1;

        let idx;
        if (this.codepoints) {
            // ...and now convert them back
            const len = this.sq$length();
            idx = -1;
            for (let i = 0; i < len; i++) {
                if (jsidx == this.codepoints[i]) {
                    idx = i;
                }
            }
        } else {
            // No astral codepoints, no conversion required
            idx = jsidx;
        }
        return idx;
    };
}

function mkStartsEndswith(funcname, is_match) {
    return function (tgt, start, end) {
        if (!(tgt instanceof Sk.builtin.str) && !(tgt instanceof Sk.builtin.tuple)) {
            throw new Sk.builtin.TypeError(funcname + " first arg must be str or a tuple of str, not " + Sk.abstr.typeName(tgt));
        }

        ({ start, end } = indices(this, start, end));

        if (start > end) {
            return Sk.builtin.bool.false$;
        }

        const substr = this.v.slice(start, end);

        if (tgt instanceof Sk.builtin.tuple) {
            for (let it = Sk.abstr.iter(tgt), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
                if (!(i instanceof Sk.builtin.str)) {
                    throw new Sk.builtin.TypeError("tuple for " + funcname + " must only contain str, not " + Sk.abstr.typeName(i));
                }
                if (is_match(substr, i.v)) {
                    return Sk.builtin.bool.true$;
                }
            }
            return Sk.builtin.bool.false$;
        }
        return new Sk.builtin.bool(is_match(substr, tgt.v));
    };
}

Sk.builtin.str.$py2decode = new Sk.builtin.method_descriptor(Sk.builtin.str, {
    $name: "decode",
    $meth(encoding, errors) {
        const pyBytes = new Sk.builtin.bytes(this.v);
        return Sk.builtin.bytes.$decode.call(pyBytes, encoding, errors);
    },
    $flags: { NamedArgs: ["encoding", "errors"] },
});

function strBytesRemainder(rhs) {
    // % format op. rhs can be a value, a tuple, or something with __getitem__ (dict)

    // From http://docs.python.org/library/stdtypes.html#string-formatting the
    // format looks like:
    // 1. The '%' character, which marks the start of the specifier.
    // 2. Mapping key (optional), consisting of a parenthesised sequence of characters (for example, (somename)).
    // 3. Conversion flags (optional), which affect the result of some conversion types.
    // 4. Minimum field width (optional). If specified as an '*' (asterisk), the actual width is read from the next
    // element of the tuple in values, and the object to convert comes after the minimum field width and optional
    // precision. 5. Precision (optional), given as a '.' (dot) followed by the precision. If specified as '*' (an
    // asterisk), the actual width is read from the next element of the tuple in values, and the value to convert comes
    // after the precision. 6. Length modifier (optional). 7. Conversion type.  length modifier is ignored

    var ret;
    var replFunc;
    var index;
    var regex;
    var val;
    const strBytesConstructor = this.sk$builtinBase;
    // distinguish between bytes and str

    if (rhs.constructor !== Sk.builtin.tuple && !(rhs instanceof Sk.builtin.dict || rhs instanceof Sk.builtin.mappingproxy)) {
        rhs = new Sk.builtin.tuple([rhs]);
    }
    // general approach is to use a regex that matches the format above, and
    // do an re.sub with a function as replacement to make the subs.

    //           1 2222222222222222   33333333   444444444   5555555555555  66666  777777777777777777
    regex = /%(\([a-zA-Z0-9]+\))?([#0 +\-]+)?(\*|[0-9]+)?(\.(\*|[0-9]+))?[hlL]?([diouxXeEfFgGcrsb%])/g;
    index = 0;
    replFunc = function (substring, mappingKey, conversionFlags, fieldWidth, precision, precbody, conversionType) {
        var result;
        var convName;
        var convValue;
        var base;
        var r;
        var mk;
        var value;
        var handleWidth;
        var formatNumber;
        var alternateForm;
        var precedeWithSign;
        var blankBeforePositive;
        var leftAdjust;
        var zeroPad;
        var i;

        fieldWidth = Sk.builtin.asnum$(fieldWidth);
        precision = Sk.builtin.asnum$(precision);

        if ((mappingKey === undefined || mappingKey === "") && conversionType != "%") {
            i = index++;
        } // ff passes '' not undef for some reason

        if (precision === "") {
            // ff passes '' here aswell causing problems with G,g, etc.
            precision = undefined;
        }

        zeroPad = false;
        leftAdjust = false;
        blankBeforePositive = false;
        precedeWithSign = false;
        alternateForm = false;
        if (conversionFlags) {
            if (conversionFlags.indexOf("-") !== -1) {
                leftAdjust = true;
            } else if (conversionFlags.indexOf("0") !== -1) {
                zeroPad = true;
            }

            if (conversionFlags.indexOf("+") !== -1) {
                precedeWithSign = true;
            } else if (conversionFlags.indexOf(" ") !== -1) {
                blankBeforePositive = true;
            }

            alternateForm = conversionFlags.indexOf("#") !== -1;
        }

        if (precision) {
            precision = parseInt(precision.substr(1), 10);
        }

        formatNumber = function (n, base) {
            var precZeroPadded;
            var prefix;
            var didSign;
            var neg;
            var r;
            var j;
            base = Sk.builtin.asnum$(base);
            neg = false;
            didSign = false;
            if (typeof n === "number") {
                if (n < 0) {
                    n = -n;
                    neg = true;
                }
                r = n.toString(base);
            } else if (n instanceof Sk.builtin.float_) {
                r = n.str$(base, false);
                if (r.length > 2 && r.substr(-2) === ".0") {
                    r = r.substr(0, r.length - 2);
                }
                neg = n.nb$isnegative();
            } else if (n instanceof Sk.builtin.int_) {
                r = n.str$(base, false);
                neg = n.nb$isnegative();
            } else if (n instanceof Sk.builtin.lng) {
                r = n.str$(base, false);
                neg = n.nb$isnegative();
            }

            Sk.asserts.assert(r !== undefined, "unhandled number format");

            precZeroPadded = false;

            if (precision) {
                //print("r.length",r.length,"precision",precision);
                for (j = r.length; j < precision; ++j) {
                    r = "0" + r;
                    precZeroPadded = true;
                }
            }

            prefix = "";

            if (neg) {
                prefix = "-";
            } else if (precedeWithSign) {
                prefix = "+" + prefix;
            } else if (blankBeforePositive) {
                prefix = " " + prefix;
            }

            if (alternateForm) {
                if (base === 16) {
                    prefix += "0x";
                } else if (base === 8 && !precZeroPadded && r !== "0") {
                    prefix += "0";
                }
            }

            return [prefix, r];
        };

        handleWidth = function (args) {
            var totLen;
            var prefix = args[0];
            var r = args[1];
            var j;

            if (fieldWidth) {
                fieldWidth = parseInt(fieldWidth, 10);
                totLen = r.length + prefix.length;
                if (zeroPad) {
                    for (j = totLen; j < fieldWidth; ++j) {
                        r = "0" + r;
                    }
                } else if (leftAdjust) {
                    for (j = totLen; j < fieldWidth; ++j) {
                        r = r + " ";
                    }
                    if (Sk.__future__.python3) {
                        r += prefix;
                        prefix = "";
                    }
                } else {
                    for (j = totLen; j < fieldWidth; ++j) {
                        prefix = " " + prefix;
                    }
                }
            }
            return prefix + r;
        };
        //print("Rhs:",rhs, "ctor", rhs.constructor);
        if (rhs.constructor === Sk.builtin.tuple) {
            value = rhs.v[i];
        } else if (rhs.mp$subscript !== undefined && mappingKey !== undefined) {
            mk = mappingKey.substring(1, mappingKey.length - 1);
            //print("mk",mk);
            value = rhs.mp$subscript(new strBytesConstructor(mk));
        } else if (rhs.constructor === Sk.builtin.dict || rhs.constructor === Sk.builtin.list) {
            // new case where only one argument is provided
            value = rhs;
        } else {
            throw new Sk.builtin.AttributeError(rhs.tp$name + " instance has no attribute 'mp$subscript'");
        }
        base = 10;
        if (conversionType === "d" || conversionType === "i") {
            let tmpData = formatNumber(value, base);
            if (tmpData[1] === undefined) {
                throw new Sk.builtin.TypeError("%" + conversionType + " format: a number is required, not " + Sk.abstr.typeName(value));
            }
            let r = tmpData[1];
            tmpData[1] = r.indexOf(".") !== -1 ? parseInt(r, 10).toString() : r;
            return handleWidth(tmpData);
        } else if (conversionType === "o") {
            return handleWidth(formatNumber(value, 8));
        } else if (conversionType === "x") {
            return handleWidth(formatNumber(value, 16));
        } else if (conversionType === "X") {
            return handleWidth(formatNumber(value, 16)).toUpperCase();
        } else if (
            conversionType === "f" ||
            conversionType === "F" ||
            conversionType === "e" ||
            conversionType === "E" ||
            conversionType === "g" ||
            conversionType === "G"
        ) {
            convValue = Sk.builtin.asnum$(value);
            if (typeof convValue === "string") {
                convValue = Number(convValue);
            }
            if (convValue === Infinity) {
                return "inf";
            }
            if (convValue === -Infinity) {
                return "-inf";
            }
            if (isNaN(convValue)) {
                return "nan";
            }
            convName = ["toExponential", "toFixed", "toPrecision"]["efg".indexOf(conversionType.toLowerCase())];
            if (precision === undefined || precision === "") {
                if (conversionType === "e" || conversionType === "E") {
                    precision = 6;
                } else if (conversionType === "f" || conversionType === "F") {
                    if (Sk.__future__.python3) {
                        precision = 6;
                    } else {
                        precision = 7;
                    }
                }
            }
            result = convValue[convName](precision); // possible loose of negative zero sign

            // apply sign to negative zeros, floats only!
            if (Sk.builtin.checkFloat(value)) {
                if (convValue === 0 && 1 / convValue === -Infinity) {
                    result = "-" + result; // add sign for zero
                }
            }
            if (Sk.__future__.python3) {
                if (result.length >= 7 && result.slice(0, 6) == "0.0000") {
                    val = parseFloat(result);
                    result = val.toExponential();
                }
                if (result.charAt(result.length - 2) == "-") {
                    result = result.slice(0, result.length - 1) + "0" + result.charAt(result.length - 1);
                }
            }
            if ("EFG".indexOf(conversionType) !== -1) {
                result = result.toUpperCase();
            }
            return handleWidth(["", result]);
        } else if (conversionType === "c") {
            if (typeof value === "number") {
                return String.fromCharCode(value);
            } else if (value instanceof Sk.builtin.int_) {
                return String.fromCharCode(value.v);
            } else if (value instanceof Sk.builtin.float_) {
                return String.fromCharCode(value.v);
            } else if (value instanceof Sk.builtin.lng) {
                return String.fromCharCode(value.str$(10, false)[0]);
            } else if (value.constructor === Sk.builtin.str) {
                return value.v.substr(0, 1);
            } else {
                throw new Sk.builtin.TypeError("an integer is required");
            }
        } else if (conversionType === "r") {
            r = Sk.builtin.repr(value);
            if (precision) {
                return r.v.substr(0, precision);
            }
            return r.v;
        } else if (conversionType === "s" && strBytesConstructor === Sk.builtin.str) {
            r = new Sk.builtin.str(value);
            r = r.$jsstr();
            if (precision) {
                return r.substr(0, precision);
            }
            if (fieldWidth) {
                r = handleWidth([" ", r]);
            }
            return r;
        } else if (conversionType === "b" || conversionType === "s") {
            if (strBytesConstructor === Sk.builtin.str) {
                throw new Sk.builtin.ValueError("unsupported format character 'b'");
            }
            let func;
            if (!(value instanceof Sk.builtin.bytes) && (func = Sk.abstr.lookupSpecial(value, Sk.builtin.str.$bytes)) === undefined) {
                throw new Sk.builtin.TypeError("%b requires a bytes-like object, or an object that implements __bytes__, not '" + Sk.abstr.typeName(value) + "'");
            }
            if (func !== undefined) {
                value = new Sk.builtin.bytes(value);
                // raises the appropriate error message if __bytes__ does not return bytes
            }
            r = value.$jsstr();
            if (precision) {
                return r.substr(0, precision);
            }
            if (fieldWidth) {
                r = handleWidth([" ", r]);
            }
            return r;
        } else if (conversionType === "%") {
            return "%";
        }
    };
    ret = this.$jsstr().replace(regex, replFunc);
    return new strBytesConstructor(ret);
};

/**
 * @constructor
 * @param {Object} obj
 */
var str_iter_ = Sk.abstr.buildIteratorClass("str_iterator", {
    constructor: function str_iter_(str) {
        this.$index = 0;
        if (str.$hasAstralCodePoints()) {
            this.$seq = str.codepoints;
            this.tp$iternext = () => {
                const i = this.$seq[this.$index];
                if (i === undefined) {
                    return undefined;
                }
                return new Sk.builtin.str(str.v.substring(i, this.$seq[++this.$index]));
            };
        } else {
            this.$seq = str.v;
            this.tp$iternext = () => {
                const ch = this.$seq[this.$index++];
                if (ch === undefined) {
                    return undefined;
                }
                return new Sk.builtin.str(ch);
            };
        }
    },
    iternext() {
        return this.tp$iternext();
    },
    methods: {
        __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});

var reservedWords_ = {
    abstract: true,
    as: true,
    boolean: true,
    break: true,
    byte: true,
    case: true,
    catch: true,
    char: true,
    class: true,
    continue: true,
    const: true,
    debugger: true,
    default: true,
    delete: true,
    do: true,
    double: true,
    else: true,
    enum: true,
    export: true,
    extends: true,
    false: true,
    final: true,
    finally: true,
    float: true,
    for: true,
    function: true,
    goto: true,
    if: true,
    implements: true,
    import: true,
    in: true,
    instanceof: true,
    int: true,
    interface: true,
    is: true,
    long: true,
    namespace: true,
    native: true,
    new: true,
    null: true,
    package: true,
    private: true,
    protected: true,
    public: true,
    return: true,
    short: true,
    static: true,
    super: true,
    switch: true,
    synchronized: true,
    this: true,
    throw: true,
    throws: true,
    transient: true,
    true: true,
    try: true,
    typeof: true,
    use: true,
    var: true,
    void: true,
    volatile: true,
    while: true,
    with: true,
    // reserved Names
    constructor: true,
    __defineGetter__: true,
    __defineSetter__: true,
    apply: true,
    arguments: true,
    call: true,
    caller: true,
    eval: true,
    hasOwnProperty: true,
    isPrototypeOf: true,
    __lookupGetter__: true,
    __lookupSetter__: true,
    __noSuchMethod__: true,
    propertyIsEnumerable: true,
    prototype: true,
    toSource: true,
    toLocaleString: true,
    toString: true,
    unwatch: true,
    valueOf: true,
    watch: true,
    length: true,
    name: true,
};

function fixReserved(name) {
    if (reservedWords_[name] === undefined) {
        return name;
    }
    return name + "_$rw$";
}

Sk.builtin.str.reservedWords_ = reservedWords_;


/***/ }),

/***/ "./src/structseq.js":
/*!**************************!*\
  !*** ./src/structseq.js ***!
  \**************************/
/*! no static exports found */
/***/ (function(module, exports) {

Sk.builtin.structseq_types = {};

Sk.builtin.make_structseq = function (module, name, fields, doc) {
    const nm = module + "." + name;
    const flds = [];
    const docs = [];
    for (let key in fields) {
        flds.push(key);
        docs.push(fields[key]);
    }

    const getsets = {};
    for (let i = 0; i < flds.length; i++) {
        getsets[flds[i]] = {
            $get() { return this.v[i]; },
            $doc: docs[i]
        };
    }

    /**
     * @constructor
     * @extends Sk.builtin.tuple
     * @param {!Array<Object>|Object} arg
     */
    var structseq = Sk.abstr.buildNativeClass(nm, {
        constructor: function structseq_constructor(v) {
            Sk.asserts.assert((Array.isArray(v) || v === undefined) && this instanceof structseq);
            Sk.builtin.tuple.call(this, v);
        },
        base: Sk.builtin.tuple,
        slots: {
            tp$new(args, kwargs) {
                Sk.abstr.checkOneArg(nm, args, kwargs);
                const v = [];
                const arg = args[0];
                for (let it = Sk.abstr.iter(arg), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
                    v.push(i);
                }
                if (v.length != flds.length) {
                    throw new Sk.builtin.TypeError(nm + "() takes a " + flds.length + "-sequence (" + v.length + "-sequence given)");
                }
                return new structseq(v);
            },
            tp$doc: doc ? doc : Sk.builtin.none.none$,
            $r() {
                var ret;
                var i;
                var bits;
                if (this.v.length === 0) {
                    return new Sk.builtin.str(nm + "()");
                }
                bits = [];
                for (i = 0; i < this.v.length; ++i) {
                    bits[i] = flds[i] + "=" + Sk.misceval.objectRepr(this.v[i]);
                }
                ret = bits.join(", ");
                if (this.v.length === 1) {
                    ret += ",";
                }
                return new Sk.builtin.str(nm + "(" + ret + ")");
            },

        },
        methods: {
            __reduce__: {
                $meth() {
                    throw new Sk.builtin.NotImplementedError("__reduce__ is not implemented");
                },
                $flags: { NoArgs: true }
            }
        },
        getsets: getsets,
        proto: {
            num_sequence_fields: new Sk.builtin.int_(flds.length)
        }
    });
    return structseq;
};
Sk.exportSymbol("Sk.builtin.make_structseq", Sk.builtin.make_structseq);


/***/ }),

/***/ "./src/super.js":
/*!**********************!*\
  !*** ./src/super.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * Sk.builtin.super_
 */
Sk.builtin.super_ = Sk.abstr.buildNativeClass("super", {
    constructor: function super_(a_type, other_self) {
        // internally we never use this method
        Sk.asserts.assert(this instanceof Sk.builtin.super_, "bad call to super, use 'new'");
        // internal calls can use this method but it 
        this.type = a_type;
        this.obj = other_self;
        if (a_type !== undefined) {
            if (!Sk.builtin.checkClass(a_type)) {
                throw new Sk.builtin.TypeError("must be type, not " + Sk.abstr.typeName(a_type));
            }
        }
        if (this.obj !== undefined) {
            this.obj_type = this.$supercheck(a_type, this.obj);
        } else {
            this.obj_type = null;
        }
    },
    slots: {
        tp$doc:
            "super() -> same as super(__class__, <first argument>)\n" +
            "super(type) -> unbound super object\nsuper(type, obj) -> bound super object; requires isinstance(obj, type)\n"+
            "super(type, type2) -> bound super object; requires issubclass(type2, type)\n"+
            "Typical use to call a cooperative superclass method:\n"+
            "class C(B):\n    def meth(self, arg):\n        super().meth(arg)\nThis works for class methods too:\nclass C(B):\n    @classmethod\n    def cmeth(cls, arg):\n        super().cmeth(arg)\n",
        tp$new: Sk.generic.new,
        tp$init(args, kwargs) {
            Sk.abstr.checkNoKwargs("super", kwargs);
            Sk.abstr.checkArgsLen("super", args, 1, 2);
            const a_type = args[0];
            const other_self = args[1];
            if (!Sk.builtin.checkClass(a_type)) {
                throw new Sk.builtin.TypeError("must be type, not " + Sk.abstr.typeName(a_type));
            }
            this.obj = other_self;
            this.type = a_type;
            if (this.obj != null) {
                this.obj_type = this.$supercheck(a_type, this.obj);
            }
        },
        $r() {
            if (this.obj) {
                return new Sk.builtin.str("<super: <class '" + this.type.prototype.tp$name + "'>, <" + Sk.abstr.typeName(this.obj) + " object>>");
            }
            return new Sk.builtin.str("<super: <class '" + this.type.prototype.tp$name + "'>, NULL>");
        },
        tp$getattr(pyName, canSuspend) {
            let starttype = this.obj_type;
            if (starttype == null) {
                return Sk.generic.getAttr.call(this, pyName, canSuspend);
            }
            const mro = starttype.prototype.tp$mro;
            const n = mro.length;
            /* We want __class__ to return the class of the super object
            (i.e. super, or a subclass), not the class of su->obj. */
            if (pyName === Sk.builtin.str.$class) {
                return Sk.generic.getAttr.call(this, pyName, canSuspend);
            }
            /* No need to check the last one: it's gonna be skipped anyway.  */
            let i;
            for (i = 0; i + 1 < n; i++) {
                if (this.type === mro[i]) {
                    break;
                }
            }
            i++;
            if (i >= n) {
                return Sk.generic.getAttr.call(this, pyName, canSuspend);
            }
            const jsName = pyName.$mangled;

            let tmp, res;
            while (i < n) {
                tmp = mro[i].prototype;
                if (tmp.hasOwnProperty(jsName)) {
                    res = tmp[jsName];
                }

                if (res !== undefined) {
                    const f = res.tp$descr_get;
                    if (f !== undefined) {
                        /* Only pass 'obj' param if this is instance-mode super
                               (See SF ID #743627)  */
                        res = f.call(res, this.obj === starttype ? null : this.obj, starttype);
                    }
                    return res;
                }
                i++;
            }
        },
        tp$descr_get(obj, obtype) {
            if (obj === null || this.obj != null) {
                return this;
            }
            if (this.ob$type !== Sk.builtin.super_) {
                /* If su is an instance of a (strict) subclass of super,
                call its type */
                return Sk.misceval.callsimOrSuspendArray(this.ob$type, [this.type, obj]);
            } else {
                /* Inline the common case */
                const obj_type = this.$supercheck(this.type, obj);
                const newobj = new Sk.builtin.super_();
                newobj.type = this.type;
                newobj.obj = obj;
                newobj.obj_type = obj_type;
                return newobj;
            }
        },
    },
    getsets: {
        __thisclass__: {
            $get() {
                return this.type;
            },
            $doc: "the class invoking super()",
        },
        __self__: {
            $get() {
                return this.obj || Sk.builtin.none.none$;
            },
            $doc: "the instance invoking super(); may be None",
        },
        __self_class__: {
            $get() {
                return this.obj_type || Sk.builtin.none.none$;
            },
            $doc: "the type of the instance invoking super(); may be None",
        },
    },
    proto: {
        $supercheck(type, obj) {
            /* Check that a super() call makes sense.  Return a type object.

            obj can be a class, or an instance of one:

            - If it is a class, it must be a subclass of 'type'.      This case is
                used for class methods; the return value is obj.

            - If it is an instance, it must be an instance of 'type'.  This is
                the normal case; the return value is obj.__class__.

            /* Check for first bullet above (special case) */
            if (Sk.builtin.checkClass(obj) && obj.$isSubType(type)) {
                return obj;
            }
            /* Normal case */
            if (obj.ob$type.$isSubType(type)) {
                return obj.ob$type;
            } else {
                /* Try the slow way */
                const class_attr = obj.tp$getattr(Sk.builtin.str.$class);
                if (class_attr !== undefined && class_attr !== obj.ob$type && Sk.builtin.checkClass(class_attr)) {
                    if (class_attr.$isSubType(type)) {
                        return class_attr;
                    }
                }
            }
            throw new Sk.builtin.TypeError("super(type, obj): obj must be an instance or subtype of type");
        },
    },
});

/***/ }),

/***/ "./src/symtable.js":
/*!*************************!*\
  !*** ./src/symtable.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* Flags for def-use information */

var DEF_GLOBAL = 1;
/* global stmt */
var DEF_LOCAL = 2;
/* assignment in code block */
var DEF_PARAM = 2 << 1;
/* formal parameter */
var USE = 2 << 2;
/* name is used */
var DEF_STAR = 2 << 3;
/* parameter is star arg */
var DEF_DOUBLESTAR = 2 << 4;
/* parameter is star-star arg */
var DEF_INTUPLE = 2 << 5;
/* name defined in tuple in parameters */
var DEF_FREE = 2 << 6;
/* name used but not defined in nested block */
var DEF_FREE_GLOBAL = 2 << 7;
/* free variable is actually implicit global */
var DEF_FREE_CLASS = 2 << 8;
/* free variable from class's method */
var DEF_IMPORT = 2 << 9;
/* assignment occurred via import */
var DEF_NONLOCAL = 2 << 10;
/* nonlocal stmt */
var DEF_ANNOT = 2 << 11;
/* this name is annotated */

var DEF_BOUND = (DEF_LOCAL | DEF_PARAM | DEF_IMPORT);

/* GLOBAL_EXPLICIT and GLOBAL_IMPLICIT are used internally by the symbol
 table.  GLOBAL is returned from PyST_GetScope() for either of them.
 It is stored in ste_symbols at bits 12-14.
 */
var SCOPE_OFF = 11;
var SCOPE_MASK = 7;

var LOCAL = 1;
var GLOBAL_EXPLICIT = 2;
var GLOBAL_IMPLICIT = 3;
var FREE = 4;
var CELL = 5;

/* The following three names are used for the ste_unoptimized bit field */
var OPT_IMPORT_STAR = 1;
var OPT_EXEC = 2;
var OPT_BARE_EXEC = 4;
var OPT_TOPLEVEL = 8;
/* top-level names, including eval and exec */

var GENERATOR = 2;
var GENERATOR_EXPRESSION = 2;

var ModuleBlock = "module";
var FunctionBlock = "function";
var ClassBlock = "class";

var SYMTAB_CONSTS = {
    DEF_GLOBAL: DEF_GLOBAL,
    DEF_LOCAL: DEF_LOCAL,
    DEF_PARAM: DEF_PARAM,
    USE: USE,
    DEF_STAR: DEF_STAR,
    DEF_DOUBLESTAR: DEF_DOUBLESTAR,
    DEF_INTUPLE: DEF_INTUPLE,
    DEF_FREE: DEF_FREE,
    DEF_FREE_GLOBAL: DEF_FREE_GLOBAL,
    DEF_FREE_CLASS: DEF_FREE_CLASS,
    DEF_IMPORT: DEF_IMPORT,
    DEF_BOUND: DEF_BOUND,
    SCOPE_OFF: SCOPE_OFF,
    SCOPE_MASK: SCOPE_MASK,
    LOCAL: LOCAL,
    GLOBAL_EXPLICIT: GLOBAL_EXPLICIT,
    GLOBAL_IMPLICIT: GLOBAL_IMPLICIT,
    FREE: FREE,
    CELL: CELL,
    OPT_IMPORT_STAR: OPT_IMPORT_STAR,
    OPT_EXEC: OPT_EXEC,
    OPT_BARE_EXEC: OPT_BARE_EXEC,
    OPT_TOPLEVEL: OPT_TOPLEVEL,
    GENERATOR: GENERATOR,
    GENERATOR_EXPRESSION: GENERATOR_EXPRESSION,
    ModuleBlock: ModuleBlock,
    FunctionBlock: FunctionBlock,
    ClassBlock: ClassBlock
};

Sk.exportSymbol("Sk.SYMTAB_CONSTS", SYMTAB_CONSTS);

/**
 * @constructor
 * @param {string} name
 * @param {number} flags
 * @param {Array.<SymbolTableScope>} namespaces
 */
function Symbol_ (name, flags, namespaces) {
    this.__name = name;
    this.__flags = flags;
    this.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK;
    this.__namespaces = namespaces || [];
}
Symbol_.prototype.get_name = function () {
    return this.__name;
};
Symbol_.prototype.is_referenced = function () {
    return !!(this.__flags & USE);
};
Symbol_.prototype.is_parameter = function () {
    return !!(this.__flags & DEF_PARAM);
};
Symbol_.prototype.is_global = function () {
    return this.__scope === GLOBAL_IMPLICIT || this.__scope == GLOBAL_EXPLICIT;
};
Symbol_.prototype.is_declared_global = function () {
    return this.__scope == GLOBAL_EXPLICIT;
};
Symbol_.prototype.is_local = function () {
    return !!(this.__flags & DEF_BOUND);
};
Symbol_.prototype.is_free = function () {
    return this.__scope == FREE;
};
Symbol_.prototype.is_imported = function () {
    return !!(this.__flags & DEF_IMPORT);
};
Symbol_.prototype.is_assigned = function () {
    return !!(this.__flags & DEF_LOCAL);
};
Symbol_.prototype.is_namespace = function () {
    return this.__namespaces && this.__namespaces.length > 0;
};
Symbol_.prototype.get_namespaces = function () {
    return this.__namespaces;
};

var astScopeCounter = 0;

/**
 * @constructor
 * @param {SymbolTable} table
 * @param {string} name
 * @param {string} type
 * @param {number} lineno
 */
function SymbolTableScope (table, name, type, ast, lineno) {
    this.symFlags = {};
    this.name = name;
    this.varnames = [];
    this.children = [];
    this.blockType = type;

    this.isNested = false;
    this.hasFree = false;
    this.childHasFree = false;  // true if child block has free vars including free refs to globals
    this.generator = false;
    this.varargs = false;
    this.varkeywords = false;
    this.returnsValue = false;

    this.lineno = lineno;

    this.table = table;

    if (table.cur && (table.cur.nested || table.cur.blockType === FunctionBlock)) {
        this.isNested = true;
    }

    ast.scopeId = astScopeCounter++;
    table.stss[ast.scopeId] = this;

    // cache of Symbols for returning to other parts of code
    this.symbols = {};
}
SymbolTableScope.prototype.get_type = function () {
    return this.blockType;
};
SymbolTableScope.prototype.get_name = function () {
    return this.name;
};
SymbolTableScope.prototype.get_lineno = function () {
    return this.lineno;
};
SymbolTableScope.prototype.is_nested = function () {
    return this.isNested;
};
SymbolTableScope.prototype.has_children = function () {
    return this.children.length > 0;
};
SymbolTableScope.prototype.get_identifiers = function () {
    return this._identsMatching(function () {
        return true;
    });
};
SymbolTableScope.prototype.lookup = function (name) {
    var namespaces;
    var flags;
    var sym;
    if (!this.symbols.hasOwnProperty(name)) {
        flags = this.symFlags[name];
        namespaces = this.__check_children(name);
        sym = this.symbols[name] = new Symbol_(name, flags, namespaces);
    }
    else {
        sym = this.symbols[name];
    }
    return sym;
};
SymbolTableScope.prototype.__check_children = function (name) {
    //print("  check_children:", name);
    var child;
    var i;
    var ret = [];
    for (i = 0; i < this.children.length; ++i) {
        child = this.children[i];
        if (child.name === name) {
            ret.push(child);
        }
    }
    return ret;
};

SymbolTableScope.prototype._identsMatching = function (f) {
    var k;
    var ret = [];
    for (k in this.symFlags) {
        if (this.symFlags.hasOwnProperty(k)) {
            if (f(this.symFlags[k])) {
                ret.push(k);
            }
        }
    }
    ret.sort();
    return ret;
};
SymbolTableScope.prototype.get_parameters = function () {
    Sk.asserts.assert(this.get_type() == "function", "get_parameters only valid for function scopes");
    if (!this._funcParams) {
        this._funcParams = this._identsMatching(function (x) {
            return x & DEF_PARAM;
        });
    }
    return this._funcParams;
};
SymbolTableScope.prototype.get_locals = function () {
    Sk.asserts.assert(this.get_type() == "function", "get_locals only valid for function scopes");
    if (!this._funcLocals) {
        this._funcLocals = this._identsMatching(function (x) {
            return x & DEF_BOUND;
        });
    }
    return this._funcLocals;
};
SymbolTableScope.prototype.get_globals = function () {
    Sk.asserts.assert(this.get_type() == "function", "get_globals only valid for function scopes");
    if (!this._funcGlobals) {
        this._funcGlobals = this._identsMatching(function (x) {
            var masked = (x >> SCOPE_OFF) & SCOPE_MASK;
            return masked == GLOBAL_IMPLICIT || masked == GLOBAL_EXPLICIT;
        });
    }
    return this._funcGlobals;
};
SymbolTableScope.prototype.get_frees = function () {
    Sk.asserts.assert(this.get_type() == "function", "get_frees only valid for function scopes");
    if (!this._funcFrees) {
        this._funcFrees = this._identsMatching(function (x) {
            var masked = (x >> SCOPE_OFF) & SCOPE_MASK;
            return masked == FREE;
        });
    }
    return this._funcFrees;
};
SymbolTableScope.prototype.get_methods = function () {
    var i;
    var all;
    Sk.asserts.assert(this.get_type() == "class", "get_methods only valid for class scopes");
    if (!this._classMethods) {
        // todo; uniq?
        all = [];
        for (i = 0; i < this.children.length; ++i) {
            all.push(this.children[i].name);
        }
        all.sort();
        this._classMethods = all;
    }
    return this._classMethods;
};
SymbolTableScope.prototype.getScope = function (name) {
    //print("getScope");
    //for (var k in this.symFlags) print(k);
    var v = this.symFlags[name];
    if (v === undefined) {
        return 0;
    }
    return (v >> SCOPE_OFF) & SCOPE_MASK;
};

/**
 * @constructor
 * @param {string} filename
 */
function SymbolTable (filename) {
    this.filename = filename;
    this.cur = null;
    this.top = null;
    this.stack = [];
    this.global = null; // points at top level module symFlags
    this.curClass = null; // current class or null
    this.tmpname = 0;

    // mapping from ast nodes to their scope if they have one. we add an
    // id to the ast node when a scope is created for it, and store it in
    // here for the compiler to lookup later.
    this.stss = {};
}
SymbolTable.prototype.getStsForAst = function (ast) {
    var v;
    Sk.asserts.assert(ast.scopeId !== undefined, "ast wasn't added to st?");
    v = this.stss[ast.scopeId];
    Sk.asserts.assert(v !== undefined, "unknown sym tab entry");
    return v;
};

SymbolTable.prototype.SEQStmt = function (nodes) {
    var val;
    var i;
    var len;
    if (nodes !== null) {
        Sk.asserts.assert(Sk.isArrayLike(nodes), "SEQ: nodes isn't array? got " + nodes.toString());
        len = nodes.length;
        for (i = 0; i < len; ++i) {
            val = nodes[i];
            if (val) {
                this.visitStmt(val);
            }
        }
    }
};

SymbolTable.prototype.SEQExpr = function (nodes) {
    var val;
    var i;
    var len;
    if (nodes !== null) {
        Sk.asserts.assert(Sk.isArrayLike(nodes), "SEQ: nodes isn't array? got " + nodes.toString());
        len = nodes.length;
        for (i = 0; i < len; ++i) {
            val = nodes[i];
            if (val) {
                this.visitExpr(val);
            }
        }
    }
};

SymbolTable.prototype.enterBlock = function (name, blockType, ast, lineno) {
    var prev;
    name = Sk.fixReserved(name);
    //print("enterBlock:", name);
    prev = null;
    if (this.cur) {
        prev = this.cur;
        this.stack.push(this.cur);
    }
    this.cur = new SymbolTableScope(this, name, blockType, ast, lineno);
    if (name === "top") {
        this.global = this.cur.symFlags;
    }
    if (prev) {
        //print("    adding", this.cur.name, "to", prev.name);
        prev.children.push(this.cur);
    }
};

SymbolTable.prototype.exitBlock = function () {
    //print("exitBlock");
    this.cur = null;
    if (this.stack.length > 0) {
        this.cur = this.stack.pop();
    }
};

SymbolTable.prototype.visitParams = function (args, toplevel) {
    var arg;
    var i;
    for (i = 0; i < args.length; ++i) {
        arg = args[i];
        if (arg.constructor === Sk.astnodes.arg) {
            // TODO arguments are more complicated in Python 3...
            this.addDef(arg.arg, DEF_PARAM, arg.lineno);
        }
        else {
            // Tuple isn't supported
            throw new Sk.builtin.SyntaxError("invalid expression in parameter list", this.filename);
        }
    }
};

SymbolTable.prototype.visitArguments = function (a, lineno) {
    if (a.args) {
        this.visitParams(a.args, true);
    }
    if (a.kwonlyargs) {
        this.visitParams(a.kwonlyargs, true);
    }
    if (a.vararg) {
        this.addDef(a.vararg.arg, DEF_PARAM, lineno);
        this.cur.varargs = true;
    }
    if (a.kwarg) {
        this.addDef(a.kwarg.arg, DEF_PARAM, lineno);
        this.cur.varkeywords = true;
    }
};

SymbolTable.prototype.newTmpname = function (lineno) {
    this.addDef(new Sk.builtin.str("_[" + (++this.tmpname) + "]"), DEF_LOCAL, lineno);
};

SymbolTable.prototype.addDef = function (name, flag, lineno) {
    var fromGlobal;
    var val;
    var mangled = Sk.mangleName(this.curClass, name).v;
    mangled = Sk.fixReserved(mangled);
    val = this.cur.symFlags[mangled];
    if (val !== undefined) {
        if ((flag & DEF_PARAM) && (val & DEF_PARAM)) {
            throw new Sk.builtin.SyntaxError("duplicate argument '" + name.v + "' in function definition", this.filename, lineno);
        }
        val |= flag;
    }
    else {
        val = flag;
    }
    this.cur.symFlags[mangled] = val;
    if (flag & DEF_PARAM) {
        this.cur.varnames.push(mangled);
    }
    else if (flag & DEF_GLOBAL) {
        val = flag;
        fromGlobal = this.global[mangled];
        if (fromGlobal !== undefined) {
            val |= fromGlobal;
        }
        this.global[mangled] = val;
    }
};

SymbolTable.prototype.visitSlice = function (s) {
    var i;
    switch (s.constructor) {
        case Sk.astnodes.Slice:
            if (s.lower) {
                this.visitExpr(s.lower);
            }
            if (s.upper) {
                this.visitExpr(s.upper);
            }
            if (s.step) {
                this.visitExpr(s.step);
            }
            break;
        case Sk.astnodes.ExtSlice:
            for (i = 0; i < s.dims.length; ++i) {
                this.visitSlice(s.dims[i]);
            }
            break;
        case Sk.astnodes.Index:
            this.visitExpr(s.value);
            break;
        case Sk.astnodes.Ellipsis:
            break;
    }
};

SymbolTable.prototype.visitStmt = function (s) {
    var cur;
    var name;
    var i;
    var nameslen;
    var tmp;
    var e_name;
    Sk.asserts.assert(s !== undefined, "visitStmt called with undefined");
    switch (s.constructor) {
        case Sk.astnodes.FunctionDef:
            this.addDef(s.name, DEF_LOCAL, s.lineno);
            if (s.args.defaults) {
                this.SEQExpr(s.args.defaults);
            }
            if (s.decorator_list) {
                this.SEQExpr(s.decorator_list);
            }
            this.enterBlock(s.name.v, FunctionBlock, s, s.lineno);
            this.visitArguments(s.args, s.lineno);
            this.SEQStmt(s.body);
            this.exitBlock();
            break;
        case Sk.astnodes.ClassDef:
            this.addDef(s.name, DEF_LOCAL, s.lineno);
            this.SEQExpr(s.bases);
            if (s.decorator_list) {
                this.SEQExpr(s.decorator_list);
            }
            this.enterBlock(s.name.v, ClassBlock, s, s.lineno);
            tmp = this.curClass;
            this.curClass = s.name;
            this.SEQStmt(s.body);
            this.exitBlock();
            break;
        case Sk.astnodes.Return:
            if (s.value) {
                this.visitExpr(s.value);
                this.cur.returnsValue = true;
                if (this.cur.generator) {
                    throw new Sk.builtin.SyntaxError("'return' with argument inside generator", this.filename);
                }
            }
            break;
        case Sk.astnodes.Delete:
            this.SEQExpr(s.targets);
            break;
        case Sk.astnodes.Assign:
            this.SEQExpr(s.targets);
            this.visitExpr(s.value);
            break;
        case Sk.astnodes.AnnAssign:
            if (s.target.constructor == Sk.astnodes.Name) {
                e_name = s.target;
                name = Sk.mangleName(this.curClass, e_name.id).v;
                name = Sk.fixReserved(name);
                cur = this.cur.symFlags[name];
                if ((cur & (DEF_GLOBAL | DEF_NONLOCAL) )
                    && (this.global != this.cur.symFlags) // TODO
                    && (s.simple)) {
                    throw new Sk.builtin.SyntaxError("annotated name '"+ name +"' can't be global", this.filename, s.lineno);
                }
                if (s.simple) {
                    this.addDef(new Sk.builtin.str(name), DEF_ANNOT | DEF_LOCAL, s.lineno);
                } else if (s.value) {
                    this.addDef(new Sk.builtin.str(name), DEF_LOCAL, s.lineno);
                }
            } else {
                this.visitExpr(s.target);
            }
            this.visitExpr(s.annotation);
            if (s.value) {
                this.visitExpr(s.value);
            }
            break;
        case Sk.astnodes.AugAssign:
            this.visitExpr(s.target);
            this.visitExpr(s.value);
            break;
        case Sk.astnodes.Print:
            if (s.dest) {
                this.visitExpr(s.dest);
            }
            this.SEQExpr(s.values);
            break;
        case Sk.astnodes.For:
            this.visitExpr(s.target);
            this.visitExpr(s.iter);
            this.SEQStmt(s.body);
            if (s.orelse) {
                this.SEQStmt(s.orelse);
            }
            break;
        case Sk.astnodes.While:
            this.visitExpr(s.test);
            this.SEQStmt(s.body);
            if (s.orelse) {
                this.SEQStmt(s.orelse);
            }
            break;
        case Sk.astnodes.If:
            this.visitExpr(s.test);
            this.SEQStmt(s.body);
            if (s.orelse) {
                this.SEQStmt(s.orelse);
            }
            break;
        case Sk.astnodes.Raise:
            if (s.exc) {
                this.visitExpr(s.exc);
                // Our hacked AST supports both Python 2 (inst, tback)
                // and Python 3 (cause) versions of the Raise statement
                if (s.inst) {
                    this.visitExpr(s.inst);
                    if (s.tback) {
                        this.visitExpr(s.tback);
                    }
                }
                if (s.cause) {
                    this.visitExpr(s.cause);
                }
            }
            break;
        case Sk.astnodes.Assert:
            this.visitExpr(s.test);
            if (s.msg) {
                this.visitExpr(s.msg);
            }
            break;
        case Sk.astnodes.Import:
        case Sk.astnodes.ImportFrom:
            this.visitAlias(s.names, s.lineno);
            break;
        case Sk.astnodes.Global:
            nameslen = s.names.length;
            for (i = 0; i < nameslen; ++i) {
                name = Sk.mangleName(this.curClass, s.names[i]).v;
                name = Sk.fixReserved(name);
                cur = this.cur.symFlags[name];
                if (cur & (DEF_LOCAL | USE)) {
                    if (cur & DEF_LOCAL) {
                        throw new Sk.builtin.SyntaxError("name '" + name + "' is assigned to before global declaration", this.filename, s.lineno);
                    }
                    else {
                        throw new Sk.builtin.SyntaxError("name '" + name + "' is used prior to global declaration", this.filename, s.lineno);
                    }
                }
                this.addDef(new Sk.builtin.str(name), DEF_GLOBAL, s.lineno);
            }
            break;
        case Sk.astnodes.Expr:
            this.visitExpr(s.value);
            break;
        case Sk.astnodes.Pass:
        case Sk.astnodes.Break:
        case Sk.astnodes.Continue:
        case Sk.astnodes.Debugger:
            // nothing
            break;
        case Sk.astnodes.With:
            VISIT_SEQ(this.visit_withitem.bind(this), s.items);
            VISIT_SEQ(this.visitStmt.bind(this), s.body);
            break;

        case Sk.astnodes.Try:
            this.SEQStmt(s.body);
            this.visitExcepthandlers(s.handlers)
            this.SEQStmt(s.orelse);
            this.SEQStmt(s.finalbody);
            break;

        default:
            Sk.asserts.fail("Unhandled type " + s.constructor.name + " in visitStmt");
    }
};

SymbolTable.prototype.visit_withitem = function(item) {
    this.visitExpr(item.context_expr);
    if (item.optional_vars) {
        this.visitExpr(item.optional_vars);
    }
}


function VISIT_SEQ(visitFunc, seq) {
    var i;
    for (i = 0; i < seq.length; i++) {
        var elt = seq[i];
        visitFunc(elt)
    }
}

SymbolTable.prototype.visitExpr = function (e) {
    var i;
    Sk.asserts.assert(e !== undefined, "visitExpr called with undefined");
    // console.log("  e: ", e.constructor.name);
    switch (e.constructor) {
        case Sk.astnodes.BoolOp:
            this.SEQExpr(e.values);
            break;
        case Sk.astnodes.BinOp:
            this.visitExpr(e.left);
            this.visitExpr(e.right);
            break;
        case Sk.astnodes.UnaryOp:
            this.visitExpr(e.operand);
            break;
        case Sk.astnodes.Lambda:
            this.addDef(new Sk.builtin.str("lambda"), DEF_LOCAL, e.lineno);
            if (e.args.defaults) {
                this.SEQExpr(e.args.defaults);
            }
            this.enterBlock("lambda", FunctionBlock, e, e.lineno);
            this.visitArguments(e.args, e.lineno);
            this.visitExpr(e.body);
            this.exitBlock();
            break;
        case Sk.astnodes.IfExp:
            this.visitExpr(e.test);
            this.visitExpr(e.body);
            this.visitExpr(e.orelse);
            break;
        case Sk.astnodes.Dict:
            this.SEQExpr(e.keys);
            this.SEQExpr(e.values);
            break;
        case Sk.astnodes.DictComp:
        case Sk.astnodes.SetComp:
            this.visitComprehension(e.generators, 0);
            break;
        case Sk.astnodes.ListComp:
            this.newTmpname(e.lineno);
            this.visitExpr(e.elt);
            this.visitComprehension(e.generators, 0);
            break;
        case Sk.astnodes.GeneratorExp:
            this.visitGenexp(e);
            break;
        case Sk.astnodes.Yield:
            if (e.value) {
                this.visitExpr(e.value);
            }
            this.cur.generator = true;
            if (this.cur.returnsValue) {
                throw new Sk.builtin.SyntaxError("'return' with argument inside generator", this.filename);
            }
            break;
        case Sk.astnodes.Compare:
            this.visitExpr(e.left);
            this.SEQExpr(e.comparators);
            break;
        case Sk.astnodes.Call:
            this.visitExpr(e.func);
            if (e.args) {
                for (let a of e.args) {
                    if (a.constructor === Sk.astnodes.Starred) {
                        this.visitExpr(a.value);
                    } else {
                        this.visitExpr(a);
                    }
                }
            }
            if (e.keywords) {
                for (let k of e.keywords) {
                    this.visitExpr(k.value);
                }
            }
            break;
        case Sk.astnodes.Num:
        case Sk.astnodes.Str:
        case Sk.astnodes.Bytes:
            break;
        case Sk.astnodes.JoinedStr:
            for (let s of e.values) {
                this.visitExpr(s);
            }
            break;
        case Sk.astnodes.FormattedValue:
            this.visitExpr(e.value);
            if (e.format_spec) {
                this.visitExpr(e.format_spec);
            }
            break;
        case Sk.astnodes.Attribute:
            this.visitExpr(e.value);
            break;
        case Sk.astnodes.Subscript:
            this.visitExpr(e.value);
            this.visitSlice(e.slice);
            break;
        case Sk.astnodes.Name:
            this.addDef(e.id, e.ctx === Sk.astnodes.Load ? USE : DEF_LOCAL, e.lineno);
            break;
        case Sk.astnodes.NameConstant:
            break;
        case Sk.astnodes.List:
        case Sk.astnodes.Tuple:
        case Sk.astnodes.Set:
            this.SEQExpr(e.elts);
            break;
        case Sk.astnodes.Starred:
            this.visitExpr(e.value);
            break;
        default:
            Sk.asserts.fail("Unhandled type " + e.constructor.name + " in visitExpr");
    }
};

SymbolTable.prototype.visitComprehension = function (lcs, startAt) {
    var lc;
    var i;
    var len = lcs.length;
    for (i = startAt; i < len; ++i) {
        lc = lcs[i];
        this.visitExpr(lc.target);
        this.visitExpr(lc.iter);
        this.SEQExpr(lc.ifs);
    }
};

SymbolTable.prototype.visitAlias = function (names, lineno) {
    /* Compute store_name, the name actually bound by the import
     operation.  It is diferent than a->name when a->name is a
     dotted package name (e.g. spam.eggs)
     */
    var dot;
    var storename;
    var name;
    var a;
    var i;
    for (i = 0; i < names.length; ++i) {
        a = names[i];
        name = a.asname === null ? a.name.v : a.asname.v;
        storename = name;
        dot = name.indexOf(".");
        if (dot !== -1) {
            storename = name.substr(0, dot);
        }
        if (name !== "*") {
            this.addDef(new Sk.builtin.str(storename), DEF_IMPORT, lineno);
        }
        else {
            if (this.cur.blockType !== ModuleBlock) {
                throw new Sk.builtin.SyntaxError("import * only allowed at module level", this.filename);
            }
        }
    }
};

SymbolTable.prototype.visitGenexp = function (e) {
    var outermost = e.generators[0];
    // outermost is evaled in current scope
    this.visitExpr(outermost.iter);
    this.enterBlock("genexpr", FunctionBlock, e, e.lineno);
    this.cur.generator = true;
    this.addDef(new Sk.builtin.str(".0"), DEF_PARAM, e.lineno);
    this.visitExpr(outermost.target);
    this.SEQExpr(outermost.ifs);
    this.visitComprehension(e.generators, 1);
    this.visitExpr(e.elt);
    this.exitBlock();
};

SymbolTable.prototype.visitExcepthandlers = function (handlers) {
    var i, eh;
    for (i = 0; eh = handlers[i]; ++i) {
        if (eh.type) {
            this.visitExpr(eh.type);
        }
        if (eh.name) {
            this.visitExpr(eh.name);
        }
        this.SEQStmt(eh.body);
    }
};

function _dictUpdate (a, b) {
    var kb;
    for (kb in b) {
        a[kb] = b[kb];
    }
}

SymbolTable.prototype.analyzeBlock = function (ste, bound, free, global) {
    var c;
    var i;
    var childlen;
    var allfree;
    var flags;
    var name;
    var local = {};
    var scope = {};
    var newglobal = {};
    var newbound = {};
    var newfree = {};

    if (ste.blockType == ClassBlock) {
        _dictUpdate(newglobal, global);
        if (bound) {
            _dictUpdate(newbound, bound);
        }
    }

    for (name in ste.symFlags) {
        flags = ste.symFlags[name];
        this.analyzeName(ste, scope, name, flags, bound, local, free, global);
    }

    if (ste.blockType !== ClassBlock) {
        if (ste.blockType === FunctionBlock) {
            _dictUpdate(newbound, local);
        }
        if (bound) {
            _dictUpdate(newbound, bound);
        }
        _dictUpdate(newglobal, global);
    }

    allfree = {};
    childlen = ste.children.length;
    for (i = 0; i < childlen; ++i) {
        c = ste.children[i];
        this.analyzeChildBlock(c, newbound, newfree, newglobal, allfree);
        if (c.hasFree || c.childHasFree) {
            ste.childHasFree = true;
        }
    }

    _dictUpdate(newfree, allfree);
    if (ste.blockType === FunctionBlock) {
        this.analyzeCells(scope, newfree);
    }
    let discoveredFree = this.updateSymbols(ste.symFlags, scope, bound, newfree, ste.blockType === ClassBlock);
    ste.hasFree = ste.hasFree || discoveredFree;

    _dictUpdate(free, newfree);
};

SymbolTable.prototype.analyzeChildBlock = function (entry, bound, free, global, childFree) {
    var tempGlobal;
    var tempFree;
    var tempBound = {};
    _dictUpdate(tempBound, bound);
    tempFree = {};
    _dictUpdate(tempFree, free);
    tempGlobal = {};
    _dictUpdate(tempGlobal, global);

    this.analyzeBlock(entry, tempBound, tempFree, tempGlobal);
    _dictUpdate(childFree, tempFree);
};

SymbolTable.prototype.analyzeCells = function (scope, free) {
    var flags;
    var name;
    for (name in scope) {
        flags = scope[name];
        if (flags !== LOCAL) {
            continue;
        }
        if (free[name] === undefined) {
            continue;
        }
        scope[name] = CELL;
        delete free[name];
    }
};

/**
 * store scope info back into the st symbols dict. symbols is modified,
 * others are not.
 */
SymbolTable.prototype.updateSymbols = function (symbols, scope, bound, free, classflag) {
    var i;
    var o;
    var pos;
    var freeValue;
    var w;
    var flags;
    var name;
    var discoveredFree = false;
    for (name in symbols) {
        flags = symbols[name];
        w = scope[name];
        flags |= w << SCOPE_OFF;
        symbols[name] = flags;
    }

    freeValue = FREE << SCOPE_OFF;
    pos = 0;
    for (name in free) {
        o = symbols[name];
        if (o !== undefined) {
            // it could be a free variable in a method of the class that has
            // the same name as a local or global in the class scope
            if (classflag && (o & (DEF_BOUND | DEF_GLOBAL))) {
                i = o | DEF_FREE_CLASS;
                symbols[name] = i;
            }
            // else it's not free, probably a cell
            continue;
        }
        if (bound[name] === undefined) {
            continue;
        }
        symbols[name] = freeValue;
        discoveredFree = true;
    }
    return discoveredFree;
};

SymbolTable.prototype.analyzeName = function (ste, dict, name, flags, bound, local, free, global) {
    if (flags & DEF_GLOBAL) {
        if (flags & DEF_PARAM) {
            throw new Sk.builtin.SyntaxError("name '" + name + "' is local and global", this.filename, ste.lineno);
        }
        dict[name] = GLOBAL_EXPLICIT;
        global[name] = null;
        if (bound && bound[name] !== undefined) {
            delete bound[name];
        }
        return;
    }
    if (flags & DEF_BOUND) {
        dict[name] = LOCAL;
        local[name] = null;
        delete global[name];
        return;
    }

    if (bound && bound[name] !== undefined) {
        dict[name] = FREE;
        ste.hasFree = true;
        free[name] = null;
    }
    else if (global && global[name] !== undefined) {
        dict[name] = GLOBAL_IMPLICIT;
    }
    else {
        if (ste.isNested) {
            ste.hasFree = true;
        }
        dict[name] = GLOBAL_IMPLICIT;
    }
};

SymbolTable.prototype.analyze = function () {
    var free = {};
    var global = {};
    this.analyzeBlock(this.top, null, free, global);
};

/**
 * @param {Object} ast
 * @param {string} filename
 */
Sk.symboltable = function (ast, filename) {
    var i;
    var ret = new SymbolTable(filename);

    ret.enterBlock("top", ModuleBlock, ast, 0);
    ret.top = ret.cur;

    //print(Sk.astDump(ast));
    for (i = 0; i < ast.body.length; ++i) {
        ret.visitStmt(ast.body[i]);
    }

    ret.exitBlock();

    ret.analyze();

    return ret;
};

Sk.dumpSymtab = function (st) {
    var pyBoolStr = function (b) {
        return b ? "True" : "False";
    }
    var pyList = function (l) {
        var i;
        var ret = [];
        for (i = 0; i < l.length; ++i) {
            ret.push(new Sk.builtin.str(l[i])["$r"]().v);
        }
        return "[" + ret.join(", ") + "]";
    };
    var getIdents = function (obj, indent) {
        var ns;
        var j;
        var sub;
        var nsslen;
        var nss;
        var info;
        var i;
        var objidentslen;
        var objidents;
        var ret;
        if (indent === undefined) {
            indent = "";
        }
        ret = "";
        ret += indent + "Sym_type: " + obj.get_type() + "\n";
        ret += indent + "Sym_name: " + obj.get_name() + "\n";
        ret += indent + "Sym_lineno: " + obj.get_lineno() + "\n";
        ret += indent + "Sym_nested: " + pyBoolStr(obj.is_nested()) + "\n";
        ret += indent + "Sym_haschildren: " + pyBoolStr(obj.has_children()) + "\n";
        if (obj.get_type() === "class") {
            ret += indent + "Class_methods: " + pyList(obj.get_methods()) + "\n";
        }
        else if (obj.get_type() === "function") {
            ret += indent + "Func_params: " + pyList(obj.get_parameters()) + "\n";
            ret += indent + "Func_locals: " + pyList(obj.get_locals()) + "\n";
            ret += indent + "Func_globals: " + pyList(obj.get_globals()) + "\n";
            ret += indent + "Func_frees: " + pyList(obj.get_frees()) + "\n";
        }
        ret += indent + "-- Identifiers --\n";
        objidents = obj.get_identifiers();
        objidentslen = objidents.length;
        for (i = 0; i < objidentslen; ++i) {
            info = obj.lookup(objidents[i]);
            ret += indent + "name: " + info.get_name() + "\n";
            ret += indent + "  is_referenced: " + pyBoolStr(info.is_referenced()) + "\n";
            ret += indent + "  is_imported: " + pyBoolStr(info.is_imported()) + "\n";
            ret += indent + "  is_parameter: " + pyBoolStr(info.is_parameter()) + "\n";
            ret += indent + "  is_global: " + pyBoolStr(info.is_global()) + "\n";
            ret += indent + "  is_declared_global: " + pyBoolStr(info.is_declared_global()) + "\n";
            ret += indent + "  is_local: " + pyBoolStr(info.is_local()) + "\n";
            ret += indent + "  is_free: " + pyBoolStr(info.is_free()) + "\n";
            ret += indent + "  is_assigned: " + pyBoolStr(info.is_assigned()) + "\n";
            ret += indent + "  is_namespace: " + pyBoolStr(info.is_namespace()) + "\n";
            nss = info.get_namespaces();
            nsslen = nss.length;
            ret += indent + "  namespaces: [\n";
            sub = [];
            for (j = 0; j < nsslen; ++j) {
                ns = nss[j];
                sub.push(getIdents(ns, indent + "    "));
            }
            ret += sub.join("\n");
            ret += indent + "  ]\n";
        }
        return ret;
    };
    return getIdents(st.top, "");
};

Sk.exportSymbol("Sk.symboltable", Sk.symboltable);
Sk.exportSymbol("Sk.dumpSymtab", Sk.dumpSymtab);


/***/ }),

/***/ "./src/timsort.js":
/*!************************!*\
  !*** ./src/timsort.js ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Sk.builtin.list=} list
 * @param {number=} length optional
 * @extends Sk.builtin.object
 */
Sk.builtin.timSort = function (list, length) {
    this.list = new Sk.builtin.list(list.v);
    // When we get into galloping mode, we stay there until both runs win less
    // often than MIN_GALLOP consecutive times.  See listsort.txt for more info.
    this.MIN_GALLOP = 7;
    if (length) {
        this.listlength = length;
    } else {
        this.listlength = list.sq$length();
    }
};

Sk.builtin.timSort.prototype.lt = function (a, b) {
    return Sk.misceval.richCompareBool(a, b, "Lt");
};

Sk.builtin.timSort.prototype.le = function (a, b) {
    return !this.lt(b, a);
};

Sk.builtin.timSort.prototype.setitem = function (item, value) {
    this.list.v[item] = value;
};

/*
 # binarysort is the best method for sorting small arrays: it does
 # few compares, but can do data movement quadratic in the number of
 # elements.
 # "a" is a contiguous slice of a list, and is sorted via binary insertion.
 # This sort is stable.
 # On entry, the first "sorted" elements are already sorted.
 # Even in case of error, the output slice will be some permutation of
 # the input (nothing is lost or duplicated)
 */
Sk.builtin.timSort.prototype.binary_sort = function (a, sorted) {
    var pivot;
    var p;
    var r;
    var l;
    var start;
    for (start = a.base + sorted; start < a.base + a.len; start++) {
        l = a.base;
        r = start;
        pivot = a.getitem(r);
        // Invariants:
        // pivot >= all in [base, l).
        // pivot  < all in [r, start).
        // The second is vacuously true at the start.
        while (l < r) {
            p = l + ((r - l) >> 1);
            if (this.lt(pivot, a.getitem(p))) {
                r = p;
            } else {
                l = p + 1;
            }
        }
        Sk.asserts.assert(l === r);
        // The invariants still hold, so pivot >= all in [base, l) and
        // pivot < all in [l, start), so pivot belongs at l.  Note
        // that if there are elements equal to pivot, l points to the
        // first slot after them -- that's why this sort is stable.
        // Slide over to make room.
        for (p = start; p > l; p--) {
            a.setitem(p, a.getitem(p - 1));
        }
        a.setitem(l, pivot);
    }
};

Sk.builtin.timSort.prototype.count_run = function (a) {
    /*
     # Compute the length of the run in the slice "a".
     # "A run" is the longest ascending sequence, with
     #
     #     a[0] <= a[1] <= a[2] <= ...
     #
     # or the longest descending sequence, with
     #
     #     a[0] > a[1] > a[2] > ...
     #
     # Return (run, descending) where descending is False in the former case,
     # or True in the latter.
     # For its intended use in a stable mergesort, the strictness of the defn of
     # "descending" is needed so that the caller can safely reverse a descending
     # sequence without violating stability (strict > ensures there are no equal
     # elements to get out of order).
     */
    var n;
    var p;
    var descending;
    if (a.len <= 1) {
        n = a.len;
        descending = false;
    } else {
        n = 2;
        if (this.lt(a.getitem(a.base + 1), a.getitem(a.base))) {
            descending = true;
            for (p = a.base + 2; p < a.base + a.len; p++) {
                if (this.lt(a.getitem(p), a.getitem(p - 1))) {
                    n++;
                } else {
                    break;
                }
            }
        } else {
            descending = false;
            for (p = a.base + 2; p < a.base + a.len; p++) {
                if (this.lt(a.getitem(p), a.getitem(p - 1))) {
                    break;
                } else {
                    n++;
                }
            }
        }
    }
    return {"run": new Sk.builtin.listSlice(a.list, a.base, n), "descending": descending};
};

Sk.builtin.timSort.prototype.sort = function () {
    /*
     # ____________________________________________________________
     # Entry point.
     */

    var minrun;
    var cr;
    var sorted;
    var remaining = new Sk.builtin.listSlice(this.list, 0, this.listlength);
    if (remaining.len < 2) {
        return;
    }

    // March over the array once, left to right, finding natural runs,
    // and extending short natural runs to minrun elements.
    this.merge_init();
    minrun = this.merge_compute_minrun(remaining.len);
    while (remaining.len > 0) {
        // Identify next run.
        cr = this.count_run(remaining);
        if (cr.descending) {
            cr.run.reverse();
        }
        // If short, extend to min(minrun, nremaining).
        if (cr.run.len < minrun) {
            sorted = cr.run.len;
            if (minrun < remaining.len) {
                cr.run.len = minrun;
            } else {
                cr.run.len = remaining.len;
            }
            this.binary_sort(cr.run, sorted);
        }
        // Advance remaining past this run.
        remaining.advance(cr.run.len);
        // Push run onto pending-runs stack, and maybe merge.
        this.pending.push(cr.run);
        this.merge_collapse();
    }
    Sk.asserts.assert(remaining.base == this.listlength);

    this.merge_force_collapse();
    Sk.asserts.assert(this.pending.length == 1);
    Sk.asserts.assert(this.pending[0].base === 0);
    Sk.asserts.assert(this.pending[0].len == this.listlength);
};

/*
 # Locate the proper position of key in a sorted vector; if the vector
 # contains an element equal to key, return the position immediately to the
 # left of the leftmost equal element -- or to the right of the rightmost
 # equal element if the flag "rightmost" is set.
 #
 # "hint" is an index at which to begin the search, 0 <= hint < a.len.
 # The closer hint is to the final result, the faster this runs.
 #
 # The return value is the index 0 <= k <= a.len such that
 #
 #     a[k-1] < key <= a[k]      (if rightmost is False)
 #     a[k-1] <= key < a[k]      (if rightmost is True)
 #
 # as long as the indices are in bound.  IOW, key belongs at index k;
 # or, IOW, the first k elements of a should precede key, and the last
 # n-k should follow key.
 */
Sk.builtin.timSort.prototype.gallop = function (key, a, hint, rightmost) {
    var lower;
    var self;
    var p;
    var lastofs;
    var ofs;
    var maxofs;
    var hintminofs;
    var hintminlastofs;
    var m;
    Sk.asserts.assert(0 <= hint && hint < a.len);
    self = this;
    if (rightmost) {
        lower = function (a, b) {
            return self.le(a, b);
        }; // search for the largest k for which a[k] <= key
    } else {
        lower = function (a, b) {
            return self.lt(a, b);
        }; // search for the largest k for which a[k] < key
    }
    p = a.base + hint;
    lastofs = 0;
    ofs = 1;
    if (lower(a.getitem(p), key)) {
        // a[hint] < key -- gallop right, until
        // a[hint + lastofs] < key <= a[hint + ofs]

        maxofs = a.len - hint; // a[a.len-1] is highest
        while (ofs < maxofs) {
            if (lower(a.getitem(p + ofs), key)) {
                lastofs = ofs;
                try {
                    ofs = (ofs << 1) + 1;
                } catch (err) {
                    ofs = maxofs;
                }
            } else {
                // key <= a[hint + ofs]
                break;
            }
        }
        if (ofs > maxofs) {
            ofs = maxofs;
        }
        // Translate back to offsets relative to a.
        lastofs += hint;
        ofs += hint;
    } else {
        // key <= a[hint] -- gallop left, until
        // a[hint - ofs] < key <= a[hint - lastofs]
        maxofs = hint + 1;   // a[0] is lowest
        while (ofs < maxofs) {
            if (lower(a.getitem(p - ofs), key)) {
                break;
            } else {
                // key <= a[hint - ofs]
                lastofs = ofs;
                try {
                    ofs = (ofs << 1) + 1;
                } catch (err) {
                    ofs = maxofs;
                }
            }
        }
        if (ofs > maxofs) {
            ofs = maxofs;
        }
        // Translate back to positive offsets relative to a.
        hintminofs = hint - ofs;
        hintminlastofs = hint - lastofs;
        lastofs = hintminofs;
        ofs = hintminlastofs;
    }
    Sk.asserts.assert(-1 <= lastofs < ofs <= a.len);

    // Now a[lastofs] < key <= a[ofs], so key belongs somewhere to the
    // right of lastofs but no farther right than ofs.  Do a binary
    // search, with invariant a[lastofs-1] < key <= a[ofs].

    lastofs += 1;
    while (lastofs < ofs) {
        m = lastofs + ((ofs - lastofs) >> 1);
        if (lower(a.getitem(a.base + m), key)) {
            lastofs = m + 1;   // a[m] < key
        } else {
            ofs = m;         // key <= a[m]
        }
    }
    Sk.asserts.assert(lastofs == ofs);         // so a[ofs-1] < key <= a[ofs]
    return ofs;
};

// ____________________________________________________________

Sk.builtin.timSort.prototype.merge_init = function () {
    // This controls when we get *into* galloping mode.  It's initialized
    // to MIN_GALLOP.  merge_lo and merge_hi tend to nudge it higher for
    // random data, and lower for highly structured data.
    this.min_gallop = this.MIN_GALLOP;

    // A stack of n pending runs yet to be merged.  Run #i starts at
    // address pending[i].base and extends for pending[i].len elements.
    // It's always true (so long as the indices are in bounds) that
    //
    //     pending[i].base + pending[i].len == pending[i+1].base
    //
    // so we could cut the storage for this, but it's a minor amount,
    // and keeping all the info explicit simplifies the code.
    this.pending = [];
};

// Merge the slice "a" with the slice "b" in a stable way, in-place.
// a.len <= b.len.  See listsort.txt for more info.
// a.len and b.len must be > 0, and a.base + a.len == b.base.
// Must also have that b.list[b.base] < a.list[a.base], that
// a.list[a.base+a.len-1] belongs at the end of the merge, and should have

Sk.builtin.timSort.prototype.merge_lo = function (a, b) {
    var min_gallop;
    var dest;
    var acount, bcount;
    var p;
    Sk.asserts.assert(a.len > 0 && b.len > 0 && a.base + a.len == b.base);
    min_gallop = this.min_gallop;
    dest = a.base;
    a = a.copyitems();

    // Invariant: elements in "a" are waiting to be reinserted into the list
    // at "dest".  They should be merged with the elements of "b".
    // b.base == dest + a.len.
    // We use a finally block to ensure that the elements remaining in
    // the copy "a" are reinserted back into this.list in all cases.
    try {
        this.setitem(dest, b.popleft());

        dest++;
        if (a.len == 1 || b.len === 0) {
            return;
        }

        while (true) {
            acount = 0;   // number of times A won in a row
            bcount = 0;   // number of times B won in a row

            // Do the straightforward thing until (if ever) one run
            // appears to win consistently.
            while (true) {
                if (this.lt(b.getitem(b.base), a.getitem(a.base))) {
                    this.setitem(dest, b.popleft());
                    dest++;
                    if (b.len === 0) {
                        return;
                    }
                    bcount++;
                    acount = 0;
                    if (bcount >= min_gallop) {
                        break;
                    }
                } else {
                    this.setitem(dest, a.popleft());
                    dest++;
                    if (a.len == 1) {
                        return;
                    }
                    acount++;
                    bcount = 0;
                    if (acount >= min_gallop) {
                        break;
                    }
                }
            }

            // One run is winning so consistently that galloping may
            // be a huge win.  So try that, and continue galloping until
            // (if ever) neither run appears to be winning consistently
            // anymore.
            min_gallop += 1;

            while (true) {
                min_gallop -= min_gallop > 1;
                this.min_gallop = min_gallop;
                acount = this.gallop(b.getitem(b.base), a, 0, true);
                for (p = a.base; p < a.base + acount; p++) {
                    this.setitem(dest, a.getitem(p));
                    dest++;
                }

                a.advance(acount);

                if (a.len <= 1) {
                    return;
                }

                this.setitem(dest, b.popleft());
                dest++;

                // a.len==0 is impossible now if the comparison
                // function is consistent, but we can't assume
                // that it is.
                if (b.len === 0) {
                    return;
                }

                bcount = this.gallop(a.getitem(a.base), b, 0, false);

                for (p = b.base; p < b.base + bcount; p++) {
                    this.setitem(dest, b.getitem(p));
                    dest++;
                }

                b.advance(bcount);
                if (b.len === 0) {
                    return;
                }
                this.setitem(dest, a.popleft());
                dest++;

                if (a.len == 1) {
                    return;
                }

                if (acount < this.MIN_GALLOP && bcount < this.MIN_GALLOP) {
                    break;
                }

                min_gallop++;  // penalize it for leaving galloping mode
                this.min_gallop = min_gallop;
            }
        }
    } finally {
        // The last element of a belongs at the end of the merge, so we copy
        // the remaining elements of b before the remaining elements of a.
        Sk.asserts.assert(a.len >= 0 && b.len >= 0);
        for (p = b.base; p < b.base + b.len; p++) {
            this.setitem(dest, b.getitem(p));
            dest++;
        }
        for (p = a.base; p < a.base + a.len; p++) {
            this.setitem(dest, a.getitem(p));
            dest++;
        }
    }
};

Sk.builtin.timSort.prototype.merge_hi = function (a, b) {
    var min_gallop;
    var dest;
    var acount, bcount, nexta, nextb;
    var k;
    var p;
    Sk.asserts.assert(a.len > 0 && b.len > 0 && a.base + a.len == b.base);
    min_gallop = this.min_gallop;
    dest = b.base + b.len;
    b = b.copyitems();

    // Invariant: elements in "a" are waiting to be reinserted into the list
    // at "dest".  They should be merged with the elements of "b".
    // b.base == dest + a.len.
    // We use a finally block to ensure that the elements remaining in
    // the copy "a" are reinserted back into this.list in all cases.
    try {
        dest--;
        this.setitem(dest, a.popright());

        if (a.len === 0 || b.len == 1) {
            return;
        }

        while (true) {
            acount = 0;   // number of times A won in a row
            bcount = 0;   // number of times B won in a row

            // Do the straightforward thing until (if ever) one run
            // appears to win consistently.
            while (true) {
                nexta = a.getitem(a.base + a.len - 1);
                nextb = b.getitem(b.base + b.len - 1);
                if (this.lt(nextb, nexta)) {
                    dest--;
                    this.setitem(dest, nexta);
                    a.len--;
                    if (a.len === 0) {
                        return;
                    }
                    acount++;
                    bcount = 0;
                    if (acount >= min_gallop) {
                        break;
                    }
                } else {
                    dest--;
                    this.setitem(dest, nextb);
                    b.len--;
                    if (b.len == 1) {
                        return;
                    }
                    bcount++;
                    acount = 0;
                    if (bcount >= min_gallop) {
                        break;
                    }
                }
            }

            // One run is winning so consistently that galloping may
            // be a huge win.  So try that, and continue galloping until
            // (if ever) neither run appears to be winning consistently
            // anymore.
            min_gallop += 1;

            while (true) {
                min_gallop -= min_gallop > 1;
                this.min_gallop = min_gallop;
                nextb = b.getitem(b.base + b.len - 1);
                k = this.gallop(nextb, a, a.len - 1, true);
                acount = a.len - k;
                for (p = a.base + a.len - 1; p > a.base + k - 1; p--) {
                    dest--;
                    this.setitem(dest, a.getitem(p));
                }
                a.len -= acount;
                if (a.len === 0) {
                    return;
                }

                dest--;
                this.setitem(dest, b.popright());
                if (b.len == 1) {
                    return;
                }

                nexta = a.getitem(a.base + a.len - 1);
                k = this.gallop(nexta, b, b.len - 1, false);
                bcount = b.len - k;
                for (p = b.base + b.len - 1; p > b.base + k - 1; p--) {
                    dest--;
                    this.setitem(dest, b.getitem(p));
                }

                b.len -= bcount;

                // b.len==0 is impossible now if the comparison
                // function is consistent, but we can't assume
                // that it is.
                if (b.len <= 1) {
                    return;
                }
                dest--;
                this.setitem(dest, a.popright());
                if (a.len === 0) {
                    return;
                }

                if (acount < this.MIN_GALLOP && bcount < this.MIN_GALLOP) {
                    break;
                }

                min_gallop++;  // penalize it for leaving galloping mode
                this.min_gallop = min_gallop;
            }
        }
    } finally {
        // The last element of a belongs at the end of the merge, so we copy
        // the remaining elements of b before the remaining elements of a.
        Sk.asserts.assert(a.len >= 0 && b.len >= 0);
        for (p = a.base + a.len - 1; p > a.base - 1; p--) {
            dest--;
            this.setitem(dest, a.getitem(p));
        }
        for (p = b.base + b.len - 1; p > b.base - 1; p--) {
            dest--;
            this.setitem(dest, b.getitem(p));
        }
    }
};

// Merge the two runs at stack indices i and i+1.

Sk.builtin.timSort.prototype.merge_at = function (i) {
    var a;
    var b;
    var k;
    if (i < 0) {
        i = this.pending.length + i;
    }

    a = this.pending[i];
    b = this.pending[i + 1];
    Sk.asserts.assert(a.len > 0 && b.len > 0);
    Sk.asserts.assert(a.base + a.len == b.base);

    // Record the length of the combined runs and remove the run b
    this.pending[i] = new Sk.builtin.listSlice(this.list, a.base, a.len + b.len);
    this.pending.splice(i + 1, 1);

    // Where does b start in a?  Elements in a before that can be
    // ignored (already in place).
    k = this.gallop(b.getitem(b.base), a, 0, true);
    a.advance(k);
    if (a.len === 0) {
        return;
    }

    // Where does a end in b?  Elements in b after that can be
    // ignored (already in place).
    b.len = this.gallop(a.getitem(a.base + a.len - 1), b, b.len - 1, false);
    if (b.len === 0) {
        return;
    }

    // Merge what remains of the runs.  The direction is chosen to
    // minimize the temporary storage needed.
    if (a.len <= b.len) {
        this.merge_lo(a, b);
    } else {
        this.merge_hi(a, b);
    }
};

// Examine the stack of runs waiting to be merged, merging adjacent runs
// until the stack invariants are re-established:
//
// 1. len[-3] > len[-2] + len[-1]
// 2. len[-2] > len[-1]
//
// See listsort.txt for more info.
Sk.builtin.timSort.prototype.merge_collapse = function () {
    var p = this.pending;
    while (p.length > 1) {
        if (p.length >= 3 && p[p.length - 3].len <= p[p.length - 2].len + p[p.length - 1].len) {
            if (p[p.length - 3].len < p[p.length - 1].len) {
                this.merge_at(-3);
            } else {
                this.merge_at(-2);
            }
        } else if (p[p.length - 2].len <= p[p.length - 1].len) {
            this.merge_at(-2);
        } else {
            break;
        }
    }
};

// Regardless of invariants, merge all runs on the stack until only one
// remains.  This is used at the end of the mergesort.

Sk.builtin.timSort.prototype.merge_force_collapse = function () {
    var p = this.pending;
    while (p.length > 1) {
        if (p.length >= 3 && p[p.length - 3].len < p[p.length - 1].len) {
            this.merge_at(-3);
        } else {
            this.merge_at(-2);
        }
    }
};
// Compute a good value for the minimum run length; natural runs shorter
// than this are boosted artificially via binary insertion.
//
// If n < 64, return n (it's too small to bother with fancy stuff).
// Else if n is an exact power of 2, return 32.
// Else return an int k, 32 <= k <= 64, such that n/k is close to, but
// strictly less than, an exact power of 2.
//
// See listsort.txt for more info.

Sk.builtin.timSort.prototype.merge_compute_minrun = function (n) {
    var r = 0;    // becomes 1 if any 1 bits are shifted off
    while (n >= 64) {
        r = r | n & 1;
        n >>= 1;
    }
    return n + r;
};

//ListSlice
/**
 * @constructor
 * @param {Sk.builtin.list=} list
 * @param {number=} base
 * @param {number=} len
 * @extends Sk.builtin.object
 */
Sk.builtin.listSlice = function (list, base, len) {
    this.list = list;
    this.base = base;
    this.len = len;
};

Sk.builtin.listSlice.prototype.copyitems = function () {
    //Make a copy of the slice of the original list
    var start = this.base;
    var stop = this.base + this.len;
    Sk.asserts.assert(0 <= start <= stop);
    return new Sk.builtin.listSlice(new Sk.builtin.list(this.list.v.slice(start, stop)), 0, this.len);
};

Sk.builtin.listSlice.prototype.advance = function (n) {
    this.base += n;
    this.len -= n;
    Sk.asserts.assert(this.base <= this.list.sq$length());
};

Sk.builtin.listSlice.prototype.getitem = function (item) {
    return this.list.v[item];
};

Sk.builtin.listSlice.prototype.setitem = function (item, value) {
    this.list.v[item] = value;
};

Sk.builtin.listSlice.prototype.popleft = function () {
    var result = this.list.v[this.base];
    this.base++;
    this.len--;
    return result;
};

Sk.builtin.listSlice.prototype.popright = function () {
    this.len--;
    return this.list.v[this.base + this.len];
};

Sk.builtin.listSlice.prototype.reverse = function () {
    // Reverse the slice in-place.
    var list_hi;
    var list_lo;
    var list = this.list;
    var lo = this.base;
    var hi = lo + this.len - 1;
    while (lo < hi) {
        list_hi = list.v[hi];
        list_lo = list.v[lo];
        list.v[lo] = list_hi;
        list.v[hi] = list_lo;
        lo++;
        hi--;
    }
};

Sk.exportSymbol("Sk.builtin.listSlice", Sk.builtin.listSlice);
Sk.exportSymbol("Sk.builtin.timSort", Sk.builtin.timSort);


/***/ }),

/***/ "./src/token.js":
/*!**********************!*\
  !*** ./src/token.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

//"""Token constants (from somewhere)."""

var __all__ = ["tok_name", "ISTERMINAL", "ISNONTERMINAL", "ISEOF"];

// #  This file is automatically generated; please don't muck it up!
// #
// #  To update the symbols in this file, 'cd' to the top directory of
// #  the python source tree after building the interpreter and run:
// #
// #    ./python Lib/token.py

// #--start constants--
var tokens = {
    T_ENDMARKER: 0,
    T_NAME: 1,
    T_NUMBER: 2,
    T_STRING: 3,
    T_NEWLINE: 4,
    T_INDENT: 5,
    T_DEDENT: 6,
    T_LPAR: 7,
    T_RPAR: 8,
    T_LSQB: 9,
    T_RSQB: 10,
    T_COLON: 11,
    T_COMMA: 12,
    T_SEMI: 13,
    T_PLUS: 14,
    T_MINUS: 15,
    T_STAR: 16,
    T_SLASH: 17,
    T_VBAR: 18,
    T_AMPER: 19,
    T_LESS: 20,
    T_GREATER: 21,
    T_EQUAL: 22,
    T_DOT: 23,
    T_PERCENT: 24,
    T_LBRACE: 25,
    T_RBRACE: 26,
    T_EQEQUAL: 27,
    T_NOTEQUAL: 28,
    T_LESSEQUAL: 29,
    T_GREATEREQUAL: 30,
    T_TILDE: 31,
    T_CIRCUMFLEX: 32,
    T_LEFTSHIFT: 33,
    T_RIGHTSHIFT: 34,
    T_DOUBLESTAR: 35,
    T_PLUSEQUAL: 36,
    T_MINEQUAL: 37,
    T_STAREQUAL: 38,
    T_SLASHEQUAL: 39,
    T_PERCENTEQUAL: 40,
    T_AMPEREQUAL: 41,
    T_VBAREQUAL: 42,
    T_CIRCUMFLEXEQUAL: 43,
    T_LEFTSHIFTEQUAL: 44,
    T_RIGHTSHIFTEQUAL: 45,
    T_DOUBLESTAREQUAL: 46,
    T_DOUBLESLASH: 47,
    T_DOUBLESLASHEQUAL: 48,
    T_AT: 49,
    T_ATEQUAL: 50,
    T_RARROW: 51,
    T_ELLIPSIS: 52,
    T_OP: 53,
    T_AWAIT: 54,
    T_ASYNC: 55,
    T_ERRORTOKEN: 56,

    //special cases
    T_NT_OFFSET: 256,
    T_N_TOKENS: 60,

    //taken from tokenize.py
    T_COMMENT: 57,
    T_NL: 58,
    T_ENCODING: 59
};
// #--end constants--

var EXACT_TOKEN_TYPES = {
    // "<>": tokens.T_NOTEQUAL, Only in py2
    "!=": tokens.T_NOTEQUAL,
    "%": tokens.T_PERCENT,
    "%=": tokens.T_PERCENTEQUAL,
    "&": tokens.T_AMPER,
    "&=": tokens.T_AMPEREQUAL,
    "(": tokens.T_LPAR,
    ")": tokens.T_RPAR,
    "*": tokens.T_STAR,
    "**": tokens.T_DOUBLESTAR,
    "**=": tokens.T_DOUBLESTAREQUAL,
    "*=": tokens.T_STAREQUAL,
    "+": tokens.T_PLUS,
    "+=": tokens.T_PLUSEQUAL,
    ",": tokens.T_COMMA,
    "-": tokens.T_MINUS,
    "-=": tokens.T_MINEQUAL,
    "->": tokens.T_RARROW,
    ".": tokens.T_DOT,
    "...": tokens.T_ELLIPSIS,
    "/": tokens.T_SLASH,
    "//": tokens.T_DOUBLESLASH,
    "//=": tokens.T_DOUBLESLASHEQUAL,
    "/=": tokens.T_SLASHEQUAL,
    ":": tokens.T_COLON,
    // ":=": tokens.T_COLONEQUAL, // currently not listed in tokens
    ";": tokens.T_SEMI,
    "<": tokens.T_LESS,
    "<<": tokens.T_LEFTSHIFT,
    "<<=": tokens.T_LEFTSHIFTEQUAL,
    "<=": tokens.T_LESSEQUAL,
    "=": tokens.T_EQUAL,
    "==": tokens.T_EQEQUAL,
    ">": tokens.T_GREATER,
    ">=": tokens.T_GREATEREQUAL,
    ">>": tokens.T_RIGHTSHIFT,
    ">>=": tokens.T_RIGHTSHIFTEQUAL,
    "@": tokens.T_AT,
    "@=": tokens.T_ATEQUAL,
    "[": tokens.T_LSQB,
    "]": tokens.T_RSQB,
    "^": tokens.T_CIRCUMFLEX,
    "^=": tokens.T_CIRCUMFLEXEQUAL,
    "{": tokens.T_LBRACE,
    "|": tokens.T_VBAR,
    "|=": tokens.T_VBAREQUAL,
    "}": tokens.T_RBRACE,
    "~": tokens.T_TILDE,
};

var tok_name = {};
(function() {
    for (var i in tokens) {
        tok_name[tokens[i]] = i;
    }
})();

__all__.concat(Object.keys(tok_name).map(function (k) { return tok_name[k]; }));

function ISTERMINAL(x) {
    return x < tokens.T_NT_OFFSET;
}

function ISNONTERMINAL(x) {
    return x >= tokens.T_NT_OFFSET;
}

function ISEOF(x) {
    return x == tokens.T_ENDMARKER;
}

Sk.token = {};
Sk.token.tokens = tokens;
Sk.token.tok_name = tok_name;
Sk.token.EXACT_TOKEN_TYPES = EXACT_TOKEN_TYPES;
Sk.token.ISTERMINAL = ISTERMINAL;
Sk.token.ISNONTERMINAL = ISNONTERMINAL;
Sk.token.ISEOF = ISEOF;

Sk.exportSymbol("Sk.token", Sk.token);
Sk.exportSymbol("Sk.token.tokens", Sk.token.tokens);
Sk.exportSymbol("Sk.token.tok_name", Sk.token.tok_name);
Sk.exportSymbol("Sk.token.EXACT_TOKEN_TYPES");
Sk.exportSymbol("Sk.token.ISTERMINAL", Sk.token.ISTERMINAL);
Sk.exportSymbol("Sk.token.ISNONTERMINAL", Sk.token.ISNONTERMINAL);
Sk.exportSymbol("Sk.token.ISEOF", Sk.token.ISEOF);


/***/ }),

/***/ "./src/tokenize.js":
/*!*************************!*\
  !*** ./src/tokenize.js ***!
  \*************************/
/*! no static exports found */
/***/ (function(module, exports) {

var tokens = Sk.token.tokens

const TokenError = Sk.builtin.SyntaxError;
const IndentationError = Sk.builtin.SyntaxError;

/**
 *
 * @constructor
 * @param {number} type
 * @param {string} string
 * @param {Array<number>} start
 * @param {Array<number>} end
 * @param {string} line
 */
function TokenInfo(type, string, start, end, line) {
    this.type = type;
    this.string = string;
    this.start = start;
    this.end = end;
    this.line = line;
}

TokenInfo.prototype.exact_type = function() {
    if (this.type == tokens.T_OP && this.string in Sk.token.EXACT_TOKEN_TYPES) {
    return Sk.token.EXACT_TOKEN_TYPES[this.string]
    } else {
        return this.type
    }
}

/** @param {...*} x */
function group (x) {
    var args = Array.prototype.slice.call(arguments);
    return "(" + args.join("|") + ")";
}

/** @param {...*} x */
function any (x) {
    return group.apply(null, arguments) + "*";
}

/** @param {...*} x */
function maybe (x) {
    return group.apply(null, arguments) + "?";
}

var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
    reHasRegExpChar = RegExp(reRegExpChar.source);

function regexEscape(string) {
    return (string && reHasRegExpChar.test(string))
        ? string.replace(reRegExpChar, '\\$&')
        : string;
}

/**
 * Iterable contains
 * @template T
 * @param {T} a
 * @param {T} obj
 */
function contains (a, obj) {
    var i = a.length;
    while (i--) {
        if (a[i] === obj) {
            return true;
        }
    }
    return false;
}

function rstrip (input, what) {
    var i;
    for (i = input.length; i > 0; --i) {
        if (what.indexOf(input.charAt(i - 1)) === -1) {
            break;
        }
    }
    return input.substring(0, i);
}

const IS_IDENTIFIER_REGEX = (function() {
    var the_underscore = '_';
    var Lu = '[A-Z]';
    var Ll = '[a-z]';
    var Lt = '[\\u{10B99}-\\u{10B9C}\\u{112A9}\\u{115DC}-\\u{115DD}\\u034F\\u115F-\\u1160\\u17B4-\\u17B5\\u2065\\u3164\\uFFA0\\uFFF0-\\uFFF8\\u{E0000}\\u{E0002}-\\u{E001F}\\u{E0080}-\\u{E00FF}\\u{E01F0}-\\u{E0FFF}\\u{112A9}\\u00D7]';
    var Lm = '[\\u02B0-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0374\\u037A\\u0559\\u06E5-\\u06E6\\u07F4-\\u07F5\\u0971\\u1C78-\\u1C7D\\u1D2C-\\u1D6A\\u1DFD-\\u1DFF\\u2E2F\\u30FC\\uA67F\\uA69C-\\uA69D\\uA717-\\uA71F\\uA788\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF70\\uFF9E-\\uFF9F\\u{16F93}-\\u{16F9F}\\u02D0-\\u02D1\\u0640\\u07FA\\u0E46\\u0EC6\\u1843\\u1AA7\\u1C7B\\u3005\\u3031-\\u3035\\u309D-\\u309E\\u30FC-\\u30FE\\uA015\\uA60C\\uA9CF\\uA9E6\\uAA70\\uAADD\\uAAF3-\\uAAF4\\uFF70\\u{16B42}-\\u{16B43}\\u{16FE0}-\\u{16FE1}\\u02B0-\\u02B8\\u02C0-\\u02C1\\u02E0-\\u02E4\\u037A\\u1D2C-\\u1D6A\\u1D78\\u1D9B-\\u1DBF\\u2071\\u207F\\u2090-\\u209C\\u2C7C-\\u2C7D\\uA69C-\\uA69D\\uA770\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF9E-\\uFF9F\\u02B2\\u1D62\\u1DA4\\u1DA8\\u2071\\u2C7C\\u2E18-\\u2E19\\u2E2F]';
    var Lo = '[\\u2135-\\u2138\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u3006\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uF900-\\uFA6D\\uFA70-\\uFAD9\\u{17000}-\\u{187F1}\\u{18800}-\\u{18AF2}\\u{1B170}-\\u{1B2FB}\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u{2F800}-\\u{2FA1D}\\uAAC0\\uAAC2\\uFE20-\\uFE2F\\u{10D22}-\\u{10D23}\\u{1135D}\\u00AA\\u00BA\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uFA0E-\\uFA0F\\uFA11\\uFA13-\\uFA14\\uFA1F\\uFA21\\uFA23-\\uFA24\\uFA27-\\uFA29\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u115F-\\u1160\\u3164\\uFFA0\\u0673\\u17A3-\\u17A4\\u0E40-\\u0E44\\u0EC0-\\u0EC4\\u19B5-\\u19B7\\u19BA\\uAAB5-\\uAAB6\\uAAB9\\uAABB-\\uAABC]';
    var Nl = '[\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u2170-\\u217F\\u2160-\\u216F]';
    var Mn = '[\\u104A-\\u104B\\u102B-\\u102C\\u102D-\\u1030\\u1031\\u1032-\\u1036\\u1038\\u103B-\\u103C\\u103D-\\u103E\\u1056-\\u1057\\u1058-\\u1059\\u105E-\\u1060\\u1062\\u1067-\\u1068\\u1071-\\u1074\\u1082\\u1083-\\u1084\\u1085-\\u1086\\u109C\\u109D\\u1037\\u1039-\\u103A\\u1087-\\u108C\\u108D\\u108F\\u109A-\\u109B\\uA9E5\\uAA7B\\uAA7C\\uAA7D\\uA9E6\\uAA70\\u104A-\\u104B]';
    var Mc = '[\\u0903\\u093B\\u093E-\\u0940\\u0949-\\u094C\\u094E-\\u094F\\u0982-\\u0983\\u09BE-\\u09C0\\u09C7-\\u09C8\\u09CB-\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB-\\u0ACC\\u0B02-\\u0B03\\u0B3E\\u0B40\\u0B47-\\u0B48\\u0B4B-\\u0B4C\\u0B57\\u0BBE-\\u0BBF\\u0BC1-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82-\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7-\\u0CC8\\u0CCA-\\u0CCB\\u0CD5-\\u0CD6\\u0D02-\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82-\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2-\\u0DF3\\u0F7F\\u102B-\\u102C\\u1031\\u1038\\u103B-\\u103C\\u1056-\\u1057\\u1062\\u1067-\\u1068\\u1083-\\u1084\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7-\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930-\\u1931\\u1933-\\u1938\\u1A19-\\u1A1A\\u1A55\\u1A57\\u1A61\\u1A63-\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B82\\u1BA1\\u1BA6-\\u1BA7\\u1BE7\\u1BEA-\\u1BEC\\u1BEE\\u1C24-\\u1C2B\\u1C34-\\u1C35\\u1CF2-\\u1CF3\\uA823-\\uA824\\uA827\\uA880-\\uA881\\uA8B4-\\uA8C3\\uA952\\uA983\\uA9B4-\\uA9B5\\uA9BA-\\uA9BB\\uA9BD-\\uA9BF\\uAA2F-\\uAA30\\uAA33-\\uAA34\\uAA4D\\uAAEB\\uAAEE-\\uAAEF\\uAAF5\\uABE3-\\uABE4\\uABE6-\\uABE7\\uABE9-\\uABEA\\u{11000}\\u{11002}\\u{11082}\\u{110B0}-\\u{110B2}\\u{110B7}-\\u{110B8}\\u{1112C}\\u{11145}-\\u{11146}\\u{11182}\\u{111B3}-\\u{111B5}\\u{111BF}\\u{1122C}-\\u{1122E}\\u{11232}-\\u{11233}\\u{112E0}-\\u{112E2}\\u{11302}-\\u{11303}\\u{1133E}-\\u{1133F}\\u{11341}-\\u{11344}\\u{11347}-\\u{11348}\\u{1134B}-\\u{1134C}\\u{11357}\\u{11362}-\\u{11363}\\u{11435}-\\u{11437}\\u{11440}-\\u{11441}\\u{11445}\\u{114B0}-\\u{114B2}\\u{114B9}\\u{114BB}-\\u{114BE}\\u{114C1}\\u{115AF}-\\u{115B1}\\u{115B8}-\\u{115BB}\\u{115BE}\\u{11630}-\\u{11632}\\u{1163B}-\\u{1163C}\\u{1163E}\\u{116AC}\\u{116AE}-\\u{116AF}\\u{11720}-\\u{11721}\\u{11726}\\u{1182C}-\\u{1182E}\\u{11838}\\u{11A39}\\u{11A57}-\\u{11A58}\\u{11A97}\\u{11C2F}\\u{11C3E}\\u{11CA9}\\u{11CB1}\\u{11CB4}\\u{11D8A}-\\u{11D8E}\\u{11D93}-\\u{11D94}\\u{11D96}\\u{11EF5}-\\u{11EF6}\\u{16F51}-\\u{16F7E}\\u0F3E-\\u0F3F\\u1087-\\u108C\\u108F\\u109A-\\u109B\\u1B44\\u1BAA\\u1CE1\\u1CF7\\u302E-\\u302F\\uA953\\uA9C0\\uAA7B\\uAA7D\\uABEC\\u{111C0}\\u{11235}\\u{1134D}\\u{116B6}\\u{1D16D}-\\u{1D172}\\u09BE\\u09D7\\u0B3E\\u0B57\\u0BBE\\u0BD7\\u0CC2\\u0CD5-\\u0CD6\\u0D3E\\u0D57\\u0DCF\\u0DDF\\u302E-\\u302F\\u{1133E}\\u{11357}\\u{114B0}\\u{114BD}\\u{115AF}\\u{1D165}\\u{1D16E}-\\u{1D172}]';
    var Nd = '[\\u{1D7CE}-\\u{1D7FF}\\uFF10-\\uFF19]';
    var Pc = '\\u2040';
    var Other_ID_Start = '[\\u1885-\\u1886\\u2118\\u212E\\u309B-\\u309C]';
    var Other_ID_Continue = '[\\u00B7\\u0387\\u1369-\\u1371\\u19DA]';
    var id_start = group(Lu, Ll,Lt, Lm, Lo, Nl, the_underscore, Other_ID_Start);
    var id_continue = group(id_start, Mn, Mc, Nd, Pc, Other_ID_Continue);

    // Fall back if we don't support unicode
    if (RegExp().unicode === false) {
        return new RegExp('^' + id_start + '+' + id_continue + '*$', 'u');
    } else {
        id_start = group(Lu, Ll, the_underscore);
        id_continue = group(id_start, '[0-9]');
        return new RegExp('^' + id_start + '+' + id_continue + '*$');
    }
})();

/**
 * test if string is an identifier
 *
 * @param {str} string
 * @returns {boolean}
 */
function isidentifier(str) {
    var normalized = str.normalize('NFKC');
    return IS_IDENTIFIER_REGEX.test(normalized);
}

/* we have to use string and ctor to be able to build patterns up. + on /.../
 * does something strange.
 * Note: we use unicode matching for names ("\w") but ascii matching for
 * number literals.
 *
 * I don't know if the comment above is still actually correct */
var Whitespace = "[ \\f\\t]*";
var Comment_ = "#[^\\r\\n]*";
var Ignore = Whitespace + any('\\\\\\r?\\n' + Whitespace) + maybe(Comment_)
var Name = "\\w+";


var Exponent = "[eE][-+]?[0-9](?:_?[0-9])*";
var Pointfloat = group('[0-9](?:_?[0-9])*\\.(?:[0-9](?:_?[0-9])*)?',
                       '\\.[0-9](?:_?[0-9])*') + maybe(Exponent)
var Expfloat = "[0-9](?:_?[0-9])*" + Exponent;
var Floatnumber = group(Pointfloat, Expfloat);
var Imagnumber = group("[0-9](?:_?[0-9])*[jJ]", Floatnumber + "[jJ]");

// Return the empty string, plus all of the valid string prefixes.
function _all_string_prefixes() {
    return [
        '', 'FR', 'RF', 'Br', 'BR', 'Fr', 'r', 'B', 'R', 'b', 'bR',
        'f', 'rb', 'rB', 'F', 'Rf', 'U', 'rF', 'u', 'RB', 'br', 'fR',
        'fr', 'rf', 'Rb'];
}

// Note that since _all_string_prefixes includes the empty string,
//  StringPrefix can be the empty string (making it optional).
var StringPrefix = group.apply(null, _all_string_prefixes())

// these regexes differ from python because .exec doesn't do the
// same thing as .match in python. It's more like .search.
// .match matches from the start of the string.
// to get the same behaviour we can add a ^ to the start of the
// regex
// Tail end of ' string.
var Single = "^[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";
// Tail end of " string.
var Double = '^[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
// Tail end of ''' string.
var Single3 = "^[^'\\\\]*(?:(?:\\\\.|'(?!''))[^'\\\\]*)*'''";
// Tail end of """ string.
var Double3 = '^[^"\\\\]*(?:(?:\\\\.|"(?!""))[^"\\\\]*)*"""';
var Triple = group(StringPrefix + "'''", StringPrefix + '"""');
// Single-line ' or " string.
var String_ = group(StringPrefix + "'[^\\n'\\\\]*(?:\\\\.[^\\n'\\\\]*)*'",
                    StringPrefix + '"[^\\n"\\\\]*(?:\\\\.[^\\n"\\\\]*)*"');

// Sorting in reverse order puts the long operators before their prefixes.
// Otherwise if = came before ==, == would get recognized as two instances
// of =.
var EXACT_TOKENS_SORTED;
var Special;
var Funny;

function setupTokens(py3) {
    // recompute the above two lines
    // <> should be included in py2 mode
    if (py3) {
        delete Sk.token.EXACT_TOKEN_TYPES["<>"];
    } else {
        Sk.token.EXACT_TOKEN_TYPES["<>"] = Sk.token.tokens.T_NOTEQUAL;
    }
    EXACT_TOKENS_SORTED = Object.keys(Sk.token.EXACT_TOKEN_TYPES).sort();
    Special = group.apply(
        this,
        EXACT_TOKENS_SORTED.reverse().map(function (t) {
            return regexEscape(t);
        })
    );
    Funny = group("\\r?\\n", Special);
}
setupTokens(true);

Sk.token.setupTokens = setupTokens;



// these aren't actually used
// var PlainToken = group(Number_, Funny, String_, Name);
// var Token = Ignore + PlainToken;

// First (or only) line of ' or " string.
var ContStr = group(StringPrefix + "'[^\\n'\\\\]*(?:\\\\.[^\\n'\\\\]*)*" +
                group("'", '\\\\\\r?\\n'),
                StringPrefix + '"[^\\n"\\\\]*(?:\\\\.[^\\n"\\\\]*)*' +
                group('"', '\\\\\\r?\\n'))
var PseudoExtras = group('\\\\\\r?\\n|$', Comment_, Triple);

// For a given string prefix plus quotes, endpats maps it to a regex
//  to match the remainder of that string. _prefix can be empty, for
//  a normal single or triple quoted string (with no prefix).
var endpats = {}
var prefixes = _all_string_prefixes();
for (let _prefix of prefixes) {
    endpats[_prefix + "'"] = Single
    endpats[_prefix + '"'] = Double
    endpats[_prefix + "'''"] = Single3
    endpats[_prefix + '"""'] = Double3
}

// A set of all of the single and triple quoted string prefixes,
//  including the opening quotes.
let single_quoted = []
let triple_quoted = []
for (let t of prefixes) {
    single_quoted.push(t + '"');
    single_quoted.push(t + "'");
    triple_quoted.push(t + '"""');
    triple_quoted.push(t + "'''");
}

var tabsize = 8

/**
 * internal tokenize function
 *
 * @param {function(): string} readline
 * @param {string} encoding
 * @param {function(TokenInfo): void} yield_
 */
function _tokenize(filename, readline, encoding, yield_) {
    // we make these regexes here because they can
    // be changed by the configuration.
    var LSuffix = !Sk.__future__.python3 ? '(?:L?)' : '';
    var Hexnumber = '0[xX](?:_?[0-9a-fA-F])+' + LSuffix;
    var Binnumber = '0[bB](?:_?[01])+' + LSuffix;
    var Octnumber = '0([oO])(?:_?[0-7])+' + LSuffix;
    var SilentOctnumber = '0([oO]?)(?:_?[0-7])+' + LSuffix;
    var Decnumber = '(?:0(?:_?0)*|[1-9](?:_?[0-9])*)' + LSuffix;
    var Intnumber = group(Hexnumber, Binnumber,
                          (Sk.__future__.silent_octal_literal ? SilentOctnumber : Octnumber), Decnumber);
    var Number_ = group(Imagnumber, Floatnumber, Intnumber);
    var PseudoToken = Whitespace + group(PseudoExtras, Number_, Funny, ContStr, Name);

    const PseudoTokenRegexp = new RegExp(PseudoToken);

    var lnum = 0,
        parenlev = 0,
        continued = 0,
        numchars = '0123456789',
        contstr = '',
        needcont = 0,
        contline = null,
        indents = [0],
        capos = null,
        endprog = undefined,
        strstart = undefined,
        end = undefined,
        pseudomatch = undefined;

    if (encoding !== undefined) {
        if (encoding == "utf-8-sig") {
            // BOM will already have been stripped.
            encoding = "utf-8";
        }

        yield_(new TokenInfo(tokens.T_ENCODING, encoding, [0, 0], [0, 0], ''));
    }

    var last_line = '';
    var line = '';
    while (true) {                                // loop over lines in stream
        try {
            // We capture the value of the line variable here because
            // readline uses the empty string '' to signal end of input,
            // hence `line` itself will always be overwritten at the end
            // of this loop.
            last_line = line;
            line = readline();
        } catch (Exception) {
            line = '';
        }

        // lets pretend this doesn't exist for now.
        // if encoding is not None:
        //     line = line.decode(encoding)
        lnum += 1;
        var pos = 0;
        var max = line.length;

        if (contstr) {                       // continued string
            if (!line) {
                throw new TokenError("EOF in multi-line string", filename, strstart[0], strstart[1]);
            }
            endprog.lastIndex = 0;
            var endmatch = endprog.exec(line);
            if (endmatch) {
                pos = end = endmatch[0].length;
                yield_(new TokenInfo(tokens.T_STRING, contstr + line.substring(0, end),
                       strstart, [lnum, end], contline + line));
                contstr = '';
                needcont = 0;
                contline = null;
            } else if (needcont && line.substring(line.length - 2) !== "\\\n" && line.substring(line.length - 3) !== "\\\r\n") {
                yield_(new TokenInfo(tokens.T_ERRORTOKEN, contstr + line,
                           strstart, [lnum, line.length], contline));
                contstr = '';
                contline = null;
                continue;
            } else {
                contstr = contstr + line;
                contline = contline + line;
                continue;
            }
        } else if (parenlev == 0 && !continued) {  // new statement
            if (!line) { break; }
            var column = 0;
            while (pos < max) {              // measure leading whitespace
                if (line[pos] == ' ') {
                    column += 1
                } else if (line[pos] == '\t') {
                    column = Math.floor(column/tabsize + 1) * tabsize;
                } else if (line[pos] == '\f') {
                    column = 0
                } else {
                    break;
                };
                pos += 1
            }

            if (pos == max) {
                break;
            }

            if (contains('#\r\n', line[pos])) {       // skip comments or blank lines
                if (line[pos] == '#') {
                    var comment_token = rstrip(line.substring(pos), '\r\n');
                    yield_(new TokenInfo(tokens.T_COMMENT, comment_token,
                           [lnum, pos], [lnum, pos + comment_token.length], line));
                    pos += comment_token.length;
                }

                yield_(new TokenInfo(tokens.T_NL, line.substring(pos),
                           [lnum, pos], [lnum, line.length], line));
                continue;
            }

            if (column > indents[indents.length - 1]) {           // count indents or dedents
                indents.push(column);
                yield_(new TokenInfo(tokens.T_INDENT, line.substring(pos), [lnum, 0], [lnum, pos], line));
            }

            while (column < indents[indents.length - 1]) {
                if (!contains(indents, column)) {
                    throw new IndentationError(
                        "unindent does not match any outer indentation level",
                        filename, lnum, pos);
                }

                indents = indents.slice(0, -1);

                yield_(new TokenInfo(tokens.T_DEDENT, '', [lnum, pos], [lnum, pos], line));
            }
        } else {                                  // continued statement
            if (!line) {
                throw new TokenError("EOF in multi-line statement", filename, lnum, 0);
            }
            continued = 0;
        }

        while (pos < max) {
            //console.log("pos:"+pos+":"+max);
            // js regexes don't return any info about matches, other than the
            // content. we'd like to put a \w+ before pseudomatch, but then we
            // can't get any data
            capos = line.charAt(pos);
            while (capos === ' ' || capos === '\f' || capos === '\t') {
                pos += 1;
                capos = line.charAt(pos);
            }

            pseudomatch = PseudoTokenRegexp.exec(line.substring(pos))
            if (pseudomatch) {                                // scan for tokens
                var start = pos;
                var end = start + pseudomatch[1].length;
                var spos = [lnum, start];
                var epos = [lnum, end];
                var pos = end;
                if (start == end) {
                    continue;
                }

                var token = line.substring(start, end);
                var initial = line[start];
                //console.log("token:",token, "initial:",initial, start, end);
                if (contains(numchars, initial) ||                 // ordinary number
                    (initial == '.' && token != '.' && token != '...')) {
                    yield_(new TokenInfo(tokens.T_NUMBER, token, spos, epos, line));
                } else if (contains('\r\n', initial)) {
                    if (parenlev > 0) {
                        yield_(new TokenInfo(tokens.T_NL, token, spos, epos, line));
                    } else {
                        yield_(new TokenInfo(tokens.T_NEWLINE, token, spos, epos, line));
                    }
                } else if (initial == '#') {
                    //assert not token.endswith("\n")
                    yield_(new TokenInfo(tokens.T_COMMENT, token, spos, epos, line));
                } else if (contains(triple_quoted, token)) {
                    endprog = RegExp(endpats[token]);
                    endmatch = endprog.exec(line.substring(pos));
                    if (endmatch) {                       // all on one line
                        pos = endmatch[0].length + pos;
                        token = line.substring(start, pos);
                        yield_(new TokenInfo(tokens.T_STRING, token, spos, [lnum, pos], line));
                    } else {
                        strstart = [lnum, start];           // multiple lines
                        contstr = line.substring(start);
                        contline = line;
                        break;
                    }
                // Check up to the first 3 chars of the token to see if
                //  they're in the single_quoted set. If so, they start
                //  a string.
                // We're using the first 3, because we're looking for
                //  "rb'" (for example) at the start of the token. If
                //  we switch to longer prefixes, this needs to be
                //  adjusted.
                // Note that initial == token[:1].
                // Also note that single quote checking must come after
                //  triple quote checking (above).
                } else if (contains(single_quoted, initial) ||
                           contains(single_quoted, token.substring(0, 2)) ||
                           contains(single_quoted, token.substring(0, 3))) {
                    if (token[token.length - 1] == '\n') {                // continued string
                        strstart = [lnum, start];
                        // Again, using the first 3 chars of the
                        //  token. This is looking for the matching end
                        //  regex for the correct type of quote
                        //  character. So it's really looking for
                        //  endpats["'"] or endpats['"'], by trying to
                        //  skip string prefix characters, if any.
                        endprog = RegExp(endpats[initial] ||
                                           endpats[token[1]] ||
                                           endpats[token[2]]);
                        contstr = line.substring(start);
                        needcont = 1;
                        contline = line;
                        break;
                    } else {                                  // ordinary string
                        yield_(new TokenInfo(tokens.T_STRING, token, spos, epos, line));
                    }

                } else if (isidentifier(initial)) {              // ordinary name
                    yield_(new TokenInfo(tokens.T_NAME, token, spos, epos, line));
                } else if (initial == '\\') {                  // continued stmt
                    continued = 1
                } else {
                    if (contains('([{', initial)) {
                        parenlev += 1
                    } else if (contains(')]}', initial)) {
                        parenlev -= 1
                    }
                    yield_(new TokenInfo(tokens.T_OP, token, spos, epos, line));
                }
            } else {
                yield_(new TokenInfo(tokens.T_ERRORTOKEN, line[pos],
                           [lnum, pos], [lnum, pos+1], line));
                pos += 1;
            }
        }
    }

    // Add an implicit NEWLINE if the input doesn't end in one
    if (last_line && !contains('\r\n', last_line[last_line.length - 1])) {
        yield_(new TokenInfo(tokens.T_NEWLINE, '', [lnum - 1, last_line.length], [lnum - 1, last_line.length + 1], ''));
    }
    for (var i in indents.slice(1)) {                 // pop remaining indent levels
        yield_(new TokenInfo(tokens.T_DEDENT, '', [lnum, 0], [lnum, 0], ''));
    }

    yield_(new TokenInfo(tokens.T_ENDMARKER, '', [lnum, 0], [lnum, 0], ''));
}

Sk._tokenize = _tokenize;

Sk.exportSymbol("Sk._tokenize", Sk._tokenize);


/***/ }),

/***/ "./src/tuple.js":
/*!**********************!*\
  !*** ./src/tuple.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Array} L
 * @extends {Sk.builtin.object}
 */
Sk.builtin.tuple = Sk.abstr.buildNativeClass("tuple", {
    constructor: function tuple(L) {
        if (L === undefined) {
            L = [];
        } else if (!Array.isArray(L)) {
            L = Sk.misceval.arrayFromIterable(L); 
            // internal calls to constructor can't suspend - avoid using this
        }
        Sk.asserts.assert(this instanceof Sk.builtin.tuple, "bad call to tuple, use 'new' with an Array of python objects");
        this.v = L;
        this.in$repr = false;
    },
    slots: /**@lends {Sk.builtin.tuple.prototype}*/ {
        tp$getattr: Sk.generic.getAttr,
        tp$as_sequence_or_mapping: true,
        tp$doc:
            "Built-in immutable sequence.\n\nIf no argument is given, the constructor returns an empty tuple.\nIf iterable is specified the tuple is initialized from iterable's items.\n\nIf the argument is a tuple, the return value is the same object.",
        $r() {
            if (this.in$repr) {
                return new Sk.builtin.str("(...)");
            }
            this.in$repr = true;
            let ret = this.v.map((x) => Sk.misceval.objectRepr(x));
            this.in$repr = false;
            ret = ret.join(", ");
            if (this.v.length === 1) {
                ret += ",";
            }
            return new Sk.builtin.str("(" + ret + ")");
        },
        /**
         * @param {Array} args
         * @param {Array=} kwargs
         * @ignore
         */
        tp$new(args, kwargs) {
            // this = Sk.builtin.prototype or a prototype that inherits from Sk.builtin.tuple.prototype
            if (this !== Sk.builtin.tuple.prototype) {
                return this.$subtype_new(args, kwargs);
            }
            Sk.abstr.checkNoKwargs("tuple", kwargs);
            Sk.abstr.checkArgsLen("tuple", args, 0, 1);
            const arg = args[0];
            if (arg === undefined) {
                return new Sk.builtin.tuple([]);
            }
            if (arg.constructor === Sk.builtin.tuple) {
                return arg;
            }
            return Sk.misceval.chain(Sk.misceval.arrayFromIterable(arg, true), (L) => new Sk.builtin.tuple(L));
        },
        tp$hash() {
            // the numbers and order are taken from Cpython
            let y,
                x = 0x345678,
                mult = 1000003;
            const len = this.v.length;
            for (let i = 0; i < len; ++i) {
                y = Sk.abstr.objectHash(this.v[i]);
                if (y === -1) {
                    return -1;
                }
                x = (x ^ y) * mult;
                mult += 82520 + len + len;
            }
            x += 97531;
            if (x === -1) {
                x = -2;
            }
            return x | 0;
        },
        tp$richcompare: Sk.generic.seqCompare,
        tp$iter() {
            return new tuple_iter_(this);
        },

        // sequence and mapping slots
        mp$subscript(index) {
            if (Sk.misceval.isIndex(index)) {
                let i = Sk.misceval.asIndexSized(index);
                if (i < 0) {
                    i = this.v.length + i;
                }
                if (i < 0 || i >= this.v.length) {
                    throw new Sk.builtin.IndexError("tuple index out of range");
                }
                return this.v[i];
            } else if (index instanceof Sk.builtin.slice) {
                const ret = [];
                index.sssiter$(this.v.length, (i) => {
                    ret.push(this.v[i]);
                });
                return new Sk.builtin.tuple(ret);
            }
            throw new Sk.builtin.TypeError("tuple indices must be integers or slices, not " + Sk.abstr.typeName(index));
        },
        sq$length() {
            return this.v.length;
        },
        sq$repeat(n) {
            n = Sk.misceval.asIndexSized(n, Sk.builtin.OverflowError);
            if (n === 1 && this.constructor === Sk.builtin.tuple) {
                return this;
            }
            const ret = [];
            for (let i = 0; i < n; i++) {
                for (let j = 0; j < this.v.length; j++) {
                    ret.push(this.v[j]);
                }
            }
            return new Sk.builtin.tuple(ret);
        },
        sq$concat(other) {
            if (!(other instanceof Sk.builtin.tuple)) {
                throw new Sk.builtin.TypeError("can only concatenate tuple (not '" + Sk.abstr.typeName(other) + "') to tuple");
            }
            return new Sk.builtin.tuple(this.v.concat(other.v));
        },
        sq$contains(ob) {
            for (let it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
                if (i === ob || Sk.misceval.richCompareBool(i, ob, "Eq")) {
                    return true;
                }
            }
            return false;
        },
    },
    proto: /**@lends {Sk.builtin.tuple.prototype}*/ {
        $subtype_new(args, kwargs) {
            const instance = new this.constructor();
            // pass the args but ignore the kwargs for subtyping - these might be handled by the subtypes init method
            const tuple = Sk.builtin.tuple.prototype.tp$new(args);
            instance.v = tuple.v;
            return instance;
        },
        sk$asarray() {
            return this.v.slice(0);
        },
    },
    methods: /**@lends {Sk.builtin.tuple.prototype}*/ {
        __getnewargs__: {
            $meth() {
                return new Sk.builtin.tuple(this.v.slice(0));
            },
            $flags: { NoArgs: true },
            $textsig: "($self, /)",
            $doc: null,
        },
        index: /**@lends {Sk.builtin.type.prototype}*/ {
            $meth(item, start, end) {
                if ((start !== undefined && !Sk.misceval.isIndex(start)) || (end !== undefined && !Sk.misceval.isIndex(end))) {
                    // unusually can't have None here so check this first...
                    throw new Sk.builtin.TypeError("slice indices must be integers or have an __index__ method");
                }
                ({ start, end } = Sk.builtin.slice.startEnd$wrt(this, start, end));
                const obj = this.v;
                for (let i = start; i < end; i++) {
                    if (obj[i] === item || Sk.misceval.richCompareBool(obj[i], item, "Eq")) {
                        return new Sk.builtin.int_(i);
                    }
                }
                throw new Sk.builtin.ValueError("tuple.index(x): x not in tuple");
            },
            $flags: { MinArgs: 1, MaxArgs: 3 },
            $textsig: "($self, value, start=0, stop=sys.maxsize, /)",
            $doc: "Return first index of value.\n\nRaises ValueError if the value is not present.",
        },
        count: {
            $meth(item) {
                const len = this.v.length;
                const obj = this.v;
                let count = 0;
                for (let i = 0; i < len; ++i) {
                    if (obj[i] === item || Sk.misceval.richCompareBool(obj[i], item, "Eq")) {
                        count += 1;
                    }
                }
                return new Sk.builtin.int_(count);
            },
            $flags: { OneArg: true },
            $textsig: "($self, value, /)",
            $doc: "Return number of occurrences of value.",
        },
    },
});

Sk.exportSymbol("Sk.builtin.tuple", Sk.builtin.tuple);

/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @param {Sk.builtin.tuple} tuple
 * @private
 */
var tuple_iter_ = Sk.abstr.buildIteratorClass("tuple_iterator", {
    constructor: function tuple_iter_(tuple) {
        this.$index = 0;
        this.$seq = tuple.sk$asarray();
    },
    iternext: Sk.generic.iterNextWithArray,
    methods: {
        __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef,
    },
    flags: { sk$acceptable_as_base_class: false },
});


/***/ }),

/***/ "./src/type.js":
/*!*********************!*\
  !*** ./src/type.js ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @namespace Sk.builtin
 *
 * @description
 * All the builtin types as well as useful functions
 */
if (Sk.builtin === undefined) {
    Sk.builtin = {};
}

/**
 * @constructor
 * @extends {Sk.builtin.object}
 * @description
 * this should never be called as a constructor
 * instead use {@link Sk.abstr.buildNativeClass} or
 * {@link Sk.misceval.buildClass}
 *
 */
Sk.builtin.type = function type(obj) {
    if (this instanceof Sk.builtin.type) {
        Sk.asserts.fail("calling new Sk.builtin.type is not safe");
    }
    return obj.ob$type; // allow this use of calling type
};

Object.defineProperties(
    Sk.builtin.type.prototype,
    /**@lends {Sk.builtin.type.prototype}*/ {
        call: { value: Function.prototype.call },
        apply: { value: Function.prototype.apply },
        tp$slots: {
            value: {
                tp$doc: "type(object_or_name, bases, dict)\ntype(object) -> the object's type\ntype(name, bases, dict) -> a new type",
                tp$call,
                tp$new,
                tp$getattr,
                tp$setattr,
                $r,
            },
            writable: true,
        },
        tp$methods: { value: null, writable: true }, // define these later
        tp$getsets: { value: null, writable: true },
        sk$type: { value: true },
        $isSubType: { value: $isSubType },
        $allocateSlot: { value: $allocateSlot },
        $allocateSlots: { value: $allocateSlots },
        $allocateGetterSlot: { value: $allocateGetterSlot },
        $typeLookup: { value: $typeLookup, writable: true },
        $mroMerge: { value: $mroMerge },
        $buildMRO: { value: $buildMRO },
        sk$attrError: {
            value() {
                return "type object '" + this.prototype.tp$name + "'";
            },
            writable: true,
        },
    }
);

/**
 * @this {typeObject | Sk.builtin.type}
 */
function tp$call(args, kwargs) {
    if (this === Sk.builtin.type) {
        // check the args are 1 - only interested in the 1 argument form if
        // if the nargs and nkeywords != 1 or 3 and zero raise an error
        if (args.length === 1 && (kwargs === undefined || !kwargs.length)) {
            return args[0].ob$type;
        } else if (args.length !== 3) {
            throw new Sk.builtin.TypeError("type() takes 1 or 3 arguments");
        }
    }
    let obj = this.prototype.tp$new(args, kwargs);

    if (obj.$isSuspension) {
        return Sk.misceval.chain(
            obj,
            (o) => {
                obj = o;
                if (!obj.ob$type.$isSubType(this)) {
                    // don't initialize an obj if it's type is not a subtype of this!
                    // typically obj$obtype === self so this check is fast
                    return;
                }
                return obj.tp$init(args, kwargs);
            },
            () => obj
        );
    } else if (!obj.ob$type.$isSubType(this)) {
        return obj;
    } else {
        const res = obj.tp$init(args, kwargs);
        if (res !== undefined && res.$isSuspension) {
            return Sk.misceval.chain(res, () => obj);
        }
        return obj;
    }
}

function tp$new(args, kwargs) {
    // currently skulpt does not support metatypes...
    // metatype.prototype = this
    if (args.length !== 3) {
        if (args.length === 1 && (kwargs === undefined || !kwargs.length)) {
            return args[0].ob$type;
        }
        throw new Sk.builtin.TypeError("type() takes 1 or 3 arguments");
    }

    let $name, bases, dict;
    $name = args[0];
    bases = args[1];
    dict = args[2];
    // first check that we only have 3 args and they're of the correct type
    // argument dict must be of type dict
    if (dict.tp$name !== "dict") {
        throw new Sk.builtin.TypeError("type() argument 3 must be dict, not " + Sk.abstr.typeName(dict));
    }
    // checks if name must be string
    if (!Sk.builtin.checkString($name)) {
        throw new Sk.builtin.TypeError("type() argument 1 must be str, not " + Sk.abstr.typeName($name));
    }
    $name = $name.$jsstr();
    // argument bases must be of type tuple
    if (bases.tp$name !== "tuple") {
        throw new Sk.builtin.TypeError("type() argument 2 must be tuple, not " + Sk.abstr.typeName(bases));
    }
    bases = bases.sk$asarray();

    /**
     * @type {!typeObject}
     */
    const klass = function () {
        // klass is essentially a function that gives its instances a dict
        // if we support slots then we might need to have two versions of this
        this.$d = new Sk.builtin.dict();
    };
    setUpKlass($name, klass, bases, this.constructor);

    // set some defaults which can be overridden by the dict object
    if (Sk.globals) {
        klass.prototype.__module__ = Sk.globals["__name__"];
    }
    klass.prototype.__doc__ = Sk.builtin.none.none$;

    // set __dict__ if not already on the prototype
    /**@todo __slots__ */
    if (klass.$typeLookup(Sk.builtin.str.$dict) === undefined) {
        klass.prototype.__dict__ = new Sk.builtin.getset_descriptor(klass, subtype_dict_getset_description);
    }

    // copy properties from dict into klass.prototype
    dict.$items().forEach(([key, val]) => {
        klass.prototype[key.$mangled] = val;
    });
    // make __new__ a static method
    if (klass.prototype.hasOwnProperty("__new__")) {
        const newf = klass.prototype.__new__;
        if (newf instanceof Sk.builtin.func) {
            // __new__ is an implied staticmethod
            klass.prototype.__new__ = new Sk.builtin.staticmethod(newf);
        }
    }
    klass.$allocateSlots();

    return klass;
}

/**
 * @param {Array} args
 * @param {Array=} kwargs
 */
function tp$init(args, kwargs) {
    if (args && args.length == 1 && kwargs && kwargs.length) {
        throw new Sk.builtin.TypeError("type.__init__() takes no keyword arguments");
    } else if (args.length != 3 && args.length != 1) {
        throw new Sk.builtin.TypeError("type.__init__() takes 1 or 3 arguments");
    }
    // according to Cpython we just call the object init method here
    return Sk.builtin.object.prototype.tp$init.call(this, []);
}

function $r() {
    let mod = this.prototype.__module__;
    let cname = "";
    let ctype = "class";
    if (mod && Sk.builtin.checkString(mod)) {
        cname = mod.v + ".";
    } else {
        mod = null;
    }
    if (!mod && !this.sk$klass && !Sk.__future__.class_repr) {
        ctype = "type";
    }
    return new Sk.builtin.str("<" + ctype + " '" + cname + this.prototype.tp$name + "'>");
}

function tp$getattr(pyName, canSuspend) {
    // first check that the pyName is indeed a string
    let res;
    const metatype = this.ob$type;
    // now check whether there is a descriptor on the metatype
    const meta_attribute = metatype.$typeLookup(pyName);

    let meta_get;
    if (meta_attribute !== undefined) {
        meta_get = meta_attribute.tp$descr_get;
        if (meta_get !== undefined && meta_attribute.tp$descr_set !== undefined) {
            // then we're a data descriptor
            res = meta_get.call(meta_attribute, this, metatype, canSuspend);
            return res;
        }
    }
    const attribute = this.$typeLookup(pyName);

    if (attribute !== undefined) {
        const local_get = attribute.tp$descr_get;
        if (local_get !== undefined) {
            // null indicates that the descriptor was on the target object itself or a buss
            res = local_get.call(attribute, null, this, canSuspend);
            return res;
        }
        return attribute;
    }
    // attribute was not found so use the meta_get if any
    if (meta_get !== undefined) {
        res = meta_get.call(meta_attribute, this, metatype, canSuspend);
        return res;
    }

    if (meta_attribute !== undefined) {
        return meta_attribute;
    }
    return;
}

function tp$setattr(pyName, value, canSuspend) {
    if (!this.sk$klass) {
        if (value !== undefined) {
            throw new Sk.builtin.TypeError("can't set attributes of built-in/extension type '" + this.prototype.tp$name + "'");
        } else {
            throw new Sk.builtin.TypeError("can't delete attributes on type object '" + this.prototype.tp$name + "'");
        }
    }
    // meta types must follow single inheritance - we could change this and do
    const descr = this.ob$type.$typeLookup(pyName);

    // if it's a data descriptor then call it
    if (descr !== undefined) {
        const f = descr.tp$descr_set;
        if (f) {
            return f.call(descr, this, value, canSuspend);
        }
    }
    // for delattr
    const jsName = pyName.$mangled;

    if (value === undefined) {
        const proto = this.prototype;
        if (!proto.hasOwnProperty(jsName)) {
            throw new Sk.builtin.AttributeError("type object '" + this.prototype.tp$name + "' has no attribute '" + pyName.$jsstr() + "'");
        } else {
            delete proto[jsName];
            // delete the slot_func
            // TODO what about slot funcs that are dual slots...
            const slot_name = Sk.dunderToSkulpt[jsName];
            if (slot_name !== undefined) {
                delete this.prototype[slot_name];
                if (!proto.sk$prototypical) {
                    this.$allocateGetterSlot(jsName);
                    // if this was a slot func and we are not prototypical
                    // allocate a getter slot in it's place
                }
            }
        }
    } else {
        this.prototype[jsName] = value;
        if (jsName in Sk.dunderToSkulpt) {
            this.$allocateSlot(jsName, value);
        }
    }
}

function fastLookup(pyName) {
    var jsName = pyName.$mangled;
    return this.prototype[jsName];
}

function slowLookup(pyName) {
    var jsName = pyName.$mangled;
    const mro = this.prototype.tp$mro;
    for (let i = 0; i < mro.length; ++i) {
        const base_proto = mro[i].prototype;
        if (base_proto.hasOwnProperty(jsName)) {
            return base_proto[jsName];
        }
    }
    return undefined;
}

function $typeLookup(pyName) {
    // all type objects override this function depending on they're prototypical inheritance
    // we use the logic here as a fall back
    if (this.prototype.sk$prototypical) {
        return fastLookup.call(this, pyName);
    }
    return slowLookup.call(this, pyName);
}

function $isSubType(other) {
    return this === other || this.prototype instanceof other || (!this.prototype.sk$prototypical && this.prototype.tp$mro.includes(other));
}

function setUpKlass($name, klass, bases, meta) {
    // this function tries to match Cpython - the best base is not always bases[0]
    // we require a best bases for checks in __new__ as well as future support for slots
    const best_base = best_base_(bases);
    const klass_proto = klass.prototype;

    Sk.abstr.setUpInheritance($name, klass, best_base, meta);

    Object.defineProperties(klass_proto, {
        sk$prototypical: { value: true, writable: true },
        tp$bases: { value: bases, writable: true },
        tp$mro: { value: null, writable: true },
        hp$type: { value: true, writable: true },
    });
    klass_proto.tp$mro = klass.$buildMRO();

    Object.defineProperties(klass, {
        $typeLookup: { value: klass_proto.sk$prototypical ? fastLookup : slowLookup, writable: true },
        sk$klass: { value: true, writable: true },
    });
}

// this function is used to determine the class constructor inheritance.
function best_base_(bases) {
    if (bases.length === 0) {
        bases.push(Sk.builtin.object);
    }
    function solid_base(type) {
        // if we support slots we would need to change this function - for now it just checks for the builtin.
        if (type.sk$klass === undefined) {
            return type;
        }
        return solid_base(type.prototype.tp$base);
    }

    let base, winner, candidate, base_i;
    for (let i = 0; i < bases.length; i++) {
        base_i = bases[i];
        if (!Sk.builtin.checkClass(base_i)) {
            throw new Sk.builtin.TypeError("bases must be 'type' objects");
        } else if (base_i.sk$acceptable_as_base_class === false) {
            throw new Sk.builtin.TypeError("type '" + base_i.prototype.tp$name + "' is not an acceptable base type");
        }
        candidate = solid_base(base_i); // basically the builtin I think
        if (winner === undefined) {
            winner = candidate;
            base = base_i;
        } else if (winner.$isSubType(candidate)) {
            // carry on
        } else if (candidate.$isSubType(winner)) {
            winner = candidate;
            base = base_i;
        } else {
            throw new Sk.builtin.TypeError("multiple bases have instance layout conflicts");
        }
    }
    return base;
}

function $mroMerge(seqs) {
    this.prototype.sk$prototypical = true; // assume true to start with
    let seq, i, j;
    const res = [];
    for (;;) {
        for (i = 0; i < seqs.length; ++i) {
            seq = seqs[i];
            if (seq.length !== 0) {
                break;
            }
        }
        if (i === seqs.length) {
            // all empty
            return res;
        }
        const cands = [];
        for (i = 0; i < seqs.length; ++i) {
            seq = seqs[i];
            //print("XXX", Sk.builtin.repr(new Sk.builtin.list(seq)).v);
            if (seq.length !== 0) {
                const cand = seq[0];
                //print("CAND", Sk.builtin.repr(cand).v);

                /* eslint-disable */
                OUTER: for (j = 0; j < seqs.length; ++j) {
                    const sseq = seqs[j];
                    for (let k = 1; k < sseq.length; ++k) {
                        if (sseq[k] === cand) {
                            break OUTER;
                        }
                    }
                }
                /* eslint-enable */

                // cand is not in any sequences' tail -> constraint-free
                if (j === seqs.length) {
                    cands.push(cand);
                }
            }
        }

        if (cands.length === 0) {
            throw new Sk.builtin.TypeError("Inconsistent precedences in type hierarchy");
        }

        const next = cands[0];

        // check prototypical mro
        if (res.length && this.prototype.sk$prototypical) {
            let prevs_prototype = Object.getPrototypeOf(res[res.length - 1].prototype);
            if (prevs_prototype !== next.prototype) {
                this.prototype.sk$prototypical = false;
            }
        }

        // append next to result and remove from sequences
        res.push(next);

        for (i = 0; i < seqs.length; ++i) {
            seq = seqs[i];
            if (seq.length > 0 && seq[0] === next) {
                seq.splice(0, 1);
            }
        }
    }
}

/*
 * C3 MRO (aka CPL) linearization. Figures out which order to search through
 * base classes to determine what should override what. C3 does the "right
 * thing", and it's what Python has used since 2.3.
 *
 * Kind of complicated to explain, but not really that complicated in
 * implementation. Explanations:
 *
 * http://people.csail.mit.edu/jrb/goo/manual.43/goomanual_55.html
 * http://www.python.org/download/releases/2.3/mro/
 * http://192.220.96.201/dylan/linearization-oopsla96.html
 *
 * This implementation is based on a post by Samuele Pedroni on python-dev
 * (http://mail.python.org/pipermail/python-dev/2002-October/029176.html) when
 * discussing its addition to Python.
 */
function $buildMRO() {
    // MERGE(klass + mro(bases) + bases)
    const all = [[this]];
    const kbases = this.prototype.tp$bases;

    for (let i = 0; i < kbases.length; ++i) {
        all.push([...kbases[i].prototype.tp$mro]);
    }

    const bases = [];
    for (let i = 0; i < kbases.length; ++i) {
        bases.push(kbases[i]);
    }
    all.push(bases);

    return this.$mroMerge(all);
}

function $allocateSlots() {
    // only allocate certain slots
    const proto = this.prototype;
    if (this.prototype.sk$prototypical) {
        Object.keys(proto).forEach((dunder) => {
            if (dunder in Sk.slots) {
                this.$allocateSlot(dunder, proto[dunder]);
            }
        });
    } else {
        Object.keys(Sk.slots).forEach((dunder) => {
            if (proto.hasOwnProperty(dunder)) {
                this.$allocateSlot(dunder, proto[dunder]);
            } else {
                this.$allocateGetterSlot(dunder);
            }
        });
    }
}

function $allocateSlot(dunder, dunderFunc) {
    const slot_def = Sk.slots[dunder];
    const slot_name = slot_def.$slot_name;
    const proto = this.prototype;
    if (proto.hasOwnProperty(slot_name)) {
        delete proto[slot_name]; // required in order to override the multiple inheritance getter slots
    }
    proto[slot_name] = slot_def.$slot_func(dunderFunc);
}

function $allocateGetterSlot(dunder) {
    const slot_name = Sk.slots[dunder].$slot_name;
    const proto = this.prototype;
    if (proto.hasOwnProperty(slot_name)) {
        return; // double slots can be problematic
    }
    Object.defineProperty(proto, slot_name, {
        configurable: true,
        get() {
            const mro = proto.tp$mro;
            for (let i = 1; i < mro.length; i++) {
                const base_proto = mro[i].prototype;
                const property = Object.getOwnPropertyDescriptor(base_proto, slot_name);
                if (property !== undefined && property.value) {
                    return property.value;
                }
            }
        },
    });
}

Sk.builtin.type.prototype.tp$getsets = {
    __base__: {
        $get() {
            return this.prototype.tp$base || Sk.builtin.none.none$;
        },
    },
    __bases__: {
        $get() {
            if (this.sk$tuple_bases === undefined) {
                this.sk$tuple_bases = new Sk.builtin.tuple(this.prototype.tp$bases);
                // make sure we always return the same tuple
            }
            return this.sk$tuple_bases;
        },
    },
    __mro__: {
        $get() {
            if (this.sk$tuple_mro === undefined) {
                this.sk$tuple_mro = new Sk.builtin.tuple(this.prototype.tp$mro);
                // make sure we always return the same tuple
            }
            return this.sk$tuple_mro;
        },
    },
    __dict__: {
        $get() {
            return new Sk.builtin.mappingproxy(this.prototype);
        },
    },
    __doc__: {
        $get() {
            const doc = this.$typeLookup(Sk.builtin.str.$doc);
            if (doc) {
                if (doc.tp$descr_get !== undefined) {
                    if (this === Sk.builtin.type) {
                        return new Sk.builtin.str(this.prototype.tp$doc);
                    }
                    return doc.tp$descr_get(null, this);
                }
                return this.prototype.__doc__;
            }
            return Sk.builtin.none.none$;
        },
        $set(value) {
            check_special_type_attr(this, value, Sk.builtin.str.$doc);
            this.prototype.__doc__ = value;
        },
    },
    __name__: {
        $get() {
            return new Sk.builtin.str(this.prototype.tp$name);
        },
        $set(value) {
            check_special_type_attr(this, value, Sk.builtin.str.$name);
            if (!Sk.builtin.checkString(value)) {
                throw new Sk.builtin.TypeError(
                    "can only assign string to " + this.prototype.tp$name + ".__name__, not '" + Sk.abstr.typeName(value) + "'"
                );
            }
            this.prototype.tp$name = value.$jsstr();
        },
    },
    __module__: {
        $get() {
            let mod = this.prototype.__module__;
            if (mod && !(mod.ob$type === Sk.builtin.getset_descriptor)) {
                return mod;
            }
            return new Sk.builtin.str("builtins");
        },
        $set(value) {
            // they can set the module to whatever they like
            check_special_type_attr(this, value, Sk.builtin.str.$module);
            this.prototype.__module__ = value;
        },
    },
};

Sk.builtin.type.prototype.tp$methods = /**@lends {Sk.builtin.type.prototype}*/ {
    mro: {
        $meth() {
            return new Sk.builtin.list(this.$buildMRO());
        },
        $flags: { NoArgs: true },
    },
    __dir__: {
        $meth: function __dir__() {
            const seen = new Set();
            const dir = [];
            function push_or_continue(attr) {
                if (attr in Sk.reservedWords_) {
                    return;
                }
                attr = Sk.unfixReserved(attr);
                if (attr.indexOf("$") !== -1) {
                    return;
                }
                if (!seen.has(attr)) {
                    seen.add(attr);
                    dir.push(new Sk.builtin.str(attr));
                }
            }
            if (this.prototype.sk$prototypical) {
                for (let attr in this.prototype) {
                    push_or_continue(attr);
                }
            } else {
                const mro = this.prototype.tp$mro;
                for (let i = 0; i < mro.length; i++) {
                    const attrs = Object.getOwnPropertyNames(mro[i].prototype);
                    for (let j = 0; j < attrs.length; j++) {
                        push_or_continue(attrs[j]);
                    }
                }
            }
            return new Sk.builtin.list(dir);
        },
        $flags: { NoArgs: true },
        $doc: "Specialized __dir__ implementation for types.",
    },
};

// similar to generic.getSetDict but have to check if there is a builtin __dict__ descriptor that we should use first!
const subtype_dict_getset_description = {
    $get() {
        const dict_descr = get_dict_descr_of_builtn_base(this.ob$type);
        if (dict_descr !== undefined) {
            return dict_descr.tp$descr_get(this, this.ob$type);
        }
        return Sk.generic.getSetDict.$get.call(this);
    },
    $set(value) {
        const dict_descr = get_dict_descr_of_builtn_base(this.ob$type);
        if (dict_descr !== undefined) {
            return dict_descr.tp$descr_set(this, value);
        }
        return Sk.generic.getSetDict.$set.call(this, value);
    },
    $doc: "dictionary for instance variables (if defined)",
    $name: "__dict__",
};

function get_dict_descr_of_builtn_base(type) {
    while (type.prototype.tp$base !== null) {
        if (type.sk$klass === undefined) {
            if (type.prototype.hasOwnProperty("__dict__")) {
                const descr = type.prototype.__dict__;
                return Sk.builtin.checkDataDescr(descr) ? descr : undefined;
            }
        }
        type = type.prototype.tp$base;
    }
}

function check_special_type_attr(type, value, pyName) {
    if (type.sk$klass === undefined) {
        throw new Sk.builtin.TypeError("can't set " + type.prototype.tp$name + "." + pyName.$jsstr());
    }
    if (value === undefined) {
        throw new Sk.builtin.TypeError("can't delete " + type.prototype.tp$name + "." + pyName.$jsstr());
    }
}


/***/ }),

/***/ "./src/util.js":
/*!*********************!*\
  !*** ./src/util.js ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {// Global Sk object
var Sk = {}; // jshint ignore:line

Sk.build = {
    githash: "e3c1c1a4e081362d96ba8afc5997be516b437f30",
    date: "2021-03-25T11:35:22.124Z"
};

/**
 * Global object no matter where we're running
 */
Sk.global =
    typeof global !== "undefined" ? global : // jshint ignore:line
    typeof self !== "undefined" ? self : // jshint ignore:line
    typeof window !== "undefined" ? window : // jshint ignore:line
    {};

/**
 * Export "object" to global namespace as "name".
 *
 * @param {string} name name to export the object to
 * @param {*} object object to export
 */
Sk.exportSymbol = function (name, object) {
    var parts = name.split(".");
    var curobj = Sk.global;
    var part, idx;

    for (idx = 0; idx < (parts.length - 1); idx++) {
        part = parts[idx];

        if (curobj.hasOwnProperty(part)) {
            curobj = curobj[part];
        } else {
            curobj = curobj[part] = {};
        }
    }

    if (typeof object !== "undefined") {
        part = parts[idx];
        curobj[part] = object;
    }
};

Sk.isArrayLike = function (object) {
    if ((object instanceof Array) || (object && object.length && (typeof object.length == "number"))) {
        return true;
    }
    return false;
};

Sk.js_beautify = function (x) {
    return x;
};

Sk.exportSymbol("Sk", Sk);
Sk.exportSymbol("Sk.global", Sk.global);
Sk.exportSymbol("Sk.build", Sk.build);
Sk.exportSymbol("Sk.exportSymbol", Sk.exportSymbol);
Sk.exportSymbol("Sk.isArrayLike", Sk.isArrayLike);
Sk.exportSymbol("Sk.js_beautify", Sk.js_beautify);

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./src/zip.js":
/*!********************!*\
  !*** ./src/zip.js ***!
  \********************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @constructor
 * @param {Array} JS Array of iterator objects
 * @extends Sk.builtin.object
 */
Sk.builtin.zip_ = Sk.abstr.buildIteratorClass("zip", {
    constructor: function zip_(iters) {
        this.$iters = iters;
        if (iters.length === 0) {
            this.tp$iternext = () => undefined;
        }
    },
    iternext(canSuspend) {
        const tup = [];
        const ret = Sk.misceval.chain(
            Sk.misceval.iterArray(this.$iters, (it) =>
                Sk.misceval.chain(it.tp$iternext(canSuspend), (i) => {
                    if (i === undefined) {
                        return new Sk.misceval.Break(true);
                    }
                    tup.push(i);
                })
            ),
            (endzip) => (endzip ? undefined : new Sk.builtin.tuple(tup))
        );
        return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret);
    },
    slots: {
        tp$doc:
            "zip(iter1 [,iter2 [...]]) --> zip object\n\nReturn a zip object whose .__next__() method returns a tuple where\nthe i-th element comes from the i-th iterable argument.  The .__next__()\nmethod continues until the shortest iterable in the argument sequence\nis exhausted and then it raises StopIteration.",
        tp$new(args, kwargs) {
            if (this === Sk.builtin.zip_.prototype) {
                Sk.abstr.checkNoKwargs("zip", kwargs);
            }
            const iters = [];
            for (let i = 0; i < args.length; i++) {
                try {
                    iters.push(Sk.abstr.iter(args[i]));
                } catch (e) {
                    if (e instanceof Sk.builtin.TypeError) {
                        throw new Sk.builtin.TypeError("zip argument #" + (i + 1) + " must support iteration");
                    } else {
                        throw e;
                    }
                }
            }
            if (this === Sk.builtin.zip_.prototype) {
                return new Sk.builtin.zip_(iters);
            } else {
                const instance = new this.constructor();
                Sk.builtin.zip_.call(instance, iters);
                return instance;
            }
        },
    },
});
Sk.exportSymbol("Sk.builtin.zip_", Sk.builtin.zip_);



/***/ }),

/***/ "./support/polyfills/JSBI.js":
/*!***********************************!*\
  !*** ./support/polyfills/JSBI.js ***!
  \***********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * functional approach to BigInt
 *
 * We use the JSBI library if BigInt is not available
 * https://github.com/GoogleChromeLabs/jsbi/
 *
 * If BigInt is available then we use the same functions as defined in the JSBI library
 * but use BigInt as the primitive type
 *
 */
const __JSBI = __webpack_require__(/*! jsbi */ "./node_modules/jsbi/dist/jsbi-umd.js");
// use jsbi which is es5 compliant - change to ES6 in the compilation version

const JSBI = Sk.global.JSBI = Sk.global.BigInt !== undefined ? {} : __JSBI;

if (Sk.global.BigInt === undefined) {
    // __isBigInt is not part of the public api so include it if this is ever removed
    const __isBigInt = JSBI.__isBigInt; // fixes a bug with null values passed to __isBigInt
    JSBI.__isBigInt = __isBigInt ? (x) => x !== null && __isBigInt(x) : (x) => x instanceof JSBI;
    JSBI.powermod = (x, y, z) => {
        const One = JSBI.BigInt(1);
        let number = One;
        y = JSBI.greaterThan(y, JSBI.__ZERO) ? y : JSBI.unaryMinus(y);
        while (JSBI.greaterThan(y, JSBI.__ZERO)) {
            if (JSBI.bitwiseAnd(y, One)) {
                number = JSBI.remainder(JSBI.multiply(number, x), z);
            }
            y = JSBI.signedRightShift(y, One);
            x = JSBI.remainder(JSBI.multiply(x, x), z);
        }
        return number;
    };
} else {
    Object.assign(JSBI, {
        BigInt: Sk.global.BigInt,
        toNumber: (x) => Number(x),
        toString: (x) => x.toString(),
        __isBigInt: (x) => typeof x === "bigint",
        unaryMinus: (x) => -x,
        bitwiseNot: (x) => ~x,
        bitwiseAnd: (x, y) => x & y,
        bitwiseOr: (x, y) => x | y,
        bitwiseXor: (x, y) => x ^ y,
        /**
         * x**y would be better but closure compilere changes that to Math.pow
         * https://github.com/google/closure-compiler/issues/3684 */
        exponentiate: (x, y) => {
            const One = JSBI.BigInt(1);
            let number = One;
            y = y > JSBI.__ZERO ? y : -y;
            while (y > JSBI.__ZERO) {
                if (y & One) {
                    number = number * x;
                }
                y = y >> One;
                x = x * x;
            }
            return number;
        },
        powermod: (x, y, z) => {
            const One = JSBI.BigInt(1);
            let number = One;
            y = y > JSBI.__ZERO ? y : -y;
            while (y > JSBI.__ZERO) {
                if (y & One) {
                    number = (number * x) % z;
                }
                y = y >> One;
                x = (x * x) % z;
            }
            return number;
        },
        multiply: (x, y) => x * y,
        divide: (x, y) => x / y,
        remainder: (x, y) => x % y,
        add: (x, y) => x + y,
        subtract: (x, y) => x - y,
        leftShift: (x, y) => x << y,
        signedRightShift: (x, y) => x >> y,
        unsignedRightShift: (x, y) => x >>> y, // will raise TypeError
        lessThan: (x, y) => x < y,
        lessThanOrEqual: (x, y) => x <= y,
        greaterThan: (x, y) => x > y,
        greaterThanOrEqual: (x, y) => x >= y,
        equal: (x, y) => x === y,
        notEqual: (x, y) => x !== y,
    });
}
JSBI.__ZERO = JSBI.BigInt(0);
JSBI.__MAX_SAFE = JSBI.BigInt(Number.MAX_SAFE_INTEGER);
JSBI.__MIN_SAFE = JSBI.BigInt(-Number.MAX_SAFE_INTEGER);
JSBI.numberIfSafe = (val) => (JSBI.lessThan(val, JSBI.__MAX_SAFE) && JSBI.greaterThan(val, JSBI.__MIN_SAFE) ? JSBI.toNumber(val) : val);


/***/ }),

/***/ "./support/time-helpers/strptime.js":
/*!******************************************!*\
  !*** ./support/time-helpers/strptime.js ***!
  \******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* jshint -W067 */
(function() {
    'use strict';

    /**
 * User: rikishi
 * Date: 07.07.13
 * Time: 19:17
 *
 */

/**
 * @param {string} str
 * @param {string} format
 * @param {Boolean} [local]
 * @returns {Date|Null}
 */
/*jshint -W079 */
var strptime = function(str, format, local) {
    return strptime.parse(str, format, local);
};


    strptime.version = '0.0.1';

    var namespace;

    if (true) {
        namespace = module.exports = strptime;
    } else {}

    namespace.strptime = strptime;

    (function(strptime) {

    strptime.locale = {
        'a': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        'A': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
        'b': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        'B': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        'f': ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'],
        'c': '%Y-%m-%d %H:%M:%S',
        'P': ['am', 'pm'],
        'r': '%I:%M:%S %p',
        'x': '%m/%d/%y',
        'X': '%H:%M:%S',
        'day': ['Yesterday', 'Today', 'Tomorrow'],

        // алиас падежа обязательно указать после обозначения
        'bg': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        'Bg': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
        'fg': ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'],

        'Date_dBY_year_in_HM': '%#B %-d, %Y at %-H:%M',
        'Date_dBY_year': '%#B %-d, %Y',
        'Date_dBY': '%#B %-d, %Y',
        'Date_AdBY': '%A, %#B %-d, %Y',
        'Date_dBA': '%#B %-d, %A',
        'Date_df_in_HM': '%#f, %-d at %-H:%M',
        'Date_dfY': '%-d %#f %Y',
        'Date_dB_in_HM': '%#B %-d at %-H:%M',
        'Date_df': '%-d %#f'
    };

}(strptime));


    (function(strptime) {

    var inArray = Array.prototype.indexOf || function(el) {
        var l = this.length;
        var i = 0;
        while (i < l) {
            if (el == this[i]) {
                return i;
            }
            i++;
        }
        return -1;
    };

    var locale = strptime.locale;

    var strRegNum2 = '[\\d\\s]?\\d';
    var strRegStr = '\\S+';

    var specifiers = {
        '%': '\\%',
        // сокращенное название дня недели, в соответствии с настройками локали
        'a': strRegStr,
        // полное название дня недели, в соответствии с настройками локали
        'A': strRegStr,
        // аббревиатура названия месяца, в соответствии с настройками локали
        'b': {
            'reg': strRegStr,
            'make': function(date, data, mod, gen) {
                data = inArray.call(gen ? locale.bg : locale.b, toLetterCaseReverse(data, mod));
                if (data === -1) {
                    return false;
                }

                date.setUTCMonth(data);
                return true;
            }
        },
        // аббревиатура названия месяца, в соответствии с настройками локали (псевдоним %b)
        'h': {
            'reg': strRegStr,
            'make': function(date, data, mod, gen) {
                data = inArray.call(gen ? locale.bg : locale.b, toLetterCaseReverse(data, mod));
                if (data === -1) {
                    return false;
                }

                date.setUTCMonth(data);
                return true;
            }
        },
        // полное название месяца, в соответствии с настройками локали
        'B': {
            'reg': strRegStr,
            'make': function(date, data, mod, gen) {
                data = inArray.call(gen ? locale.Bg : locale.B, toLetterCaseReverse(data, mod));
                if (data === -1) {
                    return false;
                }

                date.setUTCMonth(data);
                return true;
            }
        },
        // аббревиатура названия месяца с точкой, в соответствии с настройками локали
        'f': {
            'reg': strRegStr,
            'make': function(date, data, mod, gen) {
                data = inArray.call(gen ? locale.fg : locale.f, toLetterCaseReverse(data, mod));
                if (data === -1) {
                    return false;
                }

                date.setUTCMonth(data);
                return true;
            }
        },


        // двухзначный номер года в соответствии со стандартом ISO-8601:1988
        'g': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 0 || data > 99) {
                    return false;
                }

                data = data + 100 * parseInt((new Date()).getUTCFullYear() / 100, 10);
                date.setUTCFullYear(data);
                return true;
            }
        },
        // полная четырехзначная версия %g
        'G': {
            'reg': '\\d{4}',
            'make': function(date, data) {
                data = parseInt(data, 10);
                date.setUTCFullYear(data);
                return true;
            }
        },
        // двухзначное представление дня месяца (с ведущими нулями)
        'd': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 1 || data > 31) {
                    return false;
                }
                date.setUTCDate(data);
                return true;
            }
        },
        // день месяца, с ведущим пробелом, если он состоит из одной цифры
        'e': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 1 || data > 31) {
                    return false;
                }
                date.setUTCDate(data);
                return true;
            }
        },

        // двухзначный номер часа в 24-часовом формате
        'H': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 0 || data > 23) {
                    return false;
                }
                date.setUTCHours(data);
                return true;
            }
        },
        // двухзначный номер часа в 12-часовом формате
        'I': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 1 || data > 12) {
                    return false;
                }

                date.setUTCHours(date.getUTCHours() + data);
                return true;
            }
        },
        // двухзначный порядковый номер месяца (с ведущими нулями)
        'm': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 1 || data > 12) {
                    return false;
                }
                date.setUTCMonth(data - 1);
                return true;
            }
        },
        // двухзначный номер минуты (с ведущими нулями)
        'M': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 0 || data > 59) {
                    return false;
                }
                date.setUTCMinutes(data);
                return true;
            }
        },
        'n': '\\n',
        // 'AM' или 'PM' в верхнем регистре, в зависимости от указанного времени
        'p': {
            'reg': strRegStr,
            'make': function(date, data) {
                data = inArray.call(locale.P, data.toLowerCase());
                if (data === -1) {
                    return false;
                }

                if (data === 1) {
                    date.setUTCHours(date.getUTCHours() + 12);
                }

                return true;
            }
        },
        // 'am' или 'pm' в зависимости от указанного времени
        'P': {
            'reg': strRegStr,
            'make': function(date, data) {
                data = inArray.call(locale.P, data.toLowerCase());
                if (data === -1) {
                    return false;
                }

                if (data === 1) {
                    date.setUTCHours(date.getUTCHours() + 12);
                }

                return true;
            }
        },

        // двухзначный номер секунды (с ведущими нулями)
        'S': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 0 || data > 60) {
                    return false;
                }
                date.setUTCSeconds(data);
                return true;
            }
        },
        't': '\\t',
        'u': '\\d',
        'U': strRegNum2,
        'w': '\\d',
        'W': strRegNum2,
        // последние 2 цифры года
        'y': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 0 || data > 99) {
                    return false;
                }

                data = data + 100 * parseInt((new Date()).getUTCFullYear() / 100, 10);
                date.setUTCFullYear(data);
                return true;
            }
        },
        // год
        'Y': {
            'reg': '\\d{4}',
            'make': function(date, data) {
                data = parseInt(data, 10);
                date.setUTCFullYear(data);
                return true;
            }
        },
        'z': {
            'reg': '[+\\-]\\d{4}',
            'make': function(date, data) {
                var m = data.match(/^([+\-])(\d{2})(\d{2})$/);
                if (!m) {
                    return false;
                }

                var offset = (parseInt(m[2], 10) * 60 + parseInt(m[3], 10)) * 60000;
                if (m[1] === '+') {
                    offset = -offset;
                }

                date.setTime(date.getTime() + offset);

                return true;
            }
        },
        'l': {
            'reg': strRegNum2,
            'make': function(date, data) {
                data = parseInt(data, 10);
                if (data < 1 || data > 12) {
                    return false;
                }

                date.setUTCHours(date.getUTCHours() + data);
                return true;
            }
        },
        // метка времени Эпохи Unix
        's': {
            'reg': '\\d+',
            'make': function(date, data) {
                data = parseInt(data, 10);
                date.setTime(data * 1000);
                return true;
            }
        },



        'c': locale.c,
        'r': locale.r,
        'R': '%H:%M',
        'T': '%H:%M:%S',
        'x': locale.x,
        'X': locale.X,
        'D': '%m/%d/%y',
        'F': '%Y-%m-%d',


        'Date_iso': '%Y-%m-%dT%H:%M:%S',
        'Date_dBY_year_in_HM': locale.Date_dBY_year_in_HM,
        'Date_dBY_year': locale.Date_dBY_year,
        'Date_dBY': locale.Date_dBY,
        'Date_dBA': locale.Date_dBA,
        'Date_AdBY': locale.Date_AdBY,
        'Date_df_in_HM': locale.Date_df_in_HM,
        'Date_dfY': locale.Date_dfY,
        'Date_dB_in_HM': locale.Date_dB_in_HM,
        'Date_dmY__dot': '%d.%m.%Y',
        'Date_df': locale.Date_df,
        'Date_FT': '%F %T',
        'Date_dmY__minus': '%d-%m-%Y'
    };

    strptime.parse = function(str, format, local) {
        str = String(str);
        format = String(format);

        var loop = 5;
        while (/%(Date_[a-zA-Z0-9_]+|[cDFrRTxX])/g.test(format) && loop) {
            format = format.replace(/%(Date_[a-zA-Z0-9_]+|[cDFrRTxX])/, formatTransform);
            loop--;
        }

        formatTransform.make = [];
        var reg = format.replace(/%(([#\^!~]{0,2})[aAbBfh]|([0\-_]?)[degHImMSVWyl]|[GnpPtuUwYzZs%])/g, formatTransform);

        var match = str.match(new RegExp(reg));

        if (!match || !formatTransform.make.length) {
            return null;
        }

        var date = new Date(Date.UTC(0, 0));

        for (var i = 0, l = formatTransform.make.length; i < l; i++) {
            var build = formatTransform.make[i];
            if (!build[0](date, match[i + 1], build[1], build[2])) {
                return null;
            }
        }

        if (local) {
            date.setTime(date.getTime() + date.getTimezoneOffset() * 60000);
        }

        return date;
    };

    function formatTransform(_, spec, mod, numPad, pos, str) {
        spec = String(spec);
        mod = String(mod);
        spec = spec.replace(/^[#_0\^\-!~]+/, '');

        var s = specifiers[spec];

        if (!s) {
            return _;
        }

        var genitive = false;
        if (mod.indexOf('!') === -1 && spec.length === 1 && (mod.indexOf('~') > -1 || ('bBf'.indexOf(spec) > -1 && /%[0\-_]?d[\s]+$/.test(str.substr(0, pos))))) {

            genitive = true;
        }

        if ((spec === 'I' || spec === 'l') && !/%[pP]/.test(str)) {
            throw new Error('Undefined AM/PM');
        }

        // TODO добавить проверку повторяющихся форматов

        switch (typeof(s)) {
        case 'function':
            return s();
        case 'string':
            return s;
        case 'object':
            formatTransform.make.push([s.make, mod, genitive]);
            return '(' + s.reg + ')';
        default:
            return _;
        }
    }

    /**
     * @param {string} str
     * @param {string} [mode]
     * @returns {string}
     */
    function toLetterCaseReverse(str, mode) {
        str = String(str);
        mode = String(mode);

        if (mode.indexOf('#') !== -1) {
            return str.substr(0, 1).toUpperCase() + str.substr(1);
        }

        if (mode.indexOf('^') !== -1) {
            return str.substr(0, 1) + str.substr(1).toLowerCase();
        }

        return str;
    }

}(strptime));


}());


/***/ })

/******/ });
//# sourceMappingURL=skulpt.js.map