{"id":16698924,"url":"https://github.com/kovidgoyal/rapydscript-ng","last_synced_at":"2025-04-06T06:06:59.203Z","repository":{"id":47513097,"uuid":"39045277","full_name":"kovidgoyal/rapydscript-ng","owner":"kovidgoyal","description":"A transpiler for a Python like language to JavaScript","archived":false,"fork":false,"pushed_at":"2023-07-26T10:14:21.000Z","size":4457,"stargazers_count":187,"open_issues_count":9,"forks_count":45,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-05-02T01:08:31.730Z","etag":null,"topics":["javascript","python","rapydscript-ng","transpiler"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kovidgoyal.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-07-14T00:53:20.000Z","updated_at":"2024-06-19T00:07:30.385Z","dependencies_parsed_at":"2024-06-19T00:22:45.852Z","dependency_job_id":null,"html_url":"https://github.com/kovidgoyal/rapydscript-ng","commit_stats":{"total_commits":1085,"total_committers":11,"mean_commits":98.63636363636364,"dds":"0.14746543778801846","last_synced_commit":"7be1102f49a11560d4e9eff04a9a84b50b40c8cc"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kovidgoyal%2Frapydscript-ng","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kovidgoyal%2Frapydscript-ng/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kovidgoyal%2Frapydscript-ng/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kovidgoyal%2Frapydscript-ng/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kovidgoyal","download_url":"https://codeload.github.com/kovidgoyal/rapydscript-ng/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247441044,"owners_count":20939239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","python","rapydscript-ng","transpiler"],"created_at":"2024-10-12T18:04:44.495Z","updated_at":"2025-04-06T06:06:59.175Z","avatar_url":"https://github.com/kovidgoyal.png","language":"JavaScript","readme":"RapydScript\n===========\n\n\n[![Build Status](https://github.com/ebook-utils/kovidgoyal/rapydscript-ng/CI/badge.svg)](https://github.com/kovidgoyal/rapydscript-ng/actions?query=workflow%3ACI)\n[![Downloads](https://img.shields.io/npm/dm/rapydscript-ng.svg)](https://www.npmjs.com/package/rapydscript-ng)\n[![Current Release](https://img.shields.io/npm/v/rapydscript-ng.svg)](https://www.npmjs.com/package/rapydscript-ng)\n[![Known Vulnerabilities](https://snyk.io/test/github/kovidgoyal/rapydscript-ng/badge.svg)](https://snyk.io/test/github/kovidgoyal/rapydscript-ng)\n\nThis is a fork of the original RapydScript that adds many new (not always\nbackwards compatible) features. For more on the forking, [see the bottom of this file](#reasons-for-the-fork)\n\n[Try RapydScript-ng live via an in-browser REPL!](https://sw.kovidgoyal.net/rapydscript/repl/)\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Contents**\n\n- [What is RapydScript?](#what-is-rapydscript)\n- [Installation](#installation)\n- [Compilation](#compilation)\n- [Getting Started](#getting-started)\n- [Leveraging other APIs](#leveraging-other-apis)\n- [Anonymous Functions](#anonymous-functions)\n- [Decorators](#decorators)\n- [Self-Executing Functions](#self-executing-functions)\n- [Chaining Blocks](#chaining-blocks)\n- [Function calling with optional arguments](#function-calling-with-optional-arguments)\n- [Inferred Tuple Packing/Unpacking](#inferred-tuple-packingunpacking)\n- [Operators and keywords](#operators-and-keywords)\n- [Literal JavaScript](#literal-javascript)\n- [Containers (lists/sets/dicts)](#containers-listssetsdicts)\n  - [Container comparisons](#container-comparisons)\n- [Loops](#loops)\n- [List/Set/Dict Comprehensions](#listsetdict-comprehensions)\n- [Strings](#strings)\n- [The Existential Operator](#the-existential-operator)\n- [Regular Expressions](#regular-expressions)\n- [Creating DOM trees easily](#creating-dom-trees-easily)\n- [Classes](#classes)\n  - [External Classes](#external-classes)\n  - [Method Binding](#method-binding)\n- [Iterators](#iterators)\n- [Generators](#generators)\n- [Modules](#modules)\n- [Exception Handling](#exception-handling)\n- [Scope Control](#scope-control)\n- [Available Libraries](#available-libraries)\n- [Linter](#linter)\n- [Making RapydScript even more pythonic](#making-rapydscript-even-more-pythonic)\n- [Advanced Usage Topics](#advanced-usage-topics)\n    - [Browser Compatibility](#browser-compatibility)\n    - [Tabs vs Spaces](#tabs-vs-spaces)\n    - [External Libraries and Classes](#external-libraries-and-classes)\n    - [Embedding the RapydScript compiler in your webpage](#embedding-the-rapydscript-compiler-in-your-webpage)\n- [Internationalization](#internationalization)\n- [Gotchas](#gotchas)\n- [Reasons for the fork](#reasons-for-the-fork)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n\nWhat is RapydScript?\n--------------------\n\nRapydScript (pronounced 'RapidScript') is a pre-compiler for JavaScript,\nsimilar to CoffeeScript, but with cleaner, more readable syntax. The syntax is\nalmost identical to Python, but RapydScript has a focus on performance and\ninteroperability with external JavaScript libraries. This means that the\nJavaScript that RapydScript generates is performant and quite close to hand\nwritten JavaScript.\n\nRapydScript allows to write your front-end in Python without the overhead that\nother similar frameworks introduce (the performance is the same as with pure\nJavaScript). To those familiar with CoffeeScript, RapydScript is like\nCoffeeScript, but inspired by Python's readability rather than Ruby's\ncleverness. To those familiar with Pyjamas, RapydScript brings many of the same\nfeatures and support for Python syntax without the same overhead. Don't worry\nif you've never used either of the above-mentioned compilers, if you've ever\nhad to write your code in pure JavaScript you'll appreciate RapydScript.\nRapydScript combines the best features of Python as well as JavaScript,\nbringing you features most other Pythonic JavaScript replacements overlook.\nHere are a few features of RapydScript:\n\n- classes that work and feel similar to Python\n- an import system for modules/packages that works just like Python's\n- optional function arguments that work similar to Python\n- inheritance system that's both, more powerful than Python and cleaner than JavaScript\n- support for object literals with anonymous functions, like in JavaScript\n- ability to invoke any JavaScript/DOM object/function/method as if it's part of the same framework, without the need for special syntax\n- variable and object scoping that make sense (no need for repetitive 'var' or 'new' keywords)\n- ability to use both, Python's methods/functions and JavaScript's alternatives\n- similar to above, ability to use both, Python's and JavaScript's tutorials (as well as widgets)\n- it's self-hosting, that means the compiler is itself written in RapydScript and compiles into JavaScript\n\nLet's not waste any more time with the introductions, however. The best way to\nlearn a new language/framework is to dive in.\n\n\nInstallation\n------------\n\n[Try RapydScript-ng live via an in-browser REPL!](https://sw.kovidgoyal.net/rapydscript/repl/)\n\nFirst make sure you have installed the latest version of [node.js](https://nodejs.org/) (You may need to restart your computer after this step). \n\nFrom NPM for use as a command line app:\n\n\tnpm install rapydscript-ng -g\n\nFrom NPM for use in your own node project:\n\n\tnpm install rapydscript-ng\n\nFrom Git:\n\n\tgit clone git://github.com/kovidgoyal/rapydscript-ng.git\n\tcd rapydscript-ng\n\tsudo npm link .\n\tnpm install  # This will automatically install the dependencies for RapydScript\n\nIf you're using OSX, you can probably use the same commands (let me know if\nthat's not the case). If you're using Windows, you should be able to follow\nsimilar commands after installing node.js, npm and git on your system.\n\n\nCompilation\n-----------\nOnce you have installed RapydScript, compiling your application is as simple as\nrunning the following command:\n\n\trapydscript [options] \u003clocation of main file\u003e\n\nBy default this will dump the output to STDOUT, but you can specify the output\nfile using `--output` option. The generated file can then be referenced in your\nhtml page the same way as you would with a typical JavaScript file. If you're\nonly using RapydScript for classes and functions, then you're all set. For more\nhelp, use ```rapydscript -h```.\n\nGetting Started\n---------------\n\nRapydScript comes with its own Read-Eval-Print-Loop (REPL). Just run\n``rapydscript`` without any arguments to get started trying out the code\nsnippets below.\n\nLike JavaScript, RapydScript can be used to create anything from a quick\nfunction to a complex web-app. RapydScript can access anything regular\nJavaScript can, in the same manner. Let's say we want to write a function that\ngreets us with a \"Hello World\" pop-up. The following code will do it:\n\n```python\ndef greet():\n\talert(\"Hello World!\")\n```\n\nOnce compiled, the above code will turn into the following JavaScript:\n\n```javascript\nfunction greet() {\n\talert(\"Hello World!\");\n}\n```\n\nNow you can reference this function from other JavaScript or the page itself\n(using \"onclick\", for example). For our next example, let's say you want a\nfunction that computes factorial of a number:\n\n```python\ndef factorial(n):\n\tif n == 0:\n\t\treturn 1\n\treturn n * factorial(n-1)\n```\n\nNow all we need is to tie it into our page so that it's interactive. Let's add an input field to the page body and a cell for displaying the factorial of the number in the input once the input loses focus.\n\n```html\n\t\u003cinput id=\"user-input\" onblur=\"computeFactorial()\"\u003e\u003c/input\u003e\n\t\u003cdiv id=\"result\"\u003e\u003c/div\u003e\n```\n\n**NOTE:** To complement RapydScript, I have also written RapydML (\u003chttps://bitbucket.org/pyjeon/rapydml\u003e), which is a pre-compiler for HTML (just like RapydScript is a pre-compiler for JavaScript). \n\nNow let's implement computeFactorial() function in RapydScript:\n\n```python\ndef computeFactorial():\n\tn = document.getElementById(\"user-input\").value\n\tdocument.getElementById(\"result\").innerHTML = factorial(n)\n```\n\nAgain, notice that we have access to everything JavaScript has access to, including direct DOM manipulation. Once compiled, this function will look like this:\n\n```javascript\nfunction computeFactorial() {\n\tvar n;\n\tn = document.getElementById(\"user-input\").value;\n\tdocument.getElementById(\"result\").innerHTML = factorial(n);\n}\n```\n\nNotice that RapydScript automatically declares variables in local scope when\nyou try to assign to them. This not only makes your code shorter, but saves you\nfrom making common JavaScript mistake of overwriting a global. For more\ninformation on controlling variable scope, see `Scope Control` section.\n\n\nLeveraging other APIs\n---------------------\n\nAside from Python-like stdlib, RapydScript does not have any of its own APIs.\nNor does it need to, there are already good options available that we can\nleverage instead. If we wanted, for example, to rewrite the above factorial\nlogic using jQuery, we could easily do so:\n\n```python\ndef computeFactorial():\n\tn = $(\"#user-input\").val()\n\t$(\"#result\").text(factorial(n))\n```\n\nMany of these external APIs, however, take object literals as input. Like with\nJavaScript, you can easily create those with RapydScript, the same way you\nwould create a dictionary in Python:\n\n```javascript\nstyles = {\n\t'background-color':\t'#ffe',\n\t'border-left':\t\t'5px solid #ccc',\n\t'width':\t\t\t50,\n}\n```\n\nNow you can pass it to jQuery:\n\n```python\n$('#element').css(styles)\n```\n\nAnother feature of RapydScript is ability to have functions as part of your\nobject literal. JavaScript APIs often take callback/handler functions as part\nof their input parameters, and RapydScript lets you create such object literal\nwithout any quirks/hacks:\n\n```js\nparams = {\n\t'width':\t50,\n\t'height':\t30,\n\t'onclick':\tdef(event):\n\t\talert(\"you clicked me\"),\n\t'onmouseover':\tdef(event):\n\t\t$(this).css('background', 'red')\n\t,\n\t'onmouseout':\tdef(event):\n\t\t# reset the background\n\t\t$(this).css('background', '')\n}\n```\n\nNote the comma on a new line following a function declaration, it needs to be\nthere to let the compiler know there are more attributes in this object\nliteral, yet it can't go on the same line as the function since it would get\nparsed as part of the function block. Like Python, however, RapydScript\nsupports new-line shorthand using a `;`, which you could use to place the comma\non the same line:\n\n```js\nhash = {\n\t'foo':\tdef():\n\t\tprint('foo');,\n\t'bar':\tdef():\n\t\tprint('bar')\n}\n```\n\nIt is because of easy integration with JavaScript's native libraries that RapydScript keeps its own libraries to a minimum. \n\nAnonymous Functions\n-------------------\n\nLike JavaScript, RapydScript allows the use of anonymous functions. In fact,\nyou've already seen the use of anonymous functions in previous section when\ncreating an object literal ('onmouseover' and 'onmouseout' assignments). This\nis similar to Python's lambda function, except that the syntax isn't awkward\nlike lambda, and the function isn't limited to one line. The following two\nfunction declarations are equivalent:\n\n```js\ndef factorial(n):\n\tif n == 0:\n\t\treturn 1\n\treturn n * factorial(n-1)\n\nfactorial = def(n):\n\tif n == 0:\n\t\treturn 1\n\treturn n * factorial(n-1)\n```\n\nThis might not seem like much at first, but if you're familiar with JavaScript,\nyou know that this can be extremely useful to the programmer, especially when\ndealing with nested functions, which are a bit syntactically awkward in Python\n(it's not immediately obvious that those can be copied and assigned to other\nobjects). To illustrate the usefulness, let's create a method that creates and\nreturns an element that changes color while the user keeps the mouse pressed on\nit.\n\n```js\ndef makeDivThatTurnsGreen():\n\tdiv = $('\u003cdiv\u003e\u003c/div\u003e')\n\tturnGreen = def(event):\n\t\tdiv.css('background', 'green')\n\tdiv.mousedown(turnGreen)\n\tresetColor = def(event):\n\t\tdiv.css('background', '')\n\tdiv.mouseup(resetColor)\n\treturn div\n```\n\nAt first glance, anonymous functions might not seem that useful. We could have\neasily created nested functions and assigned them instead. By using anonymous\nfunctions, however, we can quickly identify that these functions will be bound\nto a different object. They belong to the div, not the main function that\ncreated them, nor the logic that invoked it. The best use case for these is\ncreating an element inside another function/object without getting confused\nwhich object the function belongs to.\n\nAdditionally, as you already noticed in the previous section, anonymous\nfunctions can be used to avoid creating excessive temporary variables and make\nyour code cleaner:\n\n```js\nmath_ops = {\n\t'add':\tdef(a, b): return a+b;,\n\t'sub':\tdef(a, b): return a-b;,\n\t'mul':\tdef(a, b): return a*b;,\n\t'div':\tdef(a, b): return a/b;,\n\t'roots':\tdef(a, b, c):\n\t\tr = Math.sqrt(b*b - 4*a*c)\n\t\td = 2*a\n\t\treturn (-b + r)/d, (-b - r)/d\n}\n```\n\nI'm sure you will agree that the above code is cleaner than declaring 5\ntemporary variables first and assigning them to the object literal keys after.\nNote that the example puts the function header (def()) and content on the same\nline. I'll refer to it as function inlining. This is meant as a feature of\nRapydScript to make the code cleaner in cases like the example above. While you\ncan use it in longer functions by chaining statements together using `;`, a\ngood rule of thumb (to keep your code clean) is if your function needs\nsemi-colons ask yourself whether you should be inlining, and if it needs more\nthan 2 semi-colons, the answer is probably no (note that you can also use\nsemi-colons as newline separators within functions that aren't inlined, as in\nthe example in the previous section).\n\n\nDecorators\n----------\nLike Python, RapydScript supports decorators. \n\n```py\ndef makebold(fn):\n\tdef wrapped():\n\t\treturn \"\u003cb\u003e\" + fn() + \"\u003c/b\u003e\"\n\treturn wrapped\n\ndef makeitalic(fn):\n\tdef wrapped():\n\t\treturn \"\u003ci\u003e\" + fn() + \"\u003c/i\u003e\"\n\treturn wrapped\n\n@makebold\n@makeitalic\ndef hello():\n\treturn \"hello world\"\n\nhello() # returns \"\u003cb\u003e\u003ci\u003ehello world\u003c/i\u003e\u003c/b\u003e\"\n```\n\nClass decorators are also supported with the caveat that the class properties\nmust be accessed via the prototype property. For example:\n\n```py\n\ndef add_x(cls):\n\tcls.prototype.x = 1\n\n@add_x\nclass A:\n   pass\n\nprint(A.x)  # will print 1\n```\n\n\nSelf-Executing Functions\n------------------------\nRapydScript wouldn't be useful if it required work-arounds for things that\nJavaScript handled easily. If you've worked with JavaScript or jQuery before,\nyou've probably seen the following syntax:\n\n```js\n(function(args){\n\t// some logic here\n})(args)\n```\n\nThis code calls the function immediately after declaring it instead of\nassigning it to a variable. Python doesn't have any way of doing this. The\nclosest work-around is this:\n\n```py\ndef tmp(args):\n\t# some logic here\ntmp.__call__(args)\n```\n\nWhile it's not horrible, it did litter our namespace with a temporary variable.\nIf we have to do this repeatedly, this pattern does get annoying. This is where\nRapydScript decided to be a little unorthodox and implement the JavaScript-like\nsolution:\n\n```js\n(def(args):\n\t# some logic here\n)()\n```\n\nA close cousin of the above is the following code (passing current scope to the function being called):\n\n```js\nfunction(){\n\t// some logic here\n}.call(this);\n```\n\nWith RapydScript equivalent of:\n\n```js\ndef():\n\t# some logic here\n.call(this)\n```\n\nThere is also a third alternative, that will pass the arguments as an array:\n\n```js\ndef(a, b):\n\t# some logic here\n.apply(this, [a, b])\n```\n\n\nChaining Blocks\n---------------\nAs seen in previous section, RapydScript will bind any lines beginning with `.` to the outside of the block with the matching indentation. This logic isn't limited to the `.call()` method, you can use it with `.apply()` or any other method/property the function has assigned to it. This can be used for jQuery as well:\n\n```js\n$(element)\n.css('background-color', 'red')\n.show()\n```\n\nThe only limitation is that the indentation has to match, if you prefer to indent your chained calls, you can still do so by using the `\\` delimiter:\n\n```js\n$(element)\\\n\t.css('background-color', 'red')\\\n\t.show()\n```\n\nSome of you might welcome this feature, some of you might not. RapydScript always aims to make its unique features unobtrusive to regular Python, which means that you don't have to use them if you disagree with them. Recently, we have enhanced this feature to handle `do/while` loops as well:\n\n```js\na = 0\ndo:\n\tprint(a)\n\ta += 1\n.while a \u003c 1\n```\n\nFunction calling with optional arguments\n-------------------------------------------\n\nRapydScript supports the same function calling format as Python. You can have\nnamed optional arguments, create functions with variable numbers of arguments\nand variable numbers of named arguments. Some examples will illustrate this\nbest:\n\n```py\n\tdef f1(a, b=2):\n\t   return [a, b]\n\n\tf1(1, 3) == f1(1, b=3) == [1, 3]\n\n\tdef f2(a, *args):\n\t\treturn [a, args]\n\n\tf2(1, 2, 3) == [1, [2, 3]]\n\n\tdef f3(a, b=2, **kwargs):\n\t    return [a, b, kwargs]\n\n\tf3(1, b=3, c=4) == [1, 3, {c:4}]\n\n\tdef f4(*args, **kwargs):\n\t\treturn [args, kwargs]\n\n\tf4(1, 2, 3, a=1, b=2):\n\t\treturn [[1, 2, 3], {a:1, b:2}]\n```\n\nOne difference between RapydScript and Python is that RapydScript is not as\nstrict as Python when it comes to validating function arguments. This is both\nfor performance and to make it easier to interoperate with other JavaScript\nlibraries. So if you do not pass enough arguments when calling a function, the\nextra arguments will be set to undefined instead of raising a TypeError, as in\nPython. Similarly, when mixing ``*args`` and optional arguments, RapydScript\nwill not complain if an optional argument is specified twice.\n\nWhen creating callbacks to pass to other JavaScript libraries, it is often the\ncase that the external library expects a function that receives an *options\nobject* as its last argument. There is a convenient decorator in the standard\nlibrary that makes this easy:\n\n```py\n@options_object\ndef callback(a, b, opt1=default1, opt2=default2):\n\tconsole.log(opt1, opt2)\n\ncallback(1, 2, {'opt1':'x', 'opt2':'y'})  # will print x, y\n```\n\nNow when you pass callback into the external library and it is called with an\nobject containing options, they will be automatically converted by RapydScript\ninto the names optional parameters you specified in the function definition.\n\n\nInferred Tuple Packing/Unpacking\n--------------------------------\nLike Python, RapydScript allows inferred tuple packing/unpacking and assignment. While inferred/implicit logic is usually bad, it can sometimes make the code cleaner, and based on the order of statements in the Zen of Python, 'beautiful' takes priority over 'explicit'. For example, if you wanted to swap two variables, the following looks cleaner than explicitly declaring a temporary variable:\n\n```py\na, b = b, a\n```\n\nLikewise, if a function returns multiple variables, it's cleaner to say:\n\n```py\na, b, c = fun()\n```\n\nrather than:\n\n```py\ntmp = fun()\na = tmp[0]\nb = tmp[1]\nc = tmp[2]\n```\n\nSince JavaScript doesn't have tuples, RapydScript uses arrays for tuple packing/unpacking behind the scenes, but the functionality stays the same. Note that unpacking only occurs when you're assigning to multiple arguments:\n\n```py\na, b, c = fun()\t\t# gets unpacked\ntmp = fun()\t\t\t# no unpacking, tmp will store an array of length 3\n```\n\nUnpacking can also be done in `for` loops (which you can read about in later section):\n\n```py\nfor index, value in enumerate(items):\n\tprint(index+': '+value)\n```\n\nTuple packing is the reverse operation, and is done to the variables being assigned, rather than the ones being assigned to. This can occur during assignment or function return:\n\n```py\ndef fun():\n\treturn 1, 2, 3\n```\n\nTo summarize packing and unpacking, it's basically just syntax sugar to remove obvious assignment logic that would just litter the code. For example, the swap operation shown in the beginning of this section is equivalent to the following code:\n\n```py\ntmp = [b, a]\na = tmp[0]\nb = tmp[1]\n```\n\n\nOperators and keywords\n------------------------\n\nRapydScript uses the python form for operators and keywords. Below is the\nmapping from RapydScript to JavaScript.\n\nKeywords:\n\n\tRapydScript\t\tJavaScript\n\t\n\tNone\t\t\tnull\n\tFalse\t\t\tfalse\n\tTrue\t\t\ttrue\n\tundefined\t\tundefined\n\tthis\t\t\tthis\n\nOperators:\n\n\tRapydScript\t\tJavaScript\n\t\n\tand\t\t\t\t\u0026\u0026\n\tor\t\t\t\t||\n\tnot\t\t\t\t!\n\tis\t\t\t\t===\n\tis not\t\t\t!==\n\t+=1\t\t\t\t++\n\t-=1\t\t\t\t--\n\t**\t\t\t\tMath.pow()\n\t\nAdmittedly, `is` is not exactly the same thing in Python as `===` in JavaScript, but JavaScript is quirky when it comes to comparing objects anyway.\n\n\nLiteral JavaScript\n-----------------------\n\nIn rare cases RapydScript might not allow you to do what you need to, and you\nneed access to pure JavaScript, this is particularly useful for performance\noptimizations in inner loops. When that's the case, you can use a *verbatim\nstring literal*.  That is simply a normal RapydScript string prefixed with the\n```v``` character. Code inside a verbatim string literal is not a sandbox, you\ncan still interact with it from normal RapydScript:\n\n```py\nv'a = {foo: \"bar\", baz: 1};'\nprint(a.foo)\t# prints \"bar\"\n\nfor v'i = 0; i \u003c arr.length; i++':\n   print (arr[i])\n```\n\nContainers (lists/sets/dicts)\n------------------------------\n\n### Lists\n\nLists in RapydScript are almost identical to lists in Python, but are also\nnative JavaScript arrays. The only small caveats are that the ``sort()`` and\n``pop()`` methods are renamed to ``pysort()`` and ``pypop()``. This is so that\nyou can pass RapydScript lists to external JavaScript libraries without any\nconflicts. Note that even list literals in RapydScript create python like list\nobjects, and you can also use the builtin ``list()`` function to create lists\nfrom other iterable objects, just as you would in python.  You can create a\nRapydScript list from a plain native JavaScript array by using the ``list_wrap()``\nfunction, like this:\n\n```py\na = v'[1, 2]'\npya = list_wrap(a)\n # Now pya is a python like list object that satisfies pya === a\n```\n\n### Sets\n\nSets in RapydScript are identical to those in python. You can create them using\nset literals or comprehensions and all set operations are supported. You can\nstore any object in a set, the only caveat is that RapydScript does not support\nthe ``__hash__()`` method, so if you store an arbitrary object as opposed to a\nprimitive type, object equality will be via the ``is`` operator.\n\nNote that sets are not a subclass of the ES 6 JavaScript Set object, however,\nthey do use this object as a backend, when available. You can create a set from\nany enumerable container, like you would in python\n\n```py\ns = set(list or other set or string)\n```\n\nYou can also wrap an existing JavaScript Set object efficiently, without\ncreating a copy with:\n\n```py\njs_set = Set()\npy_set = set_wrap(js_set)\n```\n\nNote that using non-primitive objects as set members does not behave the\nsame way as in Python. For example:\n\n```py\na = [1, 2]\ns = {a}\na in s  # True\n[1, 2] in s # False\n```\n\nThis is because, as noted above, object equality is via the ```is```\noperator, not hashes.\n\n### Dicts\n\ndicts are the most different in RapydScript, from Python. This is because\nRapydScript uses the JavaScript Object as a dict, for compatibility with\nexternal JavaScript libraries and performance. This means there are several\ndifferences between RapydScript dicts and Python dicts.\n\n    - You can only use primitive types (strings/numbers) as keys in the dict\n    - If you use numbers as keys, they are auto-converted to strings\n    - You can access the keys of the dict as attributes of the dict object\n    - Trying to access a non-existent key returns ``undefined`` instead of\n      raising a KeyError\n    - dict objects do not have the same methods as python dict objects:\n      ``items(), keys(), values(), get(), pop(), etc.`` You can however use\n      RapydScript dict objects in ```for..in``` loops.\n\nFortunately, there is a builtin ```dict``` type that behaves just like Python's\n```dict``` with all the same methods. The only caveat is that you have to add\na special line to your RapydScript code to use these dicts, as shown below:\n\n```py\nfrom __python__ import dict_literals, overload_getitem\na = {1:1, 2:2}\na[1]  # == 1\na[3] = 3\nlist(a.keys()) == [1, 2, 3]\na['3'] # raises a KeyError as this is a proper python dict, not a JavaScript object\n```\n\nThe special line, called a *scoped flag* tells the compiler that from\nthat point on, you want it to treat dict literals and the getitem operator `[]`\nas they are treated in python, not JavaScript. \n\nThe scoped flags are local to each scope, that means that if you use it in a\nmodule, it will only affect code in that module, it you use it in a function,\nit will only affect code in that function. In fact, you can even use it to\nsurround a few lines of code, like this:\n\n```py\nfrom __python__ import dict_literals, overload_getitem\na = {1:1, 2:2}\nisinstance(a, dict) == True\nfrom __python__ import no_dict_literals, no_overload_getitem\na = {1:1, 2:2}\nisinstance(a, dict) == False # a is a normal JavaScript object\n```\n\n\n### Container comparisons\n\nContainer equality (the `==` and `!=` operators) work for lists and sets and\nRapydScript dicts (but not arbitrary javascript objects). You can also define\nthe ``__eq__(self, other)`` method in your classes to have these operators work\nfor your own types.\n\nRapydScript does not overload the ordering operators ```(\u003e, \u003c, \u003e=,\n\u003c=)``` as doing so would be a big performance impact (function calls in\nJavaScript are very slow). So using them on containers is useless. \n\nLoops\n-----\nRapydScript's loops work like Python, not JavaScript. You can't, for example\nuse ```for(i=0;i\u003cmax;i++)``` syntax. You can, however, loop through arrays\nusing 'for ... in' syntax without worrying about the extra irrelevant\nattributes regular JavaScript returns.\n\n```py\nanimals = ['cat', 'dog', 'mouse', 'horse']\nfor animal in animals:\n\tprint('I have a '+animal)\n```\n\t\t\nIf you need to use the index in the loop as well, you can do so by using enumerate():\n\n```py\nfor index, animal in enumerate(animals):\n\tprint(\"index:\"+index, \"animal:\"+animal)\n```\n\nLike in Python, if you just want the index, you can use range:\n\n```py\nfor index in range(len(animals)):\t\t\t# or range(animals.length)\n\tprint(\"animal \"+index+\" is a \"+animals[index])\n```\n\nWhen possible, RapydScript will automatically optimize the loop for you into\nJavaScript's basic syntax, so you're not missing much by not being able to call\nit directly.\n\n\nList/Set/Dict Comprehensions\n-------------------------------\n\nRapydScript also supports comprehensions, using Python syntax. Instead of the following, for example:\n\n```py\nmyArray = []\nfor index in range(1,20):\n\tif index*index % 3 == 0:\n\t\tmyArray.append(index*index)\n```\n\nYou could write this:\n\n```py\nmyArray = [i*i for i in range(1,20) if i*i%3 == 0]\n```\n\nSimilarly for set and dict comprehensions:\n\n```py\nmyDict = {x:x+1 for x in range(20) if x \u003e 2}\nmySet = {i*i for i in range(1,20) if i*i%3 == 0}\n```\n\nStrings\n---------\n\nFor reasons of compatibility with external JavaScript and performance,\nRapydScript does not make any changes to the native JavaScript string type.\nHowever, all the useful Python string methods are available on the builtin\n``str`` object. This is analogous to how the functions are available in the\n``string`` module in Python 2.x.  For example,\n\n```py\nstr.strip(' a ') == 'a'\nstr.split('a b') == ['a', 'b']\nstr.format('{0:02d} {n}', 1, n=2) == '01 2'\n...\n```\n\nHowever, if you want to make the python string methods available on string\nobjects, there is a convenience method in the standard library to do so. Use\nthe following code:\n\n```py\nfrom pythonize import strings\nstrings()\n```\n\nAfter you call the `strings()` function, all python string methods will be\navailable on string objects, just as in python. The only caveat is that two\nmethods: `split()` and `replace()` are left as the native JavaScript versions,\nas their behavior is not compatible with that of the python versions. You can\ncontrol which methods are not copied to the JavaScript String object by passing\ntheir names to the `strings()` function, like this:\n\n```py\nstrings('split', 'replace', 'find', ...)\n# or\nstrings(None)  # no methods are excluded\n```\n\nOne thing to keep in mind is that in JavaScript string are UTF-16, so they\nbehave like strings in narrow builds of Python 2.x. This means that non-BMP\nunicode characters are represented as surrogate pairs. RapydScript includes\nsome functions to make dealing with non-BMP unicode characters easier:\n\n  - ``str.uchrs(string, [with_positions])`` -- iterate over unicode characters in string, so, for example:\n\n\t```py\n\tlist(str.uchrs('s🐱a')) == ['s', \"🐱\", 'a']\n\t```\n\n\tYou can also get positions of individual characters:\n\n\t```py\n\tlist(str.uchrs('s🐱a', True)) == [[0, 's'], [1, \"🐱\"], [3, 'a']]\n\t```\n\tNote that any broken surrogate pairs in the underlying string are returned\n\tas the unicode replacement character U+FFFD\n\n  - ``str.uslice(string, [start, [stop]])`` -- get a slice based on unicode character positions, for example:\n\n\t```py\n\tstr.uslice('s🐱a', 2') == 'a'  # even though a is at index 3 in the native string object\n\t```\n\n  - ``str.ulen(string)`` -- return the number of unicode characters in the string\n\nThe Existential Operator\n---------------------------\n\nOne of the annoying warts of JavaScript is that there are two \"null-like\"\nvalues: `undefined` and `null`. So if you want to test if a variable is not\nnull you often have to write a lengthy expression that looks like\n\n```py\n(var !== undefined and var !== None)\n```\n\nSimply doing `bool(var)` will not work because zero and empty strings are also\nFalse.\n\nSimilarly, if you need to access a chain of properties/keys and dont want a\n`TypeError` to be raised, if one of them is undefined/null then you have\nto do something like:\n\n```py\nif a and a.b and a.b.c:\n\tans = a.b.c()\nelse:\n\tans = undefined\n```\n\nTo ease these irritations, RapydScript borrows the *Existential operator* from\nCoffeeScript. This can be used to test if a variable is null-like, with a\nsingle character, like this:\n\n```py\nyes = True if no? else False\n# Which, without the ? operator becomes\nyes = True if no is not undefined and no is not None else False\n```\n\nWhen it comes to long chains, the `?` operator will return the expected value\nif all parts of the chain are ok, but cause the entire chaning to result in\n`undefined` if any of its links are null-like. For example:\n\n```py\nans = a?.b?[1]?()\n# Which, without the ? operator becomes\nans = undefined\nif a is not undefined and a is not None and a.b is not undefined and a.b is not None and jstype(a.b[1]) is 'function':\n\tans = a.b[1]()\n```\n\nFinally, you can also use the existential operator as shorthand for the\nconditional ternary operator, like this:\n\n```py\na = b ? c\n# is the same as\na = c if (b is undefined or b is None) else b\n```\n\nRegular Expressions\n----------------------\n\nRapydScript includes a ```re``` module that mimics the interface of the Python\nre module. However, it uses the JavaScript regular expression functionality\nunder the hood, which has several differences from the Python regular\nexpression engine. Most importantly:\n\n  - it does not support lookbehind and group existence assertions\n  - it does not support unicode (on ES 6 runtimes, unicode is supported, but\n\twith a different syntax). You can test for the presence of unicode support with\n\t```re.supports_unicode```. \n  - The ``MatchObject``'s ``start()`` and ``end()`` method cannot return correct values\n    for subgroups for some kinds of regular expressions, for example, those\n\twith nested captures. This is because the JavaScript regex API does not expose\n\tthis information, so it has to be guessed via a heuristic.\n\nYou can use the JavaScript regex literal syntax, including verbose regex\nliterals, as shown below. In verbose mode, whitespace is ignored and # comments\nare allowed (except inside character classes -- verbose mode works in the same\nway as in python, except you use the JavaScript Regex literal syntax).\n\n```py\nimport re\nre.match(/a(b)/, 'ab') == re.match('a(b)', 'ab')\n\nre.match(///\n  a  # a comment\n  b  # Another comment\n  ///, 'ab')\n```\n\nCreating DOM trees easily\n---------------------------------\n\nRapydScript includes a small module in its standard library to create DOM tress\nefficiently. It leverages the powerful support for python style function\ncalling. Best illustrated with an example:\n\n```py\nfrom elementmaker import E\n\nE.div(id=\"container\", class_=\"xxx\",\n\tE.div('The Heading', data_heading=\"1\"),\n\tE.p('Some text ',\n\t\tE.i('with italics'),\n\t\tE('custom', ' and a csutom tag'),\n\t)\n)\n```\n\nThis is equivalent to:\n\n```html\n\u003cdiv id=\"container\" class=\"xxx\"\u003e\n\t\u003cdiv data-heading=\"1\"\u003eThe Heading\u003c/div\u003e\n\t\u003cp\u003eSome text \u003ci\u003ewith italics\u003c/i\u003e\u003ccustom\u003e and a custom tag\u003c/custom\u003e\u003c/p\u003e\n\u003c/div\u003e\n```\n\nBasically, you create text nodes and children as positional arguments and\nattributes as keyword arguments. Note that if an attribute name is a reserved\nkeyword in RapydScript, you can postfix it with an underscore. So ```class_```\nbecomes ```class```. Also, underscores are automatically replaced by hyphens,\nso ```data-*``` attributes can be created. Finally, if you need a non-standard\ntag, you simply use the ```E()``` function by itself with the first argument\nbeing the tag name.\n\nAnother great feature is that you can pass functions as event handlers\ndirectly, so for example:\n\n```py\nE.a(onclick=def():\n\tpass  # do something on the click event\n)\n```\n\nClasses\n-------\nThis is where RapydScript really starts to shine. JavaScript is known for having really crappy class implementation (it's basically a hack on top of a normal function, most experienced users suggest using external libraries for creating those instead of creating them in pure JavaScript). Luckily RapydScript fixes that. Let's imagine we want a special text field that takes in a user color string and changes color based on it. Let's create such field via a class.\n\n```js\nclass ColorfulTextField:\n\tdef __init__(self):\n\t\tfield = $('\u003cinput\u003e\u003c/input\u003e')\n\t\tchangeColor = def(event):\n\t\t\tfield.css('backround', field.val())\n\t\tfield.keydown(changeColor)\n\t\tself.widget = field\n```\n\nThis class abuses DOM's tolerant behavior, where it will default to the original setting when the passed-in color is invalid (saving us the extra error-checking logic). To append this field to our page we can run the following code:\n\n```py\ntextfield = ColorfulTextField()\n$('body').append(textfield.widget)\n```\n\nIf you're used to JavaScript, the code above probably set off a few red flags in your head. In pure JavaScript, you can't create an object without using a 'new' operator. Don't worry, the above code will compile to the following:\n\n```js\nvar textfield;\ntextfield = new ColorfulTextField()\n$('body').append(textfield.widget);\n```\n\nRapydScript will automatically handle appending the 'new' keyword for you, assuming you used 'class' to create the class for your object. This also holds when creating an object inside a list or returning it as well. You could easily do the following, for example:\n\n```\nfields = [ColorfulTextField(), ColorfulTextField(), ColorfulTextField()]\n```\n\nThis is very useful for avoiding a common JavaScript error of creating 'undefined' objects by forgetting this keyword. One other point to note here is that regular DOM/JavaScript objects are also covered by this. So if you want to create a DOM image element, you should not use the 'new' keyword either:\n\n```py\nmyImage = Image()\n```\n\nBut RapydScript's capability doesn't end here. Like Python, RapydScript allows inheritance. Let's say, for example, we want a new field, which works similar to the one above. But in addition to changing color of the field, it allows us to change the color of a different item, with ID of 'target' after we press the 'apply' button, located right next to it. Not a problem, let's implement this guy:\n\n```js\n\nclass TextFieldAffectingOthers(ColorfulTextField):\n\tdef __init__(self):\n\t\tColorfulTextField.__init__(self)\n\t\tfield = self.widget\n\t\tsubmit = $('\u003cbutton type=\"button\"\u003eapply\u003c/button\u003e')\n\t\tapplyColor = def(event):\n\t\t\t$('#target').css('background', field.val())\n\t\tsubmit.click(applyColor)\n\t\tself.widget = $('\u003cdiv\u003e\u003c/div\u003e')\\\n\t\t\t.append(field)\\\n\t\t\t.append(submit)\n```\n\nA couple of things to note here. We can invoke methods from the parent class\nthe same way we would in Python, by using `Parent.method(self, ...)` syntax.\nThis allows us to control when and how (assuming it requires additional\narguments) the parent method gets executed. Also note the use of `\\` operator\nto break up a line. This is something Python allows for keeping each line short\nand legible. Likewise, RapydScript, being indentation-based, allows the same.\n\nLike Python, RapydScript allows multiple inheritance. The only caveat is that \nthe internal semantics of how it works are pretty different from python, since\nit is built on JavaScript's prototypical inheritance. For the most part you\nwont notice any differences from python, except, if you have a very complex\ninheritance hierarchy, especially, one with cycles. In this (rare) case you may\nfind that the method-resolution-order in RapydScript is different from Python.\n\nLike Python, RapydScript allows static methods. Marking the method static with\n`@staticmethod` decorator will compile that method such that it's not bound to\nthe object instance, and ensure all calls to this method compile into static\nmethod calls:\n\n```py\nclass Test:\n\tdef normalMethod(self):\n\t\treturn 1\n\n\t@staticmethod\n\tdef staticMethod(a):\n\t\treturn a+1\n```\n\n### External Classes\n\nRapydScript will automatically detect classes declared within the same scope (as long as the declaration occurs before use), as well as classes properly imported into the module (each module making use of a certain class should explicitly import the module containing that class). RapydScript will also properly detect native JavaScript classes (String, Array, Date, etc.). Unfortunately, RapydScript has no way of detecting classes from third-party libraries. In those cases, you could use the `new` keyword every time you create an object from such class. Alternatively, you could mark the class as external.\n\nMarking a class as external is done via `external` decorator. You do not need to fill in the contents of the class, a simple `pass` statement will do:\n\n```py\n@external\nclass Alpha:\n\tpass\n```\n\nRapydScript will now treat `Alpha` as if it was declared within the same scope, auto-prepending the `new` keyword when needed and using `prototype` to access its methods (see `casperjs` example in next section to see how this can be used in practice). You don't need to pre-declare the methods of this class (unless you decide to for personal reference, the compiler will simply ignore them) unless you want to mark certain methods as static:\n\n```py\n@external\nclass Alpha:\n\t@staticmethod\n\tdef one():\n\t\tpass\n```\n\n`Alpha.one` is now a static method, every other method invoked on `Alpha` will still be treated as a regular class method. While not mandatory, you could pre-declare other methods you plan to use from `Alpha` class as well, to make your code easier to read for other developers, in which case this `external` declaration would also serve as a table of contents for `Alpha`:\n\n```py\n@external\nclass Alpha:\n\tdef two(): pass\n\tdef three(): pass\n\n\t@staticmethod\n\tdef one(): pass\n```\n\nAs mentioned earlier, this is simply for making your code easier to read. The compiler itself will ignore all method declarations except ones marked with `staticmethod` decorator.\n\nYou could also use `external` decorator to bypass improperly imported RapydScript modules. However, if you actually have control of these modules, the better solution would be to fix those imports.\n\n\n### Method Binding\n\nBy default, RapydScript does not bind methods to the classes they're declared under. This behavior is unlike Python, but very much like the rest of JavaScript. For example, consider this code:\n\n```py\nclass Boy:\n\tdef __init__(self, name):\n\t\tself.name = name\n\n\tdef greet(self):\n\t\tif self:\n\t\t\tprint('My name is' + self.name)\n\ntod = Boy('Tod')\ntod.greet()                 # Hello, my name is Tod\ngetattr(tod, 'greet')()     # prints nothing\n```\n\nIn some cases, however, you may wish for the functions in the class to be\nautomatically bound when the objects of that class are instantiated. In order\nto do that, use a *scoped flag*, which is a simple instruction to the compiler\ntelling it to auto-bind methods, as shown below:\n\n```py\n\nclass AutoBound:\n\tfrom __python__ import bound_methods\n\n\tdef __init__(self):\n\t\tself.a = 3\n\n\tdef val(self):\n\t\treturn self.a\n\ngetattr(AutoBound(), 'val')() == 3\n```\n\nIf you want all classes in a module to be auto-bound simply put the scoped flag\nat the top of the module. You can even choose to have only a few methods of the\nclass auto-bound, like this:\n\n```py\nclass C:\n\n\tdef unbound1(self):\n\t\tpass # this method will not be auto-bound\n\n\tfrom __python__ import bound_methods\n\t# Methods below this line will be auto-bound\n\n\tdef bound(self):\n\t   pass # This method will be auto-bound\n\n\tfrom __python__ import no_bound_methods\n\t# Methods below this line will not be auto-bound\n\n\tdef unbound2(self):\n\t\tpass  # this method will be unbound\n```\n\nScoped flags apply only to the scope they are defined in, so if you define them\ninside a class declaration, they only apply to that class. If you define it at\nthe module level, it will only apply to all classes in the module that occur\nbelow that line, and so on.\n\nIterators\n----------\n\nRapydScript supports iterators, just like python, with a few differences to\nmake interoperating with other JavaScript code nicer. You can make an iterator\nfrom an array or object by simply calling the builtin ``iter()`` function, just\nas you would in python. The result of the function is a javascript iterator\nobject, that works both in RapydScript's for..in loops and ES6 JavaScript\nfor..of loops. Indeed they will work with any vanilla JavaScript code that\nexpects an iterable object. You can make your own classes iterable by defining\nan ``__iter__`` method, just as you would in python. For example:\n\n```python\n\tclass A:\n\n\t\tdef __init__(self):\n\t\t\tself.items = [1, 2, 3]\n\n\t\tdef __iter__(self):\n\t\t\treturn iter(self.items)\n\n\tfor x in A():\n\t   print (x)  # Will print 1, 2, 3\n```\n\nNote that unlike python, an iterators ``next()`` method does not return\nthe next value, but instead an object with two properties: ``done and value``.\n``value`` is the next value and done will be ``True`` when the iterator is\nexhausted. No ``StopIteration`` exception is raised. These choices were\nmade so that the iterator works with other JavaScript code.\n\nGenerators\n------------\n\nRapydScript supports generators (the python yield keyword). For example:\n\n```py\ndef f():\n\tfor i in range(3):\n\t\tyield i\n\n[x for x in f()] == [1, 2, 3]\n```\n\nThere is full support for generators including the Python 3, ```yield from```\nsyntax. \n\nGenerators create JavaScript iterator objects. For differences between python\nand JavaScript iterators, see the section on iterators above. \n\nCurrently, generators are down-converted to ES 5 switch statements. In the\nfuture, when ES 6 support is widespread, they will be converted to native\nJavaScript ES 6 generators.\n\nModules\n-------\n\nRapydScript's module system works almost exactly like Python's. Modules are\nfiles ending with the suffix ```.pyj``` and packages are directories containing\nan ```__init__.pyj``` file. The only caveat is that star imports are not\ncurrently supported (this is by design, star imports are easily abused).\nYou can import things from modules, just like you would in python:\n\n```py\nfrom mypackage.mymodule import something, something_else\n```\n\nWhen you import modules, the RapydScript compiler automatically generates a\nsingle large JavaScript file containing all the imported packages/modules and\ntheir dependencies, recursively. This makes it very easy to integrate the\noutput of RapydScript into your website.\n\nModules are searched for by default in the rapydscript builtin modules\ndirectory and the directory of the rapydscript file that you are\ncompiling. You can add additional directories to the searched locations via\nthe RAPYDSCRIPT_IMPORT_PATH environment variable or the --import-path option \nto the RapydScript compiler. See the documentation of the option for details.\n\nException Handling\n------------------\n\nException handling in RapydScript works just like it does in python. \n\nAn example:\n\n```py\ntry:\n\tsomefunc()\nexcept Exception as e:\n\timport traceback\n\ttraceback.print_exc()\nelse:\n    print('no exception occurred')\nfinally:\n    cleanup()\n```\n\nYou can create your own Exception classes by inheriting from `Exception`, which\nis the JavaScript Error class, for more details on this, see (the MDN documentation)[https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error]. \n\n```py\nclass MyError(Exception):\n\tdef __init__(self, message):\n\t\tself.name = 'MyError'\n\t\tself.message = message\n\nraise MyError('This is a custom error!')\n```\n\nYou can lump multiple errors in the same except block as well:\n\n```py\ntry:\n\tprint(foo)\nexcept ReferenceError, TypeError as e:\n\tprint(e.name + ':' + e.message)\n\traise # re-raise the exception\n```\n\nBasically, `try/except/finally` in RapydScript works very similar to the way it does in Python 3. \n\nScope Control\n-------------\n\nScope refers to the context of a variable. For example, a variable declared inside a function is not seen by the code outside the function. This variable's scope is local to the function. JavaScript controls scope via `var` keyword. Any variable that you start using without declaring with a `var` first will try to reference inner-most variable with the same name, if one doesn't exist, it will create a global variable with that name. For example, the following JavaScript code will not only return `a` incremented by 1, but also overwrite the global variable `a` with `a+1`:\n\n```py\na = 1;\na_plus_1 = function() {\n\treturn ++a;\n};\n```\n\nBasically, JavaScript defaults to outer or global scope if you omit `var`. This behavior can introduce some very frustrating bugs in large applications. To avoid this problem, RapydScript's scope preference works in reverse (same as Python's). RapydScript will prefer local-most scope, always creating a local variable if you perform any sort of assignment on it in a function (this is called variable shadowing). Shadowing can create another annoyance, however, of function's variable changes getting discarded. For example, at first, it looks like the following code will set `a` to 2:\n\n```py\na = 1\nb = 1\nincrement = def():\n\ta += b\nincrement()\n```\n\nWhen executed, however, increment() function will discard any changes to `a`. This is because, like Python, RapydScript will not allow you to edit variables declared in outer scope. As soon as you use any sort of assignment with `a` in the inner scope, RapydScript will declare it as an internal variable, shadowing `a` in the outer scope. One way around this is to use the `global` keyword, declaring `a` as a global variable. This, however, must be done in every function that edits `a`. It also litters global scope, which it frowned upon because it can accidentally overwrite an unrelated variable with the same name (declared by someone else or another library). RapydScript solves this by introducing `nonlocal` keyword (just like Python 3):\n\n```py\na = 1\nb = 1\nincrement = def():\n\tnonlocal a\n\ta += b\nincrement()\n```\n\nNote that `b` is not affected by shadowing. It's the assignment operator that triggers shadowing, you can read outer-scope variables without having to use `nonlocal`. You can combine multiple non-local arguments by separating them with a comma: `nonlocal a, b, c`. You can also chain `nonlocal` declarations to escape multiple scopes:\n\n```py\ndef fun1():\n\ta = 5\n\tb = fun2():\n\t\tnonlocal a\n\t\ta *= 2\n\t\tc = fun3():\n\t\t\tnonlocal a\n\t\t\ta += 1\n```\n\nShadowing is preferred in most cases, since it can't accidentally damage outside logic, and if you want to edit an external variable, you're usually better off assigning function's return value to it. There are cases, however, when using `nonlocal` makes the code cleaner. For compatibility with Python code, RapydScript also supports the `global` keyword, with the exception that if a variable is present both in the outer scope and the global scope, the variable from the outer scope will be used, rather than the variable from the global scope. This situation is rare in practice, and implementing it in RapydScript would require significant work, so RapydScript `global` remains a little incompatible with Python `global`.\n\n\nAvailable Libraries\n-------------------\n\nOne of Python's main strengths is the number of libraries available to the developer. This is something very few other `Python-in-a-browser` frameworks understand. In the browser JavaScript is king, and no matter how many libraries the community for the given project will write, the readily-available JavaScript libraries will always outnumber them. This is why RapydScript was designed with JavaScript and DOM integration in mind from the beginning. Indeed, plugging `underscore.js` in place of RapydScript's `stdlib` will work just as well, and some developers may choose to do so, after all, `underscore.js` is very Pythonic and very complete. \n\nIt is for that reason that I try to keep RapydScript bells and whistles to a minimum. RapydScript's main strength is easy integration with JavaScript and DOM, which allows me to stay sane and not rewrite my own versions of the libraries that are already available. That doesn't mean, however, that pythonic libraries can't be written for RapydScript. To prove that, I have implemented lightweight clones of several popular Python libraries and bundled them into RapydScript, you can find them in `src` directory. The following libraries are included:\n\n\tmath                # replicates almost all of the functionality from Python's math library\n\tre                  # replicates almost all of the functionality from Python's re library\n\trandom              # replicates most of the functionality from Python's random library\n\telementmaker        # easily construct DOM trees\n\taes                 # Implement AES symmetric encryption\n\tencodings           # Convert to/from UTF-8 bytearrays, base64 strings and native strings\n\tgettext             # Support for internationalization of your RapydScript app\n\toperator            # a subset of python;s operator module\n\nFor the most part, the logic implemented in these libraries functions identically to the Python versions.  I'd be happy to include more libraries, if other members of the community want to implement them (it's fun to do, `re.pyj` is a good example), but I want to reemphasize that unlike most other Python-to-JavaScript compilers, RapydScript doesn't need them to be complete since there are already tons of available JavaScript libraries that it can use natively.\n\nLinter\n---------\n\nThe RapydScript compiler includes its own, built in linter. The linter is\nmodeled on pyflakes, it catches instances of unused/undefined variables,\nfunctions, symbols, etc. While this sounds simple, it is surprisingly effective\nin practice. To run the linter:\n\n\trapydscript lint file.pyj\n\nIt will catch many errors, for example,\n\n```py\ndef f():\n\tsomevar = 1\n\treturn someva\n```\n\nThe linter will catch the typo above, saving you from having to discover it at\nruntime. Another example:\n\n```py\ndef f(somevar1):\n\tsomevar2 = somevar1 * 2\n\treturn somevar1\n```\n\nHere, you probably meant to return ``somevar2`` not ``somevar1``. The linter\nwill detect that somevar2 is defined but not used and warn you about it.\n\nThe linter is highly configurable, you can add to the list of built-in names\nthat the linter will not raise undefined errors for. You can turn off\nindividual checks that you do not find useful. See ``rapydscript lint -h`` for\ndetails.\n\nMaking RapydScript even more pythonic\n---------------------------------------\n\nRapydScript has three main goals: To be as fast as possible, to be as close to\npython as possible, to interoperate with external javascript libraries.\nSometimes these goals conflict and RapydScript chooses to be less pythonic in\nservice to the other two goals. Fortunately, there are many optional flags you\ncan use to reverse these compromises. The most important of these are called\n*scoped flags*. \n\nThe scoped flags are local to each scope, that means that if you use it in a\nmodule, it will only affect code in that module, it you use it in a function,\nit will only affect code in that function. In fact, you can even use it to\nsurround a few lines of code. There are many scoped flags, described else where\nin this document, see the sections on Method Auto-binding and the section on Dicts\nin this document.\n\nAnother common complaint is that in RapydScript strings dont have all the\nstring methods that python strings do. Fortunately, there is solution for that\nas well, described in the section on strings in this document.\n\n\nAdvanced Usage Topics\n---------------------\n\n#### Browser Compatibility\n\nRapydScript compiles your code such that it will work on browsers that are\ncompatible with the ES 5 JavaScript standard. The compiler has a \n``--js-version`` option that can also be used to output ES 6 only code. This\ncode is smaller and faster than the ES 5 version, but is not as widely\ncompatible.\n\n#### Tabs vs Spaces\n\nThis seems to be a very old debate. Python code conventions suggest 4-space\nindent. The old version of RapydScript relied on tabs, new one uses spaces\nsince that seems to be more consistent in both Python and JavaScript\ncommunities. Use whichever one you prefer, as long as you stay consistent. If\nyou intend to submit your code to RapydScript, it must use spaces to be\nconsistent with the rest of the code in the repository.\n\n#### External Libraries and Classes\n\nRapydScript will pick up any classes you declare yourself as well as native\nJavaScript classes. It will not, however, pick up class-like objects created by\noutside frameworks. There are two approaches for dealing with those. One is via\n`@external` decorator, the other is via `new` operator when declaring such\nobject. To keep code legible and consistent, I strongly prefer the use of\n`@external` decorator over the `new` operator for several reasons, even if it\nmay be more verbose:\n\n- `@external` decorator makes classes declared externally obvious to anyone looking at your code\n- class declaration that uses `@external` decorator can be exported into a reusable module\n- developers are much more likely to forget a single instance of `new` operator when declaring an object than to forget an import, the errors due to omitted `new` keyword are also likely to be more subtle and devious to debug\n\n#### Embedding the RapydScript compiler in your webpage\n\nYou can embed the RapydScript compiler in your webpage so that you can have\nyour webapp directly compile user supplied RapydScript code into JavaScript.\nTo do so, simply include the [embeddable rapydscript compiler](https://sw.kovidgoyal.net/rapydscript/repl/rapydscript.js) \nin your page, and use it to compile arbitrary RapydScript code. \n\nYou create the compiler by calling: `RapydScript.create_embedded_compiler()` and compile\ncode with `compiler.compile(code)`. You can execute the resulting JavaScript\nusing the standard `eval()` function. See the sample\nHTML below for an example.\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n    \u003chead\u003e\n        \u003cmeta charset=\"UTF-8\"\u003e\n        \u003ctitle\u003eTest embedded RapydScript\u003c/title\u003e\n        \u003cscript charset=\"UTF-8\" src=\"https://sw.kovidgoyal.net/rapydscript/repl/rapydscript.js\"\u003e\u003c/script\u003e\n        \u003cscript\u003e\nvar compiler = RapydScript.create_embedded_compiler();\nvar js = compiler.compile(\"def hello_world():\\n a='RapydScript is cool!'\\n print(a)\\n alert(a)\");\nwindow.onload = function() {\n    document.body.textContent = js;\n    eval(js);\n    eval('hello_world()');\n};\n        \u003c/script\u003e\n    \u003c/head\u003e\n    \u003cbody style=\"white-space:pre-wrap\"\u003e\u003c/body\u003e\n\u003c/html\u003e\n```\n\nThere are a couple of caveats when using the embedded compiler:\n\n* It only works when run in a modern browser (one that supports ES6) so no\n  Internet Explorer. You can have it work in an ES 5 runtime by passing\n  an option to the compile() method, like this:\n  ```\n  compiler.compile(code, {js_version:5})\n  ```\n  Note that doing this means that you cannot use generators and the\n  yield keyword in your RapydScript code.\n\n* Importing of modules only works with the standard library modules. There is\n  currently no way to make your own modules importable.\n\n* To generate the embedded compiler yourself (rapydscript.js) from a source\n  checkout of rapydscript, follow the instructions above for installing from\n  source, then run `bin/web-repl-export /path/to/export/directory`\n\nInternationalization\n-------------------------\n\nRapydScript includes support for internationalization -- i.e. the translation\nof user interface strings defined in the RapydScript source code. The interface\nfor this is very similar to Python's gettext module.  Suppose you have some\ncode that needs internalization support, the first step is to mark all\nuser-viewable strings as translatable:\n\n```py\nfrom gettext import gettext as _\ncreate_button(_('My Button'))\ncreate_button(_('Another Button'))\n```\n\nNow we need to extract these string from the source code into a .pot file which\ncan be used to create translations. To do that, run:\n\n```\nrapydscript gettext file.pyj \u003e messages.pot\n```\n\nNow send the `messages.pot` file to your translators. Suppose you get back a\n`de.po` file from the translators with German translations. You now need to\ncompile this into a format that can be used by RapydScript (RapydScript uses a\nJSON based format for easy operation over HTTP). Simply run:\n\n```\nrapydscript msgfmt \u003c messages.pot \u003e messages.json\n```\n\nNow, suppose you load up the translation data in your application. Exactly how\nyou do that is upto you. You can load it via Ajax or using a `\u003cscript\u003e` tag. To\nactivate the loaded data, simply run:\n\n```py\nfrom gettext import install\n\ninstall(translation_data)\n```\n\nNow everywhere in your program that you have calls to the `_()` function, you\nwill get translated output. So make sure you install the translation data\nbefore building the rest of your user-interface.\n\nJust as in python, you also have a `ngettext()` function for translating\nstrings that depend on a count.\n\n\nGotchas\n---------\n\nRapydScript has a couple of mutually conflicting goals: Be as close to python\nas possible, while also generating clean, performant JavaScript and making\ninterop with external JavaScript libraries easy.\n\nAs a result, there are some things in RapydScript that might come as surprises\nto an experienced Python developer. The most important such gotchas are listed\nbelow:\n\n- Truthiness in JavaScript is very different from Python. Empty lists and dicts\n  are ``False`` in Python but ``True`` in JavaScript. The compiler could work\n  around that, but not without a significant performance cost, so it is best to\n  just get used to checking the length instead of the object directly.\n\n- Operators in JavaScript are very different from Python. ``1 + '1'`` would be\n  an error in Python, but results in ``'11'`` in JavaScript. Similarly, ``[1] +\n  [1]`` is a new list in Python, but a string in JavaScript. Keep that in mind\n  as you write code. RapydScript does not implement operator overloading, as\n  method calls in JavaScript are very slow compared to raw operators.\n\n- There are many more keywords than in Python. Because RapydScript compiles\n  down to JavaScript, the set of keywords is all the keywords of Python + all\n  the keywords of JavaScript. For example, ``default`` and ``switch`` are\n  keywords in RapydScript. See [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords)\n  for a list of JavaScript keywords.\n\n- Method binding in RS is not automatic. So ``someobj.somemethod()`` will do the\n  right thing, but ``x = someobj.somethod; x()`` will not. You can turn method binding on\n  via a scoped flag. See the section above on method binding for details.\n\n- Nested comprehensions are not supported. So you cannot do this:\n\t[a for a in b for b in c]\n\n- RapydScript automatically appends 'new' keyword when using classes generated\n  by it, native JavaScript objects like `Image` and `RegExp` and classes from\n  other libraries marked as external. However, automatic new insertion depends\n  on the compiler being able to detect that a symbol resolves to a class.\n  Because of the dynamic nature of JavaScript this is not possible to do with\n  100% accuracy. So it is best to get in the habit of using the `new` keyword\n  yourself. Similarly, the compiler will try to convert SomeClass.method() into\n  SomeClass.prototype.method() for you, but again, this is not 100% reliable.\n\n- The {\"a\":b} syntax is used to create JavaScript hashes. These do not behave\n  like python dictionaries. To create python like dictionary objects, you\n  should use a scoped flag. See the section on dictionaries above for details.\n\n\nReasons for the fork\n----------------------\n\nThe fork was initially created because the original developer of RapydScript\ndid not have the time to keep up with the pace of development. Since then, \ndevelopment on the original RapydScript seems to have stalled completely.\nAlso, there are certain disagreements on the future direction of RapydScript.\n\nRegardless, this fork is not a hostile fork, if development on the original\never resumes, they are welcome to use the code from this fork. I have kept all\nnew code under the same license, to make that possible.\n\nSee the [Changelog](https://github.com/kovidgoyal/rapydscript-ng/blob/master/CHANGELOG.md)\nfor a list of changes to rapydscript-ng since the fork.\n\nFor some discussion surrounding the fork, see \n[this bug report](https://github.com/kovidgoyal/rapydscript-ng/issues/15)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkovidgoyal%2Frapydscript-ng","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkovidgoyal%2Frapydscript-ng","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkovidgoyal%2Frapydscript-ng/lists"}