diff --git a/README.md b/README.md index 43939fa..db3e43f 100644 --- a/README.md +++ b/README.md @@ -1,751 +1,284 @@ -##PythonJS 0.8.9 - - - -Community ---------- - -[https://groups.google.com/forum/#!forum/pythonjs](https://groups.google.com/forum/#!forum/pythonjs) - -irc freenode:: - - #pythonjs - Introduction -====== - -PythonJS is a Python to Javascript translator written in -Python, created by Amirouche Boubekki and Brett Hartshorn, -currently maintained and developed by Brett. It features: -list comprehensions, classes, multiple inheritance, operator -overloading, function and class decorators, generator functions, -HTML DOM, and easily integrates with JavaScript and external JavaScript -libraries. The generated code works in the Browser and in NodeJS. -Note: the Dart, CoffeeScript and Lua backends are still very experimental. - -Speed ---------------- -PythonJS allows you to select which features you need for -each section of your code, where you need performance you -can disable operator overloading, and other slow operations. -Features can be switched off and on for blocks of code using -`pythonjs.configure()` or the special `with` statements and -decorators described below. When PythonJS is run in fastest -mode (javascript mode) it beats PyPy in the Richards, and N-Body benchmarks. - - - -N-Body benchmark, PythonJS in javascript mode has similar performance to Dart. - -##pypubjs - integrated develop environment - -[](http://www.youtube.com/watch?v=WjIGAUbcN9Q) - -PythonJS includes an IDE called __pypubjs__ that is built using Node-Webkit, Ace.js, and Empythoned. It runs on Linux, OSX, and Windows. - - -Directly Calling JavaScript Functions ---------------- - -####HTML DOM Example - -
- - - - - - - - - - - -PythonJS allows you to call any JavaScript function directly -by wrapping it at runtime. Attributes of JavaScript objects -are also returned directly, like document.body. This allows -you to use the HTML DOM API just as you would in normal -JavaScript. - -If the JavaScript function you are calling takes a JavaScript -Object as the last argument you can call the function using -keyword arguments and they will be automatically converted -to a JavaScript Object. Any dictionaries or lists you pass -to a JavaScript function will be converted to: Array or Object. - -####Example - - - - - - -translator.py ------------ -To convert your python script into javascript use translator.py located in the "pythonjs" directory. You can give it a list of python files to translate at once. It will output the translation to stdout. The default output type is JavaScript. - -Usage:: - - translator.py [--dart|--coffee] file.py - -Example:: - - cd PythonJS - pythonjs/translator.py --coffee myscript.py > myscript.coffee +PythonJS is a transpiler written in Python that converts a python like language into fast +JavaScript. It also includes experimental backends that translate to: Dart, Lua, CoffeeScript, and Go. +[Syntax Documentation](https://github.com/PythonJS/PythonJS/blob/master/doc/syntax.md) -How does translation work? ----------------- -Translation to JavaScript is done in two steps:: - - +------------+ +-----------------+ +------------+ - ¦ .py source ¦--->¦ pythonjs subset ¦--->¦ .js source ¦ - +------------+ +-----------------+ +------------+ - -First, the script walks the AST tree of Python source and -translates it into the subset of Python called `pythonjs`. -This reduced subset is still valid Python code, and its -AST gets parsed again in the second translation phase -that converts it into final JavaScript form. - - -Python vs JavaScript Modes -------------------------- - -PythonJS has two primary modes you can write code in: `python` and `javascript`. The default mode is `python`, you can mark sections of your code to use either mode with `pythonjs.configure(javascript=True/False)` or nesting blocks inside `with python:` or `with javascript:`. The `javascript` mode can be used for sections of code where performance is a major concern. When in `javascript` mode Python dictionaries become JavaScript Objects. In both modes you can directly call external JavaScript functions, its only faster in `javascript` mode because function calls are direct without any wrapping. - - -Function Types ---------------- - -PythonJS has three main types of functions: `normal`, `fastdef`, and `javascript`. +Go backend +---------- +The Go backend uses a fully typed subset of Python, mixed with extra syntax inspired by Golang to output Go programs that can be compiled to native executeables, or translated to JavaScript using GopherJS. -By default a function is "normal" and fully emulates the Python standard, it allows for: arguments, keyword args with defaults, variable length arguments (*args) and variable length keyword args (**kwargs). Functions that are "normal" also have special logic that allows them to be called from external JavaScript like normal JavaScript functions (keyword args become normal positional arguments when called from JavaScript). Calling "normal" functions is slow because of this overhead, when you need faster function calls you can use "fastdef" or "javascript". +[Syntax Documentation](https://github.com/PythonJS/PythonJS/blob/master/doc/go_syntax.md) -Functions decorated with `@fastdef`, or inside a `with fastdef:` block become "fastdef" type functions. This makes calling them faster, but they do not support variable length arguments (*args) or variable length keyword args (**kwargs). -Another limitation is that when called from external JavaScript you must pack args into an Array as the first argument, and pack keyword arguments into an Object as the second argument. -Functions decorated with @javascript, or inside a `with javascript:` block, or following the call: `pythonjs.configure(javascript=True)` become `javascript` type functions, these offer the highest calling speed. They do not support *args or **kwargs. When called from external JavaScript, keyword arguments are not given by name, they become positional arguments that default to the default value if undefined. When called from within PythonJS code, they need to be called from inside a `with javascript:` block, or following the call `pythonjs.configure(javascript=True)` that sets all following code to be in `javascript` mode. +Getting Started +=============== +PythonJS can be run with regular Python, or fully self-hosted within +NodeJS using Empythoned. -####Example +To get started, you have two options: +1. install NodeJS, python-js package, and write a build script. +2. or install Python2 and use translator.py from this repo directly. - pythonjs.configure( javascript=True ) - def myfunc(x,y,z, a=1,b=2,c=3): - print x,y,z,a,b,c +1. Installing NodeJS Package +------------- +You can quickly get started with the stable version of PythonJS by installing the NodeJS package, +and writing a build script in javascript to compile your python scripts to javascript. +(Python2.7 is not required) -####Example JavaScript Translation +``` +npm install python-js +``` - myfunc = function(x, y, z, a, b, c) { - if (a === undefined) a = 1; - if (b === undefined) b = 2; - if (c === undefined) c = 3; - console.log(x, y, z, a, b, c); - } +NodeJS Quick Example +-------------- -Class Types ------------ +``` +var pythonjs = require('python-js'); +var pycode = "a = []; a.append('hello'); a.append('world'); print(a)"; +var jscode = pythonjs.translator.to_javascript( pycode ); +eval( pythonjs.runtime.javascript + jscode ); -PythonJS has two types of classes: `normal` and `javascript`. By default classes are `normal` and support operator overloading and properties. Calling methods on a `javascript` class is much faster than method calls on a `normal` class, but follow the same rules as described above for `javascript` type functions. Both class types can be used from external JavaScript, the only difference is that instances of a "normal" class can pass their methods directly as arguments to a function that will use the method as a callback - even if that external function depends on the context of `this`. Whereas instances of a `javascript` class can not directly pass their methods as arguments, because they depend on the calling context of `this` - if you are familiar with JavaScript this comes as no surprise. +``` -Example:: - pythonjs.configure( javascript=True ) - class A: - def __init__(self, x,y,z): - self.x = x - self.y = y - self.z = z - - def foo(self, w): - return self.x + w - -Example JavaScript Translation:: - - A = function(x, y, z) { - A.__init__(this, x,y,z); - } - - A.prototype.__init__ = function(x, y, z) { - this.x=x; - this.y=y; - this.z=z; - } - A.__init__ = function () { return A.prototype.__init__.apply(arguments[0], Array.prototype.slice.call(arguments,1)) }; - - A.prototype.foo = function(w) { - return (this.x + w); - } - A.foo = function () { return A.prototype.foo.apply(arguments[0], Array.prototype.slice.call(arguments,1)) }; - - -Method Overrides +Example Projects ---------------- -In the example above, you might be wondering why in the JavaScript translation, is the class A constructor calling `A.__init__(this, x,y,z)`, and why is the `__init__` method assigned `A.prototype` and then wrapped and assigned to `A.__init__`. This is done so that subclasses are able to override their parent's methods, but still have a way of calling them, an example that subclasses A will make this more clear. - -####Example - - class B( A ): - def __init__(self, w): - A.__init__(self, 10, 20, 30) - self.w = w - -####Example JavaScript Translation - - B = function(w) { - B.__init__(this, w); - } - - B.prototype.__init__ = function(w) { - A.__init__(this,10,20,30); - this.w=w; - } - B.__init__ = function () { return B.prototype.__init__.apply(arguments[0], Array.prototype.slice.call(arguments,1)) }; - - for (var n in A.prototype) { if (!(n in B.prototype)) { B.prototype[n] = A.prototype[n] }}; - - -The above output Javascript shows how the constructor for `B` calls `B.__init__` which then calls `B.prototype.__init__`. -`B.prototype.__init__` calls `A.__init__` passing `this` as the first argument. This emulates in JavaScript how unbound methods work in Python. When using the Dart backend, the output is different but the concept is the same - static "class methods" are created that implement the method body, the instance methods are just short stubs that call the static "class methods". - -####Example Dart Translation - - class B implements A { - var y; - var x; - var z; - var w; - B(w) {B.__init__(this,w);} - static void __init__(self, w) { - A.__init__(self,10,20,30); - self.w=w; - } - - foo(w) { return A.__foo(this,w); } - } - -Above the method `foo` calls the static class method `A.__foo`. Note that the static class methods are automatically prefixed with `__`. - +The example projects below, require the NodeJS python-js package. -Multiple Inheritance --------------------- +[https://github.com/PythonJS/pythonjs-demo-server-nodejs](https://github.com/PythonJS/pythonjs-demo-server-nodejs) -Multiple inheritance is fully supported for both JavaScript and Dart backends. When using the Dart backend it will generate stub-methods that call static class methods that are prefixed with `__`. -Methods that the subclass extends can call: ParentClassName.some_method(self) and this will be translated into: ParentClassName.__some_method(this) +[https://github.com/PythonJS/pypubjs](https://github.com/PythonJS/pypubjs) -#### Example - class A: - def foo(self): - print 'foo' - class B: - def bar(self): - print 'bar' - - class C( A, B ): - def call_foo_bar(self): - print 'call_foo_bar in subclass C' - self.foo() - self.bar() - - ## extend foo ## - def foo(self): - A.foo(self) - print 'foo extended' - -#### Example Dart Translation - - class A { - foo() { return A.__foo(this); } - static __foo(self) { - print("foo"); - } - - } - class B { - bar() { return B.__bar(this); } - static __bar(self) { - print("bar"); - } - - } - class C implements A, B { - call_foo_bar() { return C.__call_foo_bar(this); } - static __call_foo_bar(self) { - print("call_foo_bar in subclass C"); - self.foo(); - self.bar(); - } - - foo() { return C.__foo(this); } - static __foo(self) { - A.__foo(self); - print("foo extended"); - } - - bar() { return B.__bar(this); } - } - - -Generator Functions -------------------- - -Functions that use the `yield` keyword are generator functions. They allow you to quickly write complex iterables. -PythonJS supports simple generator functions that have a single for loop, and up to three `yield` statements. -The first `yield` comes before the for loop, and the final `yield` comes after the for loop. -The compiler will translate your generator function into a simple class with state-machine. This implementation -bypasses using the native JavaScript `yield` keyword, and ensures that your generator function can work in all web browsers. - -Instances of the generator function will have a next method. Using a for loop to iterate over a generator function will automatically call its next method. - -####Example - - def fib(n): - yield 'hello' - a, b = 0, 1 - for x in range(n): - yield a - a,b = b, a+b - yield 'world' - - def test(): - for n in fib(20): - print n - -####Example Output - - fib = function(n) { - this.n = n; - this.__head_yield = "hello"; - this.__head_returned = 0; - var __r_0; - __r_0 = [0, 1]; - this.a = __r_0[0]; - this.b = __r_0[1]; - this.__iter_start = 0; - this.__iter_index = 0; - this.__iter_end = this.n; - this.__done__ = 0; - } - - fib.prototype.next = function() { - if (( this.__head_returned ) == 0) { - this.__head_returned = 1; - return this.__head_yield; - } else { - if (( this.__iter_index ) < this.__iter_end) { - __yield_return__ = this.a; - var __r_1; - __r_1 = [this.b, (this.a + this.b)]; - this.a = __r_1[0]; - this.b = __r_1[1]; - this.__iter_index += 1 - return __yield_return__; - } else { - this.__done__ = 1; - __yield_return__ = "world"; - return __yield_return__; - } - } - } - - test = function(args, kwargs) { - var __iterator__, n; - var n, __generator__; - __generator__ = new fib(20); - while(( __generator__.__done__ ) != 1) { - n = __generator__.next(); - console.log(n); - } - } - - ---------------- - -Inline JavaScript ---------------- - -There are times that JavaScript needs to be directly inlined -into PythonJS code, this is done with the special -`JS([str])` function that takes a string literal as its only -argument. The compiler will insert the string directly into -the final output JavaScript. - -####JS Example - - JS("var arr = new Array()") - JS("var ob = new Object()") - JS("ob['key'] = 'value'") - if JS("Object.prototype.toString.call( arr ) === '[object Array]'"): - JS("arr.push('hello world')") - JS("arr.push( ob )") - -In the example above we create a new JavaScript Array. -Notice that the if-statement above has a condition that is -inlined JavaScript. Lets take a look at two alternative -ways this can be rewritten. - -1. JSArray, JSObject, and instanceof:: - - arr = JSArray() - ob = JSObject() - if instanceof(arr, Array): - arr.push('hello world') - arr.push( ob ) - -The special function JSArray will create a new JavaScript -Array object, and JSObject creates a new JavaScript Object. -The `instanceof` function will be translated into using the -'instanceof' JavaScript operator. At the end, arr.push is -called without wrapping it in `JS()`, this is allowed because -from PythonJS, we can directly call JavaScript functions by -dynamically wrapping it at runtime. - -This code is more clear than before, but the downside is -that the calls to arr.push will be slower because it gets -wrapped at runtime. To have fast and clear code we need to -use the final method below, `with javascript` - -2. with javascript:: - - with javascript: - arr = [] - ob = {} - if instanceof(arr, Array): - arr.push('hello world') - arr.push( ob ) - -The `with javascript:` statement can be used to mark a block -of code as being direct JavaScript. The compiler will -basically wrap each line it can in JS() calls. The calls to -arr.push will be fast because there is no longer any runtime -wrapping. Instead of using JSArray and JSObject you just -use the literal notation to create them. - ---------------- - -Calling PythonJS Functions from JavaScript ------------------------------- - -PythonJS functions can be used as callbacks in Javascript -code, there are no special calling conventions that you need -to worry about. Simply define a function in PythonJS and -call it from JavaScript. Note that if your PythonJS -function uses keyword arguments, you can use them as a -normal positional arguments. - -####Example - - # PythonJS - def my_pyfunction( a,b,c, optional='some default'): - print a,b,c, optional - - // javascript - my_pyfunction( 1,2,3, 'my kwarg' ); - - ---------------- - -Calling PythonJS Methods from JavaScript ------------------------------- - -Calling PythonJS methods is also simple, you just need to -create an instance of the class in PythonJS and then pass -the method to a JavaScript function, or assign it to a new -variable that the JavaScript code will use. PythonJS takes -care of wrapping the method for you so that `self` is bound -to the method, and is callable from JavaScript. - -####Example - - // javascript - function js_call_method( method_callback ) { - method_callback( 1,2,3 ) - } - - # PythonJS - class A: - def my_method(self, a,b,c): - print self, a,b,c - self.a = a - self.b = b - self.c = c - - a = A() - js_call_method( a.my_method ) - - ---------------- - -Passing PythonJS Instances to JavaScript ------------------------------- - -If you are doing something complex like deep integration -with an external JavaScript library, the above technique of -passing each method callback to JavaScript might become -inefficient. If you want to pass the PythonJS instance -itself and have its methods callable from JavaScript, you -can do this now simply by passing the instance. - -####Example - - // javascript - function js_function( pyob ) { - pyob.foo( 1,2,3 ) - pyob.bar( 4,5,6 ) - } - - # PythonJS - class A: - def foo(self, a,b,c): - print a+b+c - def bar(self, a,b,c): - print a*b*c - - a = A() - js_function( a ) - - ---------------- - -Define JavaScript Prototypes from PythonJS ------------------------------- - -If you are going beyond simple integration with an external -JavaScript library, and perhaps want to change the way it -works on a deeper level, you can modify JavaScript -prototypes from PythonJS using some special syntax. +2. translator.py +-------------------------------------- +If you want to run the latest version of the translator, you will need to install +Python2.7 and git clone this repo. (the NodeJS package above is not required) +Then, to translate your python script, directly run the `translator.py` script in the "pythonjs" directory. You can give it a list of python files to translate at once. +It will output the translation to stdout. The default output type is JavaScript. +An html file can also be used as input, python code inside a script tag: ` -