From 919e08a0de80839ff973749d5a22eed37c47484e Mon Sep 17 00:00:00 2001 From: hartsantler Date: Fri, 29 Aug 2014 09:11:48 -0700 Subject: [PATCH 01/17] automatically translate coffee scripts to javascript and inline them in the output html. --- pythonjs/python_to_pythonjs.py | 49 ++++++++++++++++++++++++++++------ pythonjs/pythonjs.py | 15 ++++++++--- pythonjs/translator.py | 1 + 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index f16a369..4bbba7a 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -144,14 +144,41 @@ def __init__(self, source=None, module=None, module_path=None, dart=False, coffe if source.strip().startswith('') - script = list() + if line.strip().startswith('') + script = list() + elif 'src=' in line and '~/' in line: ## external javascripts installed in users home folder + x = line.split('src="')[-1].split('"')[0] + if os.path.isfile(os.path.expanduser(x)): + o = [] + o.append( '') + if script is True: + self._html_tail.extend( o ) + else: + for y in o: + writer.write(y) + + else: + writer.write(line) + elif line.strip() == '': - if script: + if type(script) is list and len(script): source = '\n'.join(script) - script = True - self._html_tail.append( '') + script = True + self._html_tail.append( '') + else: + writer.write( line ) elif isinstance( script, list ): script.append( line ) @@ -174,7 +201,7 @@ def __init__(self, source=None, module=None, module_path=None, dart=False, coffe self._direct_operators = set() ## optimize "+" and "*" operator self._with_ll = False ## lowlevel - self._with_js = False + self._with_js = True self._in_lambda = False self._in_while_test = False self._use_threading = False @@ -272,8 +299,14 @@ def __init__(self, source=None, module=None, module_path=None, dart=False, coffe self._source = source.splitlines() + if '--debug' in sys.argv: + try: + tree = ast.parse( source ) + except: + raise SyntaxError(source) + else: + tree = ast.parse( source ) - tree = parse( source ) ## ast.parse self._generator_function_nodes = collect_generator_functions( tree ) for node in tree.body: diff --git a/pythonjs/pythonjs.py b/pythonjs/pythonjs.py index d9db207..41a234f 100755 --- a/pythonjs/pythonjs.py +++ b/pythonjs/pythonjs.py @@ -1265,6 +1265,7 @@ def main(source, requirejs=True, insert_runtime=True, webworker=False, function_ head = [] tail = [] script = False + osource = source if source.strip().startswith('') script = list() elif line.strip() == '': - if script: + if type(script) is list: source = '\n'.join(script) - script = True - tail.append( '') + script = True + tail.append( '') + elif script is True: + tail.append( '') + else: + head.append( '') elif isinstance( script, list ): script.append( line ) @@ -1315,6 +1320,10 @@ def main(source, requirejs=True, insert_runtime=True, webworker=False, function_ sys.stderr.write( lines[lineno] ) sys.stderr.write( '\n' ) + if '--debug' in sys.argv: + sys.stderr.write( osource ) + sys.stderr.write( '\n' ) + sys.exit(1) gen = JSGenerator( requirejs=requirejs, insert_runtime=insert_runtime, webworker=webworker, function_expressions=function_expressions ) diff --git a/pythonjs/translator.py b/pythonjs/translator.py index 0d12938..b133f66 100755 --- a/pythonjs/translator.py +++ b/pythonjs/translator.py @@ -51,6 +51,7 @@ def main(script, module_path=None): code = pythonjs_to_luajs( a ) else: a = python_to_pythonjs(script, module_path=module_path) + if isinstance(a, dict): res = {} for jsfile in a: From 1dc78a8c9b59b12f053ea3e25fafc8128e113457 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Sat, 30 Aug 2014 08:21:08 -0700 Subject: [PATCH 02/17] improved `new` syntax keyword. --- pythonjs/pythonjs.py | 5 ++++- pythonjs/typedpython.py | 5 +++-- regtests/html/date.html | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 regtests/html/date.html diff --git a/pythonjs/pythonjs.py b/pythonjs/pythonjs.py index 41a234f..b56c759 100755 --- a/pythonjs/pythonjs.py +++ b/pythonjs/pythonjs.py @@ -950,7 +950,10 @@ def visit_BinOp(self, node): op = self.visit(node.op) right = self.visit(node.right) - if op == '<<': + if op == '>>' and left == '__new__': + return ' new %s' %right + + elif op == '<<': if left in ('__go__receive__', '__go__send__'): return '<- %s' %right elif isinstance(node.left, ast.Call) and isinstance(node.left.func, ast.Name) and node.left.func.id in ('__go__array__', '__go__arrayfixed__', '__go__map__'): diff --git a/pythonjs/typedpython.py b/pythonjs/typedpython.py index d4d813c..5f9ff3e 100644 --- a/pythonjs/typedpython.py +++ b/pythonjs/typedpython.py @@ -155,8 +155,9 @@ def transform_source( source, strip=False ): elif c.startswith('import ') and '-' in c: c = c.replace('-', '__DASH__') elif ' new ' in c: - c += ')' * c.count(' new ') - c = c.replace(' new ', ' new(') + #c += ')' * c.count(' new ') + #c = c.replace(' new ', ' new(') + c = c.replace(' new ', ' __new__>>') diff --git a/regtests/html/date.html b/regtests/html/date.html new file mode 100644 index 0000000..97b3f50 --- /dev/null +++ b/regtests/html/date.html @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file From db92530aec387684ae811f0c48fa7db3eb1ecf21 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Sat, 30 Aug 2014 09:40:53 -0700 Subject: [PATCH 03/17] fixed `JS(" new XXX")` --- pythonjs/pythonjs.py | 4 +++- regtests/lang/new.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 regtests/lang/new.py diff --git a/pythonjs/pythonjs.py b/pythonjs/pythonjs.py index b56c759..8dd2704 100755 --- a/pythonjs/pythonjs.py +++ b/pythonjs/pythonjs.py @@ -919,7 +919,9 @@ def _inline_code_helper(self, s): ## TODO, should newline be changed here? s = s.replace('\n', '\\n').replace('\0', '\\0') ## AttributeError: 'BinOp' object has no attribute 's' - this is caused by bad quotes if s.strip().startswith('#'): s = '/*%s*/'%s - if '"' in s or "'" in s: ## can not trust direct-replace hacks + if '__new__>>' in s: ## fixes inline `JS("new XXX")` + s = s.replace('__new__>>', ' new ') + elif '"' in s or "'" in s: ## can not trust direct-replace hacks pass else: if ' or ' in s: diff --git a/regtests/lang/new.py b/regtests/lang/new.py new file mode 100644 index 0000000..b65296b --- /dev/null +++ b/regtests/lang/new.py @@ -0,0 +1,8 @@ +''' +js new keyword +''' + +def main(): + #a = new Date() ## this also works + a = JS(' new Date()') + TestError( a.getFullYear()==2014 ) From 628d2a8e664d34822b71fb6a0785767c5f9a9b1d Mon Sep 17 00:00:00 2001 From: hartsantler Date: Mon, 1 Sep 2014 07:21:49 -0700 Subject: [PATCH 04/17] fixed ` new XXX` tab space before new. --- pythonjs/typedpython.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonjs/typedpython.py b/pythonjs/typedpython.py index 5f9ff3e..c2ebf3e 100644 --- a/pythonjs/typedpython.py +++ b/pythonjs/typedpython.py @@ -154,11 +154,11 @@ def transform_source( source, strip=False ): output.append( indent + '@returns(%s)' %rtype) elif c.startswith('import ') and '-' in c: c = c.replace('-', '__DASH__') - elif ' new ' in c: - #c += ')' * c.count(' new ') - #c = c.replace(' new ', ' new(') - c = c.replace(' new ', ' __new__>>') + if ' new ' in c: + c = c.replace(' new ', ' __new__>>') + if '\tnew ' in c: + c = c.replace('\tnew ', ' __new__>>') ## golang From c7a77447d4a95edef50bd4ee064c93e7c14f8d78 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Wed, 3 Sep 2014 02:18:45 -0700 Subject: [PATCH 05/17] new backend Gopherjs --- pythonjs/translator.py | 13 +++++++++++++ regtests/run.py | 19 +++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pythonjs/translator.py b/pythonjs/translator.py index b133f66..0b772f9 100755 --- a/pythonjs/translator.py +++ b/pythonjs/translator.py @@ -27,6 +27,19 @@ def main(script, module_path=None): if '--go' in sys.argv: a = python_to_pythonjs(script, go=True, module_path=module_path) code = pythonjs_to_go( a ) + elif '--gopherjs' in sys.argv: + a = python_to_pythonjs(script, go=True, module_path=module_path) + code = pythonjs_to_go( a ) + + exe = os.path.expanduser('~/go/bin/gopherjs') + if not os.path.isfile(exe): + raise RuntimeError('gopherjs not installed to ~/go/bin/gopherjs') + import subprocess + path = '/tmp/gopherjs-input.go' + open(path, 'wb').write(code) + subprocess.check_call([exe, 'build', path], cwd='/tmp') + code = open('/tmp/gopherjs-input.js', 'rb').read() + elif '--dart' in sys.argv: a = python_to_pythonjs(script, dart=True, module_path=module_path) code = pythonjs_to_dart( a ) diff --git a/regtests/run.py b/regtests/run.py index 9a8702a..cf5ff34 100755 --- a/regtests/run.py +++ b/regtests/run.py @@ -194,6 +194,7 @@ def run_old_pypy_test_on(filename): luajs_runnable = os.path.isfile( lua2js ) and '--lua2js' in sys.argv go_runnable = runnable( 'go version') +gopherjs_runnable = runnable( 'gopherjs') assert rhino_runnable or node_runnable @@ -474,7 +475,7 @@ def run_python3_test_on(filename): -def translate_js(filename, javascript=False, dart=False, coffee=False, lua=False, luajs=False, go=False, multioutput=False, requirejs=True): +def translate_js(filename, javascript=False, dart=False, coffee=False, lua=False, luajs=False, go=False, gopherjs=False, multioutput=False, requirejs=True): global tmpname tmpname = os.path.join( tempfile.gettempdir(), @@ -506,7 +507,7 @@ def translate_js(filename, javascript=False, dart=False, coffee=False, lua=False ] content = '\n'.join( source ) - elif go: + elif go or gopherjs: content = patch_python(filename, backend='GO') else: @@ -535,6 +536,8 @@ def translate_js(filename, javascript=False, dart=False, coffee=False, lua=False cmd.append( '--luajs') elif go: cmd.append( '--go' ) + elif gopherjs: + cmd.append( '--gopherjs' ) if not requirejs: cmd.append( '--no-wrapper' ) @@ -799,6 +802,15 @@ def run_go(content): return run_command( '/tmp/regtest-go' ) +def run_pythonjs_gopherjs_test(dummy_filename): + """PythonJS (Gopherjs)""" + return run_if_no_error(run_gopherjs_node) + +def run_gopherjs_node(content): + """Run Gopherjs using Node""" + write("%s.js" % tmpname, content) + return run_command("node %s.js" % tmpname) + def run_html_test( filename, sum_errors ): lines = open(filename, 'rb').read().decode('utf-8').splitlines() filename = os.path.split(filename)[-1] @@ -967,6 +979,9 @@ def display(function): js = translate_js(filename, go=True) display(run_pythonjs_go_test) + if gopherjs_runnable: + js = translate_js(filename, gopherjs=True) + display(run_pythonjs_gopherjs_test) print() return sum_errors From 276b5f79b64b38de676c01384bd4460ba822f829 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Wed, 3 Sep 2014 09:18:07 -0700 Subject: [PATCH 06/17] Go backend: fixed chan test, and sending data over a channel. --- pythonjs/python_to_pythonjs.py | 1 + pythonjs/pythonjs_to_go.py | 21 ++++++++++++++++----- pythonjs/typedpython.py | 9 +++++---- regtests/go/chan.py | 15 +++++++-------- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index 4bbba7a..d3fa084 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -2780,6 +2780,7 @@ def visit_FunctionDef(self, node): local_typedefs.append( '%s=%s' %(kw.arg, kwval)) if decorator.func.id=='typedef_chan': typedef_chans.append( kw.arg ) + writer.write('@__typedef_chan__(%s=%s)' %(kw.arg, kwval)) else: writer.write('@__typedef__(%s=%s)' %(kw.arg, kwval)) diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index 9eecbe4..4bccfe4 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -217,20 +217,26 @@ def _visit_call_helper_go(self, node): return 'go %s' %self.visit(node.args[0]) elif name == '__gomake__': return 'make(%s)' %self.visit(node.args[0]) + elif name == '__go_make_chan__': + return 'make(chan %s)' %self.visit(node.args[0]) else: - return SyntaxError('invalid special go call') + raise SyntaxError('invalid special go call') def _visit_function(self, node): if self._function_stack[0] is node: self._vars = set() args_typedefs = {} + chan_args_typedefs = {} return_type = None for decor in node.decorator_list: if isinstance(decor, ast.Call) and isinstance(decor.func, ast.Name) and decor.func.id == '__typedef__': for key in decor.keywords: #args_typedefs[ key.arg ] = key.value.id args_typedefs[ key.arg ] = self.visit(key.value) + elif isinstance(decor, ast.Call) and isinstance(decor.func, ast.Name) and decor.func.id == '__typedef_chan__': + for key in decor.keywords: + chan_args_typedefs[ key.arg ] = self.visit(key.value) elif isinstance(decor, ast.Call) and isinstance(decor.func, ast.Name) and decor.func.id == 'returns': if decor.keywords: raise SyntaxError('invalid go return type') @@ -248,7 +254,7 @@ def _visit_function(self, node): for i, arg in enumerate(node.args.args): arg_name = arg.id - if arg_name not in args_typedefs: + if arg_name not in args_typedefs.keys()+chan_args_typedefs.keys(): if arg_name=='self': assert i==0 is_method = True @@ -258,8 +264,13 @@ def _visit_function(self, node): err += '\n missing typedef: %s' %arg.id raise SyntaxError(err) - arg_type = args_typedefs[arg_name] - a = '%s %s' %(arg_name, arg_type) + if arg_name in args_typedefs: + arg_type = args_typedefs[arg_name] + a = '%s %s' %(arg_name, arg_type) + else: + arg_type = chan_args_typedefs[arg_name] + a = '%s chan %s' %(arg_name, arg_type) + dindex = i - offset if a.startswith('__variable_args__'): ## TODO support go `...` varargs @@ -347,7 +358,7 @@ def visit_Assign(self, node): elif isinstance(node.value, ast.BinOp) and self.visit(node.value.op)=='<<' and isinstance(node.value.left, ast.Name) and node.value.left.id=='__go__send__': target = self.visit(target) value = self.visit(node.value.right) - return 'var %s <- %s;' % (target, value) + return '%s <- %s;' % (target, value) elif not self._function_stack: target = self.visit(target) diff --git a/pythonjs/typedpython.py b/pythonjs/typedpython.py index c2ebf3e..7f31496 100644 --- a/pythonjs/typedpython.py +++ b/pythonjs/typedpython.py @@ -23,7 +23,7 @@ GO_SPECIAL_CALLS = { 'go' : '__go__', - 'go.channel' : '__gomake__', + 'go.channel' : '__go_make_chan__', } def transform_source( source, strip=False ): @@ -267,8 +267,9 @@ def transform_source( source, strip=False ): y, kw = y.split('=') arg, typedef = y.split(':') chan = False - if len(arg.split()) == 2: - chan, arg = arg.split() + if len(typedef.strip().split()) == 2: + chan = True + typedef = typedef.strip().split()[-1] if '*' in arg: arg_name = arg.split('*')[-1] else: @@ -385,7 +386,7 @@ def xxx(): def call_method( cb:func(int)(int) ) ->int: return cb(3) -def wrapper(a:int, chan c:int): +def wrapper(a:int, c:chan int): result = longCalculation(a) c <- result diff --git a/regtests/go/chan.py b/regtests/go/chan.py index 1ddbcf1..fdf6e02 100644 --- a/regtests/go/chan.py +++ b/regtests/go/chan.py @@ -1,17 +1,16 @@ -"""a <- """ +"""send int over channel""" + +def wrapper(a:int, c: chan int): + result = 100 + c <- result def main(): c = go.channel(int) - - def wrapper(a:int, chan c:int): - result = 100 - c <- result - go( - wrapper(17, c) - ) + go( wrapper(17, c) ) # Do other work in the current goroutine until the channel has a result. x = <-c print(x) + TestError(x==100) From c1a088b9cd45b1a37b1945290fec9326188ef11c Mon Sep 17 00:00:00 2001 From: hartsantler Date: Wed, 3 Sep 2014 13:40:18 -0700 Subject: [PATCH 07/17] Go backend: fixed select on channel --- pythonjs/python_to_pythonjs.py | 2 +- pythonjs/pythonjs_to_go.py | 61 ++++++++++++++++++++++++++++++++-- regtests/go/select.py | 44 +++++++++++++++++++----- 3 files changed, 95 insertions(+), 12 deletions(-) diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index d3fa084..802ec1a 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -2107,7 +2107,7 @@ def _visit_assign_helper(self, node, target): writer.write("%s = __ternary_operator__(instanceof(%s,Array), %s[%s], %s)" % (self.visit(target), r, r,i, fallback )) def visit_Print(self, node): - writer.write('print %s' % ', '.join(map(self.visit, node.values))) + writer.write('print(%s)' % ', '.join(map(self.visit, node.values))) def visit_Str(self, node): s = node.s.replace('\\','\\\\').replace('\n', '\\n').replace('\r', '\\r').replace('\0', '\\0') diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index 4bccfe4..73e4864 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -18,6 +18,7 @@ def __init__(self, requirejs=False, insert_runtime=False): self._class_props = dict() self._vars = set() + self._known_vars = set() self._kwargs_type_ = dict() def visit_ClassDef(self, node): @@ -89,7 +90,13 @@ def visit_Slice(self, node): def visit_Print(self, node): - r = [ 'fmt.Println(%s);' %self.visit(e) for e in node.values] + r = [] + for e in node.values: + s = self.visit(e) + if isinstance(e, ast.List): + r.append('fmt.Println(%s);' %s[1:-1]) + else: + r.append('fmt.Println(%s);' %s) return ''.join(r) def visit_Expr(self, node): @@ -225,6 +232,7 @@ def _visit_call_helper_go(self, node): def _visit_function(self, node): if self._function_stack[0] is node: self._vars = set() + self._known_vars = set() args_typedefs = {} chan_args_typedefs = {} @@ -350,6 +358,47 @@ def _visit_call_helper_var(self, node): #return '\n'.join(out) return '' + def visit_With(self, node): + r = [] + is_switch = False + if isinstance( node.context_expr, ast.Name ) and node.context_expr.id == '__default__': + r.append('default:') + elif isinstance( node.context_expr, ast.Name ) and node.context_expr.id == '__select__': + r.append('select {') + is_switch = True + elif isinstance( node.context_expr, ast.Call ): + if not isinstance(node.context_expr.func, ast.Name): + raise SyntaxError( self.visit(node.context_expr)) + + if len(node.context_expr.args): + a = self.visit(node.context_expr.args[0]) + else: + assert len(node.context_expr.keywords) + ## need to catch if this is a new variable ## + name = node.context_expr.keywords[0].arg + if name not in self._known_vars: + a = '%s := %s' %(name, self.visit(node.context_expr.keywords[0].value)) + else: + a = '%s = %s' %(name, self.visit(node.context_expr.keywords[0].value)) + + if node.context_expr.func.id == '__case__': + r.append('case %s:' %a) + elif node.context_expr.func.id == '__switch__': + r.append('switch (%s) {' %self.visit(node.context_expr.args[0])) + is_switch = True + else: + raise SyntaxError( 'invalid use of with') + + + for b in node.body: + a = self.visit(b) + if a: r.append(a) + + if is_switch: + r.append('}') + + return '\n'.join(r) + def visit_Assign(self, node): target = node.targets[0] if isinstance(target, ast.Tuple): @@ -369,15 +418,22 @@ def visit_Assign(self, node): target = self.visit(target) value = self.visit(node.value) self._vars.remove( target ) + self._known_vars.add( target ) return '%s := %s;' % (target, value) else: target = self.visit(target) value = self.visit(node.value) + + #if '<-' in value: + # raise RuntimeError(target+value) + return '%s = %s;' % (target, value) def visit_While(self, node): - body = [ 'for %s {' %self.visit(node.test)] + cond = self.visit(node.test) + if cond == 'true' or cond == '1': cond = '' + body = [ 'for %s {' %cond] self.push() for line in list( map(self.visit, node.body) ): body.append( self.indent()+line ) @@ -393,6 +449,7 @@ def _inline_code_helper(self, s): def main(script, insert_runtime=True): + if insert_runtime: dirname = os.path.dirname(os.path.abspath(__file__)) dirname = os.path.join(dirname, 'runtime') diff --git a/regtests/go/select.py b/regtests/go/select.py index f0cb7f0..6bcb8fe 100644 --- a/regtests/go/select.py +++ b/regtests/go/select.py @@ -1,17 +1,43 @@ """go select""" +def send_data( A:chan int, B:chan int, X:int, Y:int): + while True: + print('sending data..') + A <- X + B <- Y + +def select_loop(A:chan int, B:chan int, W:chan int) -> int: + print('starting select loop') + y = 0 + while True: + print('select loop:',y) + select: + case x = <- A: + y += x + W <- y + case x = <- B: + y += x + W <- y + print('end select loop', y) + return y + def main(): a = go.channel(int) b = go.channel(int) + w = go.channel(int) - a <- 1 - b <- 2 - y = 0 + go( + select_loop(a,b, w) + ) + + + go( + send_data(a,b, 5, 10) + ) - select: - case x = <- a: - y += x - case x = <- b: - y += x + z = 0 + while z < 100: + z = <- w + print('main loop', z) - print(y) + print('end test') \ No newline at end of file From 284180b42f104a024b127b97e7b4e3f48230b831 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Thu, 4 Sep 2014 10:44:25 -0700 Subject: [PATCH 08/17] function expressions are now the default function type (hoisted functions are not default anymore) this breaks some of the regtests that were prototypes for auto async transform using functions like `sleep`. --- pythonjs/pythonjs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonjs/pythonjs.py b/pythonjs/pythonjs.py index 8dd2704..8eb22bd 100755 --- a/pythonjs/pythonjs.py +++ b/pythonjs/pythonjs.py @@ -25,7 +25,7 @@ def __init__(self, node): RuntimeError.__init__(self) class JSGenerator(NodeVisitor): #, inline_function.Inliner): - def __init__(self, requirejs=True, insert_runtime=True, webworker=False, function_expressions=False): + def __init__(self, requirejs=True, insert_runtime=True, webworker=False, function_expressions=True): #writer = code_writer.Writer() #self.setup_inliner( writer ) self._func_expressions = function_expressions @@ -1266,7 +1266,7 @@ def generate_runtime(): ] return '\n'.join( lines ) -def main(source, requirejs=True, insert_runtime=True, webworker=False, function_expressions=False): +def main(source, requirejs=True, insert_runtime=True, webworker=False, function_expressions=True): head = [] tail = [] script = False From de044f4785c9806c92c04708e6d6f54853db147c Mon Sep 17 00:00:00 2001 From: hartsantler Date: Fri, 5 Sep 2014 08:55:02 -0700 Subject: [PATCH 09/17] fixed array slice `a[:0]` --- pythonjs/pythonjs.js | 108 +++++++++++++++++------------------ pythonjs/runtime/builtins.py | 19 ++++-- 2 files changed, 69 insertions(+), 58 deletions(-) diff --git a/pythonjs/pythonjs.js b/pythonjs/pythonjs.js index 8feb509..fa6a00b 100644 --- a/pythonjs/pythonjs.js +++ b/pythonjs/pythonjs.js @@ -484,15 +484,6 @@ var __getargs__ = function(func_name, signature, args, kwargs) { return out; } -try { -/*pass*/ -} catch(__exception__) { -console.trace(); -console.error(__exception__, __exception__.message); -console.error("line 4: pythonjs.configure( runtime_exceptions=False )"); -throw new RuntimeError("line 4: pythonjs.configure( runtime_exceptions=False )"); - -} _PythonJS_UID = 0; IndexError = function(msg) {this.message = msg || "";}; IndexError.prototype = Object.create(Error.prototype); IndexError.prototype.name = "IndexError"; KeyError = function(msg) {this.message = msg || "";}; KeyError.prototype = Object.create(Error.prototype); KeyError.prototype.name = "KeyError"; @@ -507,7 +498,8 @@ var __getfast__ = function(ob, attr) { } else { return v; } -};__getfast__.is_wrapper = true; +} + var __wrap_function__ = function(f) { f.is_wrapper = true; @@ -1459,7 +1451,7 @@ var __create_class__ = function(class_name, parents, attrs, props) { } else { wrapper = __get__(object, name); if (__test_if_true__(! (wrapper.is_wrapper))) { - console.log("RUNTIME ERROR: failed to get wrapper for:", name); + console.log(["RUNTIME ERROR: failed to get wrapper for:", name]); } } } @@ -1496,7 +1488,7 @@ var type = function(args, kwargs) { var ob_or_class_name = __args__['ob_or_class_name']; var bases = __args__['bases']; var class_dict = __args__['class_dict']; - "\n type(object) -> the object's type\n type(name, bases, dict) -> a new(type ## broken? - TODO test)\n "; + "\n type(object) -> the object's type\n type(name, bases, dict) -> a __new__>>type ## broken? - TODO test\n "; if (__test_if_true__((( bases ) === null && ( class_dict ) === null))) { return ob_or_class_name.__class__; } else { @@ -1537,7 +1529,7 @@ var getattr = function(args, kwargs) { if (__test_if_true__((prop && prop["get"]))) { return prop["get"]([ob], __jsdict([])); } else { - console.log("ERROR: getattr property error", prop); + console.log(["ERROR: getattr property error", prop]); } } else { return __get__(ob, attr); @@ -1563,7 +1555,7 @@ var setattr = function(args, kwargs) { if (__test_if_true__((prop && prop["set"]))) { prop["set"]([ob, value], __jsdict([])); } else { - console.log("ERROR: setattr property error", prop); + console.log(["ERROR: setattr property error", prop]); } } else { __set__(ob, attr, value); @@ -2007,7 +1999,9 @@ var _setup_array_prototype = function() { var i,arr,n; arr = []; start = (start | 0); - stop = (stop | this.length); + if (( stop ) === undefined) { + stop = this.length; + } if (( start ) < 0) { start = (this.length + start); } @@ -2327,7 +2321,7 @@ var sum = function(args, kwargs) { var arr = __args__['arr']; a = 0; var b,__iterator__40; - __iterator__40 = __get__(__get__(arr, "__iter__", "no iterator - line 1064: for b in arr:"), "__call__")([], __NULL_OBJECT__); + __iterator__40 = __get__(__get__(arr, "__iter__", "no iterator - line 1065: for b in arr:"), "__call__")([], __NULL_OBJECT__); var __next__40; __next__40 = __get__(__iterator__40, "next"); while (( __iterator__40.index ) < __iterator__40.length) { @@ -2383,7 +2377,7 @@ var next = function(args, kwargs) { } __args__ = __getargs__("next", __sig__, args, kwargs); var obj = __args__['obj']; - return __get__(__get__(obj, "next", "missing attribute `next` - line 1082: return obj.next()"), "__call__")(); + return __get__(__get__(obj, "next", "missing attribute `next` - line 1083: return obj.next()"), "__call__")(); };next.is_wrapper = true; var map = function(args, kwargs) { var arr,v; @@ -2400,7 +2394,7 @@ var map = function(args, kwargs) { var objs = __args__['objs']; arr = []; var ob,__iterator__41; - __iterator__41 = __get__(__get__(objs, "__iter__", "no iterator - line 1085: for ob in objs:"), "__call__")([], __NULL_OBJECT__); + __iterator__41 = __get__(__get__(objs, "__iter__", "no iterator - line 1086: for ob in objs:"), "__call__")([], __NULL_OBJECT__); var __next__41; __next__41 = __get__(__iterator__41, "next"); while (( __iterator__41.index ) < __iterator__41.length) { @@ -2425,7 +2419,7 @@ var filter = function(args, kwargs) { var objs = __args__['objs']; arr = []; var ob,__iterator__42; - __iterator__42 = __get__(__get__(objs, "__iter__", "no iterator - line 1092: for ob in objs:"), "__call__")([], __NULL_OBJECT__); + __iterator__42 = __get__(__get__(objs, "__iter__", "no iterator - line 1093: for ob in objs:"), "__call__")([], __NULL_OBJECT__); var __next__42; __next__42 = __get__(__iterator__42, "next"); while (( __iterator__42.index ) < __iterator__42.length) { @@ -2450,7 +2444,7 @@ var min = function(args, kwargs) { var lst = __args__['lst']; a = null; var value,__iterator__43; - __iterator__43 = __get__(__get__(lst, "__iter__", "no iterator - line 1099: for value in lst:"), "__call__")([], __NULL_OBJECT__); + __iterator__43 = __get__(__get__(lst, "__iter__", "no iterator - line 1100: for value in lst:"), "__call__")([], __NULL_OBJECT__); var __next__43; __next__43 = __get__(__iterator__43, "next"); while (( __iterator__43.index ) < __iterator__43.length) { @@ -2479,7 +2473,7 @@ var max = function(args, kwargs) { var lst = __args__['lst']; a = null; var value,__iterator__44; - __iterator__44 = __get__(__get__(lst, "__iter__", "no iterator - line 1105: for value in lst:"), "__call__")([], __NULL_OBJECT__); + __iterator__44 = __get__(__get__(lst, "__iter__", "no iterator - line 1106: for value in lst:"), "__call__")([], __NULL_OBJECT__); var __next__44; __next__44 = __get__(__iterator__44, "next"); while (( __iterator__44.index ) < __iterator__44.length) { @@ -2585,7 +2579,7 @@ var __Iterator___init__ = function(args, kwargs) { self.obj = obj; self.index = index; self.length = len([obj], __NULL_OBJECT__); - self.obj_get = __get__(obj, "get", "missing attribute `get` - line 1133: self.obj_get = obj.get ## cache this for speed"); + self.obj_get = __get__(obj, "get", "missing attribute `get` - line 1134: self.obj_get = obj.get ## cache this for speed"); };__Iterator___init__.is_wrapper = true; __Iterator_attrs.__init__ = __Iterator___init__; var __Iterator_next = function(args, kwargs) { @@ -2716,7 +2710,7 @@ var __dict___init__ = function(args, kwargs) { ob = js_object; if (__test_if_true__(ob instanceof Array)) { var o,__iterator__45; - __iterator__45 = __get__(__get__(ob, "__iter__", "no iterator - line 1195: for o in ob:"), "__call__")([], __NULL_OBJECT__); + __iterator__45 = __get__(__get__(ob, "__iter__", "no iterator - line 1196: for o in ob:"), "__call__")([], __NULL_OBJECT__); var __next__45; __next__45 = __get__(__iterator__45, "next"); while (( __iterator__45.index ) < __iterator__45.length) { @@ -2729,7 +2723,7 @@ var __dict___init__ = function(args, kwargs) { v = o["value"]; } try { -__get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1202: self.__setitem__( k,v )"), "__call__")([k, v], __NULL_OBJECT__); +__get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1203: self.__setitem__( k,v )"), "__call__")([k, v], __NULL_OBJECT__); } catch(__exception__) { if (__exception__ == KeyError || __exception__ instanceof KeyError) { throw new KeyError("error in dict init, bad key"); @@ -2740,16 +2734,16 @@ throw new KeyError("error in dict init, bad key"); } else { if (__test_if_true__(isinstance([ob, dict], __NULL_OBJECT__))) { var key,__iterator__46; - __iterator__46 = __get__(__get__(__jsdict_keys(ob), "__iter__", "no iterator - line 1206: for key in ob.keys():"), "__call__")([], __NULL_OBJECT__); + __iterator__46 = __get__(__get__(__jsdict_keys(ob), "__iter__", "no iterator - line 1207: for key in ob.keys():"), "__call__")([], __NULL_OBJECT__); var __next__46; __next__46 = __get__(__iterator__46, "next"); while (( __iterator__46.index ) < __iterator__46.length) { key = __next__46(); - value = ((ob instanceof Array) ? ob[key] : __get__(ob, "__getitem__", "line 1207: value = ob[ key ]")([key], __NULL_OBJECT__)); - __get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1208: self.__setitem__( key, value )"), "__call__")([key, value], __NULL_OBJECT__); + value = ((ob instanceof Array) ? ob[key] : __get__(ob, "__getitem__", "line 1208: value = ob[ key ]")([key], __NULL_OBJECT__)); + __get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1209: self.__setitem__( key, value )"), "__call__")([key, value], __NULL_OBJECT__); } } else { - console.log("ERROR init dict from:", js_object); + console.log(["ERROR init dict from:", js_object]); throw new TypeError; } } @@ -2771,15 +2765,15 @@ var __dict_jsify = function(args, kwargs) { var self = __args__['self']; keys = __object_keys__([self["$wrapped"]], __NULL_OBJECT__); var key,__iterator__47; - __iterator__47 = __get__(__get__(keys, "__iter__", "no iterator - line 1215: for key in keys:"), "__call__")([], __NULL_OBJECT__); + __iterator__47 = __get__(__get__(keys, "__iter__", "no iterator - line 1216: for key in keys:"), "__call__")([], __NULL_OBJECT__); var __next__47; __next__47 = __get__(__iterator__47, "next"); while (( __iterator__47.index ) < __iterator__47.length) { key = __next__47(); - value = __get__(self["$wrapped"], "__getitem__", "line 1216: value = self[...][key]")([key], __NULL_OBJECT__); + value = __get__(self["$wrapped"], "__getitem__", "line 1217: value = self[...][key]")([key], __NULL_OBJECT__); if ((typeof(value) instanceof Array ? JSON.stringify(typeof(value))==JSON.stringify("object") : typeof(value)==="object")) { if (__test_if_true__(hasattr([value, "jsify"], __NULL_OBJECT__))) { - __get__(__get__(self["$wrapped"], "__setitem__"), "__call__")([key, __get__(__get__(value, "jsify", "missing attribute `jsify` - line 1219: self[...][key] = value.jsify()"), "__call__")()], {}); + __get__(__get__(self["$wrapped"], "__setitem__"), "__call__")([key, __get__(__get__(value, "jsify", "missing attribute `jsify` - line 1220: self[...][key] = value.jsify()"), "__call__")()], {}); } } else { if ((typeof(value) instanceof Array ? JSON.stringify(typeof(value))==JSON.stringify("function") : typeof(value)==="function")) { @@ -2835,7 +2829,7 @@ var __dict_has_key = function(args, kwargs) { var key = __args__['key']; __dict = self["$wrapped"]; if (__test_if_true__(typeof(key) === 'object' || typeof(key) === 'function')) { - key = __get__(key, "__uid__", "missing attribute `__uid__` - line 1232: key = key.__uid__"); + key = __get__(key, "__uid__", "missing attribute `__uid__` - line 1233: key = key.__uid__"); } if (__test_if_true__(key in __dict)) { return true; @@ -2858,12 +2852,12 @@ var __dict_update = function(args, kwargs) { var self = __args__['self']; var other = __args__['other']; var key,__iterator__48; - __iterator__48 = __get__(__get__(other, "__iter__", "no iterator - line 1238: for key in other:"), "__call__")([], __NULL_OBJECT__); + __iterator__48 = __get__(__get__(other, "__iter__", "no iterator - line 1239: for key in other:"), "__call__")([], __NULL_OBJECT__); var __next__48; __next__48 = __get__(__iterator__48, "next"); while (( __iterator__48.index ) < __iterator__48.length) { key = __next__48(); - __get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1239: self.__setitem__( key, other[key] )"), "__call__")([key, ((other instanceof Array) ? other[key] : __get__(other, "__getitem__", "line 1239: self.__setitem__( key, other[key] )")([key], __NULL_OBJECT__))], __NULL_OBJECT__); + __get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1240: self.__setitem__( key, other[key] )"), "__call__")([key, ((other instanceof Array) ? other[key] : __get__(other, "__getitem__", "line 1240: self.__setitem__( key, other[key] )")([key], __NULL_OBJECT__))], __NULL_OBJECT__); } };__dict_update.is_wrapper = true; __dict_attrs.update = __dict_update; @@ -2881,12 +2875,12 @@ var __dict_items = function(args, kwargs) { var self = __args__['self']; arr = []; var key,__iterator__49; - __iterator__49 = __get__(__get__(__jsdict_keys(self), "__iter__", "no iterator - line 1242: for key in self.keys():"), "__call__")([], __NULL_OBJECT__); + __iterator__49 = __get__(__get__(__jsdict_keys(self), "__iter__", "no iterator - line 1243: for key in self.keys():"), "__call__")([], __NULL_OBJECT__); var __next__49; __next__49 = __get__(__iterator__49, "next"); while (( __iterator__49.index ) < __iterator__49.length) { key = __next__49(); - __get__(__get__(arr, "append", "missing attribute `append` - line 1243: arr.append( [key, self[key]] )"), "__call__")([[key, __get__(self, "__getitem__")([key], __NULL_OBJECT__)]], __NULL_OBJECT__); + __get__(__get__(arr, "append", "missing attribute `append` - line 1244: arr.append( [key, self[key]] )"), "__call__")([[key, __get__(self, "__getitem__")([key], __NULL_OBJECT__)]], __NULL_OBJECT__); } return arr; };__dict_items.is_wrapper = true; @@ -2927,7 +2921,7 @@ var __dict_set = function(args, kwargs) { var self = __args__['self']; var key = __args__['key']; var value = __args__['value']; - __get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1251: self.__setitem__(key, value)"), "__call__")([key, value], __NULL_OBJECT__); + __get__(__get__(self, "__setitem__", "missing attribute `__setitem__` - line 1252: self.__setitem__(key, value)"), "__call__")([key, value], __NULL_OBJECT__); };__dict_set.is_wrapper = true; __dict_attrs.set = __dict_set; var __dict___len__ = function(args, kwargs) { @@ -3135,7 +3129,7 @@ var set = function(args, kwargs) { } __args__ = __getargs__("set", __sig__, args, kwargs); var a = __args__['a']; - "\n This returns an array that is a minimal implementation of set.\n Often sets are used simply to remove duplicate entries from a list, \n and then it get converted back to a list, it is safe to use fastset for this.\n The array prototype is overloaded with basic set functions:\n difference\n intersection\n issubset\n Note: sets in Python are not subscriptable, but can be iterated over.\n Python docs say that set are unordered, some programs may rely on this disorder\n for randomness, for sets of integers we emulate the unorder only uppon initalization \n of the set, by masking the value by bits-1. Python implements sets starting with an \n array of length 8, and mask of 7, if set length grows to 6 (3/4th), then it allocates \n a new(array of length 32 and mask of 31. This is only emulated for arrays of )\n integers up to an array length of 1536.\n "; + "\n This returns an array that is a minimal implementation of set.\n Often sets are used simply to remove duplicate entries from a list, \n and then it get converted back to a list, it is safe to use fastset for this.\n The array prototype is overloaded with basic set functions:\n difference\n intersection\n issubset\n Note: sets in Python are not subscriptable, but can be iterated over.\n Python docs say that set are unordered, some programs may rely on this disorder\n for randomness, for sets of integers we emulate the unorder only uppon initalization \n of the set, by masking the value by bits-1. Python implements sets starting with an \n array of length 8, and mask of 7, if set length grows to 6 (3/4th), then it allocates \n a __new__>>array of length 32 and mask of 31. This is only emulated for arrays of \n integers up to an array length of 1536.\n "; hashtable = null; if (( a.length ) <= 1536) { hashtable = __jsdict([]); @@ -3235,7 +3229,7 @@ var __array___init__ = function(args, kwargs) { var initializer = __args__['initializer']; var little_endian = __args__['little_endian']; self.typecode = typecode; - self.itemsize = __get__(__get__(self, "typecodes", "missing attribute `typecodes` - line 1435: self.itemsize = self.typecodes[ typecode ]"), "__getitem__", "line 1435: self.itemsize = self.typecodes[ typecode ]")([typecode], __NULL_OBJECT__); + self.itemsize = __get__(__get__(self, "typecodes", "missing attribute `typecodes` - line 1436: self.itemsize = self.typecodes[ typecode ]"), "__getitem__", "line 1436: self.itemsize = self.typecodes[ typecode ]")([typecode], __NULL_OBJECT__); self.little_endian = little_endian; if (__test_if_true__(initializer)) { self.length = len([initializer], __NULL_OBJECT__); @@ -3259,7 +3253,7 @@ var __array___init__ = function(args, kwargs) { buff = new ArrayBuffer(size); self.dataview = new DataView(buff); self.buffer = buff; - __get__(__get__(self, "fromlist", "missing attribute `fromlist` - line 1456: self.fromlist( initializer )"), "__call__")([initializer], __NULL_OBJECT__); + __get__(__get__(self, "fromlist", "missing attribute `fromlist` - line 1457: self.fromlist( initializer )"), "__call__")([initializer], __NULL_OBJECT__); };__array___init__.is_wrapper = true; __array_attrs.__init__ = __array___init__; var __array___len__ = function(args, kwargs) { @@ -3290,7 +3284,7 @@ var __array___contains__ = function(args, kwargs) { __args__ = __getargs__("__array___contains__", __sig__, args, kwargs); var self = __args__['self']; var value = __args__['value']; - arr = __get__(__get__(self, "to_array", "missing attribute `to_array` - line 1462: arr = self.to_array()"), "__call__")(); + arr = __get__(__get__(self, "to_array", "missing attribute `to_array` - line 1463: arr = self.to_array()"), "__call__")(); if ((arr.indexOf(value) instanceof Array ? JSON.stringify(arr.indexOf(value))==JSON.stringify(-1) : arr.indexOf(value)===-1)) { return false; } else { @@ -3314,7 +3308,7 @@ var __array___getitem__ = function(args, kwargs) { step = self.itemsize; offset = (step * index); dataview = self.dataview; - func_name = ("get" + __get__(__get__(self, "typecode_names", "missing attribute `typecode_names` - line 1470: func_name = 'get'+self.typecode_names[ self.typecode ]"), "__getitem__", "line 1470: func_name = 'get'+self.typecode_names[ self.typecode ]")([self.typecode], __NULL_OBJECT__)); + func_name = ("get" + __get__(__get__(self, "typecode_names", "missing attribute `typecode_names` - line 1471: func_name = 'get'+self.typecode_names[ self.typecode ]"), "__getitem__", "line 1471: func_name = 'get'+self.typecode_names[ self.typecode ]")([self.typecode], __NULL_OBJECT__)); func = dataview[func_name].bind(dataview); if (( offset ) < self.bytes) { value = func(offset); @@ -3351,7 +3345,7 @@ var __array___setitem__ = function(args, kwargs) { } offset = (step * index); dataview = self.dataview; - func_name = ("set" + __get__(__get__(self, "typecode_names", "missing attribute `typecode_names` - line 1486: func_name = 'set'+self.typecode_names[ self.typecode ]"), "__getitem__", "line 1486: func_name = 'set'+self.typecode_names[ self.typecode ]")([self.typecode], __NULL_OBJECT__)); + func_name = ("set" + __get__(__get__(self, "typecode_names", "missing attribute `typecode_names` - line 1487: func_name = 'set'+self.typecode_names[ self.typecode ]"), "__getitem__", "line 1487: func_name = 'set'+self.typecode_names[ self.typecode ]")([self.typecode], __NULL_OBJECT__)); func = dataview[func_name].bind(dataview); if (( offset ) < self.bytes) { if ((self.typecode instanceof Array ? JSON.stringify(self.typecode)==JSON.stringify("float8") : self.typecode==="float8")) { @@ -3416,13 +3410,13 @@ var __array_fromlist = function(args, kwargs) { typecode = self.typecode; size = (length * step); dataview = self.dataview; - func_name = ("set" + __get__(__get__(self, "typecode_names", "missing attribute `typecode_names` - line 1506: func_name = 'set'+self.typecode_names[ typecode ]"), "__getitem__", "line 1506: func_name = 'set'+self.typecode_names[ typecode ]")([typecode], __NULL_OBJECT__)); + func_name = ("set" + __get__(__get__(self, "typecode_names", "missing attribute `typecode_names` - line 1507: func_name = 'set'+self.typecode_names[ typecode ]"), "__getitem__", "line 1507: func_name = 'set'+self.typecode_names[ typecode ]")([typecode], __NULL_OBJECT__)); func = dataview[func_name].bind(dataview); if (( size ) <= self.bytes) { i = 0; offset = 0; while (( i ) < length) { - item = ((lst instanceof Array) ? lst[i] : __get__(lst, "__getitem__", "line 1511: item = lst[i]")([i], __NULL_OBJECT__)); + item = ((lst instanceof Array) ? lst[i] : __get__(lst, "__getitem__", "line 1512: item = lst[i]")([i], __NULL_OBJECT__)); if ((typecode instanceof Array ? JSON.stringify(typecode)==JSON.stringify("float8") : typecode==="float8")) { item *= self._norm_set; } else { @@ -3478,7 +3472,7 @@ var __array_append = function(args, kwargs) { var self = __args__['self']; var value = __args__['value']; length = self.length; - __get__(__get__(self, "resize", "missing attribute `resize` - line 1534: self.resize( self.length + 1 )"), "__call__")([(self.length + 1)], __NULL_OBJECT__); + __get__(__get__(self, "resize", "missing attribute `resize` - line 1535: self.resize( self.length + 1 )"), "__call__")([(self.length + 1)], __NULL_OBJECT__); __get__(__get__(self, "__setitem__"), "__call__")([length, value], {}); };__array_append.is_wrapper = true; __array_attrs.append = __array_append; @@ -3496,12 +3490,12 @@ var __array_extend = function(args, kwargs) { var self = __args__['self']; var lst = __args__['lst']; var value,__iterator__54; - __iterator__54 = __get__(__get__(lst, "__iter__", "no iterator - line 1537: for value in lst:"), "__call__")([], __NULL_OBJECT__); + __iterator__54 = __get__(__get__(lst, "__iter__", "no iterator - line 1538: for value in lst:"), "__call__")([], __NULL_OBJECT__); var __next__54; __next__54 = __get__(__iterator__54, "next"); while (( __iterator__54.index ) < __iterator__54.length) { value = __next__54(); - __get__(__get__(self, "append", "missing attribute `append` - line 1538: self.append( value )"), "__call__")([value], __NULL_OBJECT__); + __get__(__get__(self, "append", "missing attribute `append` - line 1539: self.append( value )"), "__call__")([value], __NULL_OBJECT__); } };__array_extend.is_wrapper = true; __array_attrs.extend = __array_extend; @@ -3539,7 +3533,7 @@ var __array_to_list = function(args, kwargs) { } __args__ = __getargs__("__array_to_list", __sig__, args, kwargs); var self = __args__['self']; - return __get__(__get__(self, "to_array", "missing attribute `to_array` - line 1548: return self.to_array()"), "__call__")(); + return __get__(__get__(self, "to_array", "missing attribute `to_array` - line 1549: return self.to_array()"), "__call__")(); };__array_to_list.is_wrapper = true; __array_attrs.to_list = __array_to_list; var __array_to_ascii = function(args, kwargs) { @@ -3555,9 +3549,9 @@ var __array_to_ascii = function(args, kwargs) { __args__ = __getargs__("__array_to_ascii", __sig__, args, kwargs); var self = __args__['self']; string = ""; - arr = __get__(__get__(self, "to_array", "missing attribute `to_array` - line 1551: arr = self.to_array()"), "__call__")(); + arr = __get__(__get__(self, "to_array", "missing attribute `to_array` - line 1552: arr = self.to_array()"), "__call__")(); i = 0; - length = __get__(arr, "length", "missing attribute `length` - line 1552: i = 0; length = arr.length"); + length = __get__(arr, "length", "missing attribute `length` - line 1553: i = 0; length = arr.length"); while (( i ) < length) { var num = arr[i]; var char = String.fromCharCode(num); @@ -3618,14 +3612,14 @@ var __file_read = function(args, kwargs) { _fs = __get__(require, "__call__")(["fs"], __NULL_OBJECT__); path = self.path; if (__test_if_true__((binary || self.binary))) { - return _fs.readFileSync(path); + return _fs.readFileSync(path, { encoding:null }); } else { return _fs.readFileSync(path, __jsdict([["encoding", "utf8"]])); } };__file_read.is_wrapper = true; __file_attrs.read = __file_read; var __file_write = function(args, kwargs) { - var _fs,path; + var path,buff,_fs; var __sig__,__args__; __sig__ = { kwargs:{"binary": false},args:["self", "data", "binary"] }; if ((args instanceof Array && (Object.prototype.toString.call(kwargs) instanceof Array ? JSON.stringify(Object.prototype.toString.call(kwargs))==JSON.stringify("[object Object]") : Object.prototype.toString.call(kwargs)==="[object Object]") && (arguments.length instanceof Array ? JSON.stringify(arguments.length)==JSON.stringify(2) : arguments.length===2))) { @@ -3641,7 +3635,13 @@ var __file_write = function(args, kwargs) { _fs = __get__(require, "__call__")(["fs"], __NULL_OBJECT__); path = self.path; if (__test_if_true__((binary || self.binary))) { - _fs.writeFileSync(path, data); + binary = (binary || self.binary); + if ((binary instanceof Array ? JSON.stringify(binary)==JSON.stringify("base64") : binary==="base64")) { + buff = new Buffer(data, "base64"); + _fs.writeFileSync(path, buff, __jsdict([["encoding", null]])); + } else { + _fs.writeFileSync(path, data, __jsdict([["encoding", null]])); + } } else { _fs.writeFileSync(path, data, __jsdict([["encoding", "utf8"]])); } diff --git a/pythonjs/runtime/builtins.py b/pythonjs/runtime/builtins.py index d493cdd..7047472 100644 --- a/pythonjs/runtime/builtins.py +++ b/pythonjs/runtime/builtins.py @@ -1030,10 +1030,11 @@ def func(): @Array.prototype.__getslice__ def func(start, stop, step): - arr = [] #new(Array(this.length)) + arr = [] start = start | 0 - stop = stop | this.length + if stop is undefined: + stop = this.length if start < 0: start = this.length + start @@ -1878,7 +1879,7 @@ def read(self, binary=False): path = self.path with javascript: if binary or self.binary: - return _fs.readFileSync( path ) + return _fs.readFileSync( path, encoding=None ) else: return _fs.readFileSync( path, {'encoding':'utf8'} ) @@ -1887,8 +1888,18 @@ def write(self, data, binary=False): path = self.path with javascript: if binary or self.binary: - _fs.writeFileSync( path, data ) + binary = binary or self.binary + if binary == 'base64': ## TODO: fixme, something bad in this if test + #print('write base64 data') + buff = new Buffer(data, 'base64') + _fs.writeFileSync( path, buff, {'encoding':None}) + + else: + #print('write binary data') + #print(binary) + _fs.writeFileSync( path, data, {'encoding':None}) else: + #print('write utf8 data') _fs.writeFileSync( path, data, {'encoding':'utf8'} ) def close(self): From 027a3a878269499f5f6bc21a20a66ced497a14ae Mon Sep 17 00:00:00 2001 From: hartsantler Date: Sun, 7 Sep 2014 02:50:28 -0700 Subject: [PATCH 10/17] Go backend: subclasses, methods can over-ride the parents, but not extend. --- pythonjs/pythonjs_to_go.py | 42 +++++++++++++++++++++++++++++++++-- regtests/class/mi.py | 8 +++---- regtests/class/mi_override.py | 32 ++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 regtests/class/mi_override.py diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index 73e4864..c418d84 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -32,6 +32,27 @@ def visit_ClassDef(self, node): self._classes[ node.name ] = node self._class_props[ node.name ] = props + + + for base in node.bases: + n = self.visit(base) + if n == 'object': + continue + node._parents.add( n ) + + bases.add( n ) + if n in self._class_props: + props.update( self._class_props[n] ) + base_classes.add( self._classes[n] ) + #else: ## special case - subclassing a builtin like `list` + # continue + + for p in self._classes[ n ]._parents: + bases.add( p ) + props.update( self._class_props[p] ) + base_classes.add( self._classes[p] ) + + for decor in node.decorator_list: ## class decorators if isinstance(decor, ast.Call): assert decor.func.id=='__struct__' @@ -47,10 +68,11 @@ def visit_ClassDef(self, node): init = None - + method_names = set() for b in node.body: - out.append( self.visit(b) ) assert isinstance(b, ast.FunctionDef) + method_names.add( b.name ) + out.append( self.visit(b) ) if b.name == '__init__': init = b @@ -66,6 +88,21 @@ def visit_ClassDef(self, node): out.append( 'func __new__%s() *%s { return &%s{} }' %(node.name, node.name, node.name)) + if base_classes: + for bnode in base_classes: + for b in bnode.body: + if isinstance(b, ast.FunctionDef): + if b.name == '__init__': continue + if b.name in method_names: continue + out.append( self.visit(b) ) + #args = [self.visit(a) for a in b.args.args][1:] + #args = ','.join(args) + #if args: + # out.append(self.indent()+ '%s(%s) { return %s.__%s(this,%s); }'%(b.name, args, bnode.name, b.name, args) ) + #else: + # out.append(self.indent()+ '%s() { return %s.__%s(this); }'%(b.name, bnode.name, b.name) ) + + self._class_stack.pop() return '\n'.join(out) @@ -443,6 +480,7 @@ def visit_While(self, node): def _inline_code_helper(self, s): return s + #return 'js.Global.Call("eval", "%s")' %s ## TODO inline JS() diff --git a/regtests/class/mi.py b/regtests/class/mi.py index 0b96290..5c1a591 100644 --- a/regtests/class/mi.py +++ b/regtests/class/mi.py @@ -2,21 +2,21 @@ multiple inheritance ''' class A: - def foo(self): + def foo(self) -> int: return 1 class B: - def bar(self): + def bar(self) -> int: return 2 class C( A, B ): - def call_foo_bar(self): + def call_foo_bar(self) -> int: a = self.foo() a += self.bar() return a ## extend foo ## - def foo(self): + def foo(self) -> int: a = A.foo(self) a += 100 return a diff --git a/regtests/class/mi_override.py b/regtests/class/mi_override.py new file mode 100644 index 0000000..d36fb6f --- /dev/null +++ b/regtests/class/mi_override.py @@ -0,0 +1,32 @@ +''' +multiple inheritance +''' +class A: + def foo(self) -> int: + return 1 + +class B: + def bar(self) -> int: + return 2 + +class C( A, B ): + def call_foo_bar(self) -> int: + a = self.foo() + a += self.bar() + return a + + ## override foo ## + def foo(self) -> int: + return 100 + +def main(): + a = A() + TestError( a.foo()==1 ) + b = B() + TestError( b.bar()==2 ) + + c = C() + TestError( c.foo()==100 ) + TestError( c.bar()==2 ) + + TestError( c.call_foo_bar()==102 ) From 4f39bd5b2e55b5170c0e0702dba75db601fd7f1e Mon Sep 17 00:00:00 2001 From: hartsantler Date: Sun, 7 Sep 2014 11:10:15 -0700 Subject: [PATCH 11/17] documentation for the Go backend --- README.md | 11 +++- doc/go_syntax.md | 157 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 doc/go_syntax.md diff --git a/README.md b/README.md index aaed747..db3e43f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,18 @@ Introduction ------------ PythonJS is a transpiler written in Python that converts a python like language into fast -JavaScript. With a few exceptions, the PythonJS language is a superset of Python that is extended to support new syntax inspired by JavaScript and Go. +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) +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. + +[Syntax Documentation](https://github.com/PythonJS/PythonJS/blob/master/doc/go_syntax.md) + + Getting Started =============== PythonJS can be run with regular Python, or fully self-hosted within @@ -59,7 +66,7 @@ will be converted into JavaScript. Usage:: - translator.py [--help|--dart|--coffee|--lua|--no-wrapper|--analyzer] file.py + translator.py [--help|--go|--dart|--coffee|--lua|--no-wrapper|--analyzer] file.py Examples:: diff --git a/doc/go_syntax.md b/doc/go_syntax.md new file mode 100644 index 0000000..698fbc3 --- /dev/null +++ b/doc/go_syntax.md @@ -0,0 +1,157 @@ +PythonJS Go Syntax +=============== + +PythonJS supports a fully typed subset of Python with extra syntax to support the Golang backend. + + +select +------- +Below `A` and `B` are typed as `chan int`. Data is read from a channel with `<-`. +``` + def select_loop(A:chan int, B:chan int): + print('starting select loop') + y = 0 + while True: + select: + case x = <- A: + y += x + case x = <- B: + y += x + +``` + +maps +------- +Go maps store key value pairs. The key type is given first enclosed in brackets, the value type is given after. +The example below shows a map with string keys and integer values + +``` + a = map[string]int{ + 'x': 1, + 'y': 2, + 'z': 3, + } +``` + +map iteration +------------- +The key value pairs can be looped over with a for loop. +``` + def main(): + a = map[string]int{'x':100, 'y':200} + b = '' + c = 0 + for key,value in a: + b += key + c += value +``` + +arrays +------ +Go typed arrays are defined with an optional size followed by the type, and values passed as arguments to the constructor. +Items in an array can be iterated over with a normal `for x in a` loop. Arrays also support index value pair loops using `enumerate` + +``` + a = []int(1,2,3) + b = [2]int(100,200) + +``` + +classes +------- +A Python class is translated into a Go struct with methods. Below a dict is used to type all the attribute variables that `self` will use. +``` + class A: + { + x:int, + y:int, + z:int, + } + def __init__(self, x:int, y:int, z:int=1): + self.x = x + self.y = y + self.z = z + + +``` + +subclasses +---------- +Subclasses can mix multiple classes, and override methods from the parent class. + +``` + class A: + def foo(self) -> int: + return 1 + + class B: + def bar(self) -> int: + return 2 + + class C( A, B ): + def call_foo_bar(self) -> int: + a = self.foo() + a += self.bar() + return a + + ## override foo ## + def foo(self) -> int: + return 100 + + def main(): + a = A() + b = B() + + c = C() + + ## below is all true ## + b.bar()==2 + c.bar()==2 + c.foo()==100 + c.call_foo_bar()==102 + +``` + +callbacks +--------- +Functions and methods can be passed as callbacks to other functions. The function argument type must contain the keyword `func` followed by the type signature of the callback (argument types) and (return type). Below the method is typed as `func(int)(int)` + +``` +class A: + { + x:int, + y:int, + z:int, + } + def __init__(self, x:int, y:int, z:int=1): + self.x = x + self.y = y + self.z = z + + def mymethod(self, m:int) -> int: + return self.x * m + + def call_method( cb:func(int)(int), mx:int ) ->int: + return cb(mx) + + def main(): + a = A( 100, 200, z=9999 ) + c = call_method( a.mymethod, 4 ) + print( c ) + +``` + +goroutines +---------- +The function `go` can be called to spawn a new function call as a goroutine +``` + go( myfunc(x,y,z) ) + +``` + +channels +-------- +To make a new Go channel call `go.channel(type)`, this is the same as in Go calling `make(chan type)`. +``` + c = go.channel(int) +``` From 1bc0b9dea97e557b731559ef475ac49b21024cf5 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Sun, 7 Sep 2014 11:34:15 -0700 Subject: [PATCH 12/17] Go backend: fixed subclasses without __init__. --- pythonjs/pythonjs_to_go.py | 42 +++++++++++++++++++++++-------------- regtests/go/subclass.py | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 regtests/go/subclass.py diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index c418d84..8eb00b7 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -24,6 +24,7 @@ def __init__(self, requirejs=False, insert_runtime=False): def visit_ClassDef(self, node): self._class_stack.append( node ) node._parents = set() + node._struct_def = dict() out = [] sdef = dict() props = set() @@ -61,7 +62,13 @@ def visit_ClassDef(self, node): props.add( kw.arg ) sdef[ kw.arg ] = kw.value.id + node._struct_def.update( sdef ) out.append( 'type %s struct {' %node.name) + if base_classes: + for bnode in base_classes: + ## Go only needs the name of the parent struct and all its items are inserted automatically ## + out.append('%s' %bnode.name) + for name in sdef: out.append('%s %s' %(name, sdef[name])) out.append('}') @@ -76,7 +83,25 @@ def visit_ClassDef(self, node): if b.name == '__init__': init = b - if init: + + parent_init = None + if base_classes: + for bnode in base_classes: + for b in bnode.body: + if isinstance(b, ast.FunctionDef): + if b.name in method_names: + continue + if b.name == '__init__': + parent_init = {'class':bnode, 'init':b} + #continue + out.append( self.visit(b) ) + + if init or parent_init: + if parent_init: + classname = parent_init['class'].name + init = parent_init['init'] + else: + classname = node.name out.append( 'func __new__%s( %s ) *%s {' %(node.name, init._args_signature, node.name)) out.append( ' ob := %s{}' %node.name ) @@ -88,21 +113,6 @@ def visit_ClassDef(self, node): out.append( 'func __new__%s() *%s { return &%s{} }' %(node.name, node.name, node.name)) - if base_classes: - for bnode in base_classes: - for b in bnode.body: - if isinstance(b, ast.FunctionDef): - if b.name == '__init__': continue - if b.name in method_names: continue - out.append( self.visit(b) ) - #args = [self.visit(a) for a in b.args.args][1:] - #args = ','.join(args) - #if args: - # out.append(self.indent()+ '%s(%s) { return %s.__%s(this,%s); }'%(b.name, args, bnode.name, b.name, args) ) - #else: - # out.append(self.indent()+ '%s() { return %s.__%s(this); }'%(b.name, bnode.name, b.name) ) - - self._class_stack.pop() return '\n'.join(out) diff --git a/regtests/go/subclass.py b/regtests/go/subclass.py new file mode 100644 index 0000000..69e87b9 --- /dev/null +++ b/regtests/go/subclass.py @@ -0,0 +1,43 @@ +''' +simple class +''' +class A: + { + x:int, + y:int, + z:int, + } + def __init__(self, x:int, y:int, z:int=1): + self.x = x + self.y = y + self.z = z + + def mymethod(self, m:int) -> int: + return self.x * m + +class B(A): + { + w:string + } + + def method2(self, v:string) ->string: + self.w = v + return self.w + +def call_method( cb:func(int)(int), mx:int ) ->int: + return cb(mx) + +def main(): + a = A( 100, 200, z=9999 ) + print( a.x ) + print( a.y ) + print( a.z ) + + b = a.mymethod(3) + print( b ) + + c = call_method( a.mymethod, 4 ) + print( c ) + + x = B(1,2,z=3) + print( x.method2('hello world') ) \ No newline at end of file From 09c5f685e33a142ed831eedb998436d2fb0c6af9 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Sun, 7 Sep 2014 15:23:38 -0700 Subject: [PATCH 13/17] Go backend: list comprehensions `[]int( x for x in range(n) )` --- doc/go_syntax.md | 7 +++++++ pythonjs/python_to_pythonjs.py | 27 +++++++++++++++++++++++++-- pythonjs/pythonjs_to_go.py | 4 +++- pythonjs/typedpython.py | 3 +++ regtests/go/list_comprehension.py | 11 +++++++++++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 regtests/go/list_comprehension.py diff --git a/doc/go_syntax.md b/doc/go_syntax.md index 698fbc3..598778a 100644 --- a/doc/go_syntax.md +++ b/doc/go_syntax.md @@ -155,3 +155,10 @@ To make a new Go channel call `go.channel(type)`, this is the same as in Go call ``` c = go.channel(int) ``` + +list comprehensions +------------------- + +``` + a = []int(x for x in range(3)) +``` \ No newline at end of file diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index 802ec1a..11f5c4d 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -604,7 +604,11 @@ def visit_ListComp(self, node): a = ['get%s'%i for i in range(length)] writer.write('var( %s )' %','.join(a) ) - writer.write('%s = JSArray()'%cname) + if self._with_go: + assert node.go_listcomp_type + writer.write('%s = __go__array__(%s)' %(cname, node.go_listcomp_type)) + else: + writer.write('%s = JSArray()'%cname) generators = list( node.generators ) generators.reverse() @@ -661,6 +665,8 @@ def _gen_comp(self, generators, node): writer.write('%s.add( %s )' %(cname,self.visit(node.elt)) ) elif self._with_lua: writer.write('table.insert(%s, %s )' %(cname,self.visit(node.elt)) ) + elif self._with_go: + writer.write('%s = append(%s, %s )' %(cname, cname,self.visit(node.elt)) ) else: writer.write('%s.push( %s )' %(cname,self.visit(node.elt)) ) writer.pull() @@ -670,6 +676,8 @@ def _gen_comp(self, generators, node): writer.write('%s.add( %s )' %(cname,self.visit(node.elt)) ) elif self._with_lua: writer.write('table.insert(%s, %s )' %(cname,self.visit(node.elt)) ) + elif self._with_go: + writer.write('%s = append(%s, %s )' %(cname, cname,self.visit(node.elt)) ) else: writer.write('%s.push( %s )' %(cname,self.visit(node.elt)) ) if self._with_lua: @@ -1365,10 +1373,25 @@ def visit_Return(self, node): def visit_BinOp(self, node): left = self.visit(node.left) op = self.visit(node.op) + + is_go_listcomp = False + if self._with_go: + if op == '<<': + if isinstance(node.left, ast.Call) and isinstance(node.left.func, ast.Name) and node.left.func.id=='__go__array__': + if isinstance(node.right, ast.GeneratorExp): + is_go_listcomp = True + node.right.go_listcomp_type = node.left.args[0].id + + right = self.visit(node.right) - if self._with_glsl or self._with_go: + if self._with_glsl: return '(%s %s %s)' % (left, op, right) + elif self._with_go: + if is_go_listcomp: + return right + else: + return '(%s %s %s)' % (left, op, right) elif op == '|': if isinstance(node.right, Str): diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index 8eb00b7..2c02e63 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -273,8 +273,10 @@ def _visit_call_helper_go(self, node): return 'make(%s)' %self.visit(node.args[0]) elif name == '__go_make_chan__': return 'make(chan %s)' %self.visit(node.args[0]) + elif name == '__go__array__': + return '[]%s{}' %self.visit(node.args[0]) else: - raise SyntaxError('invalid special go call') + raise SyntaxError(name) def _visit_function(self, node): if self._function_stack[0] is node: diff --git a/pythonjs/typedpython.py b/pythonjs/typedpython.py index 7f31496..ecc1b86 100644 --- a/pythonjs/typedpython.py +++ b/pythonjs/typedpython.py @@ -24,6 +24,7 @@ GO_SPECIAL_CALLS = { 'go' : '__go__', 'go.channel' : '__go_make_chan__', + 'go.array' : '__go__array__' } def transform_source( source, strip=False ): @@ -427,6 +428,8 @@ def f(a:int=100, b:int=100) ->int: def f(*args:int, **kwargs:int) ->int: return a+b +a = []int(x for x in range(3)) + ''' if __name__ == '__main__': diff --git a/regtests/go/list_comprehension.py b/regtests/go/list_comprehension.py new file mode 100644 index 0000000..75c7f7a --- /dev/null +++ b/regtests/go/list_comprehension.py @@ -0,0 +1,11 @@ +''' +go list comprehensions +''' + +def main(): + a = []int(x for x in range(3)) + + TestError( len(a)==3 ) + TestError( a[0]==0 ) + TestError( a[1]==1 ) + TestError( a[2]==2 ) From 3b12597f32b13d0875858a497ec1edcb8141b955 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Mon, 8 Sep 2014 19:17:53 -0700 Subject: [PATCH 14/17] fixed `yield` generator functions. --- pythonjs/python_to_pythonjs.py | 35 ++++++---------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index 11f5c4d..a507fc1 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -3874,38 +3874,15 @@ class GeneratorFunctionTransformer( PythonToPythonJS ): ''' def __init__(self, node, compiler=None): - self._with_ll = False - self._with_js = False - self._with_dart = False - self._with_coffee = False - self._with_lua = False - self._with_rpc = None - self._with_rpc_name = None - self._with_inline = False - self._in_while_test = False - self._in_lambda = False + assert '_stack' in dir(compiler) + #self.__dict___ = compiler.__dict__ ## share all state + for name in dir(compiler): + if name not in dir(self): + setattr(self, name, (getattr(compiler, name))) - if compiler._with_dart: ## TODO - self._with_dart = True - elif compiler._with_coffee: - self._with_coffee = True - elif compiler._with_lua: - self._with_lua = True - else: - self._with_js = True - - self._typedef_vars = compiler._typedef_vars - self._direct_operators = compiler._direct_operators - self._builtin_functions = compiler._builtin_functions - self._js_classes = compiler._js_classes - self._global_functions = compiler._global_functions - self._addop_ids = compiler._addop_ids - self._cache_for_body_calls = False - self._source = compiler._source - self._instances = dict() self._head_yield = False self.visit( node ) - compiler._addop_ids = self._addop_ids + compiler._addop_ids = self._addop_ids ## note: need to keep id index insync def visit_Yield(self, node): if self._in_head: From d9f143f8d2d37ba05a83dba57cfd7f8720367ad4 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Wed, 10 Sep 2014 17:42:14 -0700 Subject: [PATCH 15/17] go backend: multidimensional array `[][]` --- pythonjs/python_to_pythonjs.py | 4 +++- pythonjs/pythonjs_to_go.py | 4 ++-- pythonjs/typedpython.py | 24 ++++++++++++++++++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index a507fc1..6d89ae4 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -424,7 +424,9 @@ def visit_Import(self, node): tornado = ['tornado', 'tornado.web', 'tornado.ioloop'] for alias in node.names: - if alias.name in tornado: + if self._with_go: + writer.write('import %s' %alias.name) + elif alias.name in tornado: pass ## pythonjs/fakelibs/tornado.py elif alias.name == 'tempfile': pass ## pythonjs/fakelibs/tempfile.py diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index 2c02e63..56c5fcf 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -150,9 +150,9 @@ def visit_Expr(self, node): return self.visit(node.value) def visit_Import(self, node): - r = [alias.name for alias in node.names] + r = [alias.name.replace('__SLASH__', '/') for alias in node.names] if r: - return 'import "%s"' %';'.join(r) + return 'import("%s")' %';'.join(r) else: return '' diff --git a/pythonjs/typedpython.py b/pythonjs/typedpython.py index ecc1b86..9c06d9b 100644 --- a/pythonjs/typedpython.py +++ b/pythonjs/typedpython.py @@ -44,7 +44,7 @@ def transform_source( source, strip=False ): if nextchar.strip(): break j += 1 - if a and char==']' and j==i+1 and nextchar!=None and nextchar in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': + if a and char==']' and j==i+1 and nextchar!=None and nextchar in '[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': assert '[' in a gotype = [] b = a.pop() @@ -54,7 +54,10 @@ def transform_source( source, strip=False ): gotype.reverse() gotype = ''.join(gotype) if not gotype: - a.append('__go__array__(') + if nextchar=='[': + a.append('__go__array__<<') + else: + a.append('__go__array__(') elif gotype.isdigit(): a.append('__go__arrayfixed__(%s,' %gotype) else: @@ -69,6 +72,10 @@ def transform_source( source, strip=False ): elif hit_go_typedef and char=='{': a.append(')<<{') hit_go_typedef = False + elif hit_go_typedef and char==',': + a.append('),') + hit_go_typedef = False + elif a and char in __whitespace: b = ''.join(a) @@ -153,8 +160,15 @@ def transform_source( source, strip=False ): break indent = ''.join(indent) output.append( indent + '@returns(%s)' %rtype) - elif c.startswith('import ') and '-' in c: - c = c.replace('-', '__DASH__') + + if c.startswith('import '): + if '-' in c: + c = c.replace('-', '__DASH__') + if '/' in c: + c = c.replace('/', '__SLASH__') + if '"' in c: + c = c.replace('"', '') + if ' new ' in c: c = c.replace(' new ', ' __new__>>') @@ -430,6 +444,8 @@ def f(*args:int, **kwargs:int) ->int: a = []int(x for x in range(3)) +y = go.make([]float64, 1000) + ''' if __name__ == '__main__': From 2dd323501d3751a7c7ae632d5d9e60782788f447 Mon Sep 17 00:00:00 2001 From: hartsantler Date: Thu, 11 Sep 2014 02:56:27 -0700 Subject: [PATCH 16/17] go backend: support go.make --- pythonjs/python_to_pythonjs.py | 4 ++-- pythonjs/pythonjs.py | 3 +++ pythonjs/pythonjs_to_go.py | 26 ++++++++++++++++++-------- pythonjs/typedpython.py | 14 ++++++++------ 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index 6d89ae4..4bf5339 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -299,10 +299,10 @@ def __init__(self, source=None, module=None, module_path=None, dart=False, coffe self._source = source.splitlines() - if '--debug' in sys.argv: + if '--debug--' in sys.argv: try: tree = ast.parse( source ) - except: + except SyntaxError: raise SyntaxError(source) else: tree = ast.parse( source ) diff --git a/pythonjs/pythonjs.py b/pythonjs/pythonjs.py index 8eb22bd..159f25c 100755 --- a/pythonjs/pythonjs.py +++ b/pythonjs/pythonjs.py @@ -962,6 +962,7 @@ def visit_BinOp(self, node): if node.left.func.id == '__go__map__': key_type = self.visit(node.left.args[0]) value_type = self.visit(node.left.args[1]) + if value_type == 'interface': value_type = 'interface{}' return 'map[%s]%s%s' %(key_type, value_type, right) else: if not right.startswith('{') and not right.endswith('}'): @@ -973,6 +974,8 @@ def visit_BinOp(self, node): asize = self.visit(node.left.args[0]) atype = self.visit(node.left.args[1]) return '[%s]%s%s' %(asize, atype, right) + elif isinstance(node.left, ast.Name) and node.left.id=='__go__array__' and op == '<<': + return '[]%s' %self.visit(node.right) if left in self._typed_vars and self._typed_vars[left] == 'numpy.float32': left += '[_id_]' diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index 56c5fcf..8599819 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -21,6 +21,8 @@ def __init__(self, requirejs=False, insert_runtime=False): self._known_vars = set() self._kwargs_type_ = dict() + self._imports = [] + def visit_ClassDef(self, node): self._class_stack.append( node ) node._parents = set() @@ -152,9 +154,9 @@ def visit_Expr(self, node): def visit_Import(self, node): r = [alias.name.replace('__SLASH__', '/') for alias in node.names] if r: - return 'import("%s")' %';'.join(r) - else: - return '' + for name in r: + self._imports.append('import("%s");' %name) + return '' def visit_Module(self, node): header = [ @@ -173,7 +175,10 @@ def visit_Module(self, node): else: lines.append( sub ) else: - raise SyntaxError(b) + if isinstance(b, ast.Import): + pass + else: + raise SyntaxError(b) lines.append('type _kwargs_type_ struct {') for name in self._kwargs_type_: @@ -182,7 +187,7 @@ def visit_Module(self, node): lines.append( ' __use__%s bool' %name) lines.append('}') - lines = header + lines + lines = header + self._imports + lines return '\n'.join( lines ) @@ -269,12 +274,17 @@ def _visit_call_helper_go(self, node): name = self.visit(node.func) if name == '__go__': return 'go %s' %self.visit(node.args[0]) - elif name == '__gomake__': + elif name == '__go_make__': return 'make(%s)' %self.visit(node.args[0]) elif name == '__go_make_chan__': return 'make(chan %s)' %self.visit(node.args[0]) elif name == '__go__array__': - return '[]%s{}' %self.visit(node.args[0]) + if isinstance(node.args[0], ast.BinOp):# and node.args[0].op == '<<': ## todo assert right is `typedef` + a = self.visit(node.args[0].left) + return '[]%s' %a + else: + a = self.visit(node.args[0]) + return '[]%s{}' %a else: raise SyntaxError(name) @@ -507,7 +517,7 @@ def main(script, insert_runtime=True): script = runtime + '\n' + script tree = ast.parse(script) - #return GoGenerator().visit(tree) + return GoGenerator().visit(tree) try: return GoGenerator().visit(tree) except SyntaxError as err: diff --git a/pythonjs/typedpython.py b/pythonjs/typedpython.py index 9c06d9b..f5bf3f3 100644 --- a/pythonjs/typedpython.py +++ b/pythonjs/typedpython.py @@ -24,7 +24,8 @@ GO_SPECIAL_CALLS = { 'go' : '__go__', 'go.channel' : '__go_make_chan__', - 'go.array' : '__go__array__' + 'go.array' : '__go__array__', + 'go.make' : '__go_make__' } def transform_source( source, strip=False ): @@ -73,7 +74,8 @@ def transform_source( source, strip=False ): a.append(')<<{') hit_go_typedef = False elif hit_go_typedef and char==',': - a.append('),') + #a.append(', type=True),') ## this breaks function annotations that splits on ',' + a.append('< Date: Thu, 11 Sep 2014 06:20:28 -0700 Subject: [PATCH 17/17] go backend: fixed go.make extra arguments. --- pythonjs/python_to_pythonjs.py | 5 +- pythonjs/pythonjs_to_go.py | 29 ++++++-- regtests/go/chan-transfer-speed.py | 107 +++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 regtests/go/chan-transfer-speed.py diff --git a/pythonjs/python_to_pythonjs.py b/pythonjs/python_to_pythonjs.py index 4bf5339..f05a92c 100755 --- a/pythonjs/python_to_pythonjs.py +++ b/pythonjs/python_to_pythonjs.py @@ -1923,7 +1923,7 @@ def _visit_assign_helper(self, node, target): elif isinstance(target.slice, ast.Slice): code = '%s.__setslice__(%s, %s)' %(self.visit(target.value), self.visit(target.slice), self.visit(node.value)) - elif self._with_dart or self._with_ll or self._with_glsl: + elif self._with_dart or self._with_ll or self._with_glsl or self._with_go: code = '%s[ %s ] = %s' code = code % (self.visit(target.value), self.visit(target.slice.value), self.visit(node.value)) @@ -2206,7 +2206,8 @@ def visit_Call(self, node): name = self.visit(node.func) if name in typedpython.GO_SPECIAL_CALLS: name = typedpython.GO_SPECIAL_CALLS[ name ] - return '%s( %s )' %(name, self.visit(node.args[0])) + args = [self.visit(e) for e in node.args ] + return '%s( %s )' %(name, ','.join(args)) if self._with_rpc: if not self._with_rpc_name: diff --git a/pythonjs/pythonjs_to_go.py b/pythonjs/pythonjs_to_go.py index 8599819..c0ff87c 100644 --- a/pythonjs/pythonjs_to_go.py +++ b/pythonjs/pythonjs_to_go.py @@ -212,11 +212,20 @@ def visit_For(self, node): target = self.visit(node.target) lines = [] if isinstance(node.iter, ast.Call) and isinstance(node.iter.func, ast.Name): - iter = self.visit(node.iter.args[0]) if node.iter.func.id == 'range': - lines.append('for %s := 0; %s < %s; %s++ {' %(target, target, iter, target)) + if len(node.iter.args)==1: + iter = self.visit(node.iter.args[0]) + lines.append('for %s := 0; %s < %s; %s++ {' %(target, target, iter, target)) + elif len(node.iter.args)==2: + start = self.visit(node.iter.args[0]) + iter = self.visit(node.iter.args[1]) + lines.append('for %s := %s; %s < %s; %s++ {' %(target, start, target, iter, target)) + else: + raise SyntaxError('invalid for range loop') + elif node.iter.func.id == 'enumerate': + iter = self.visit(node.iter.args[0]) idx = self.visit(node.target.elts[0]) tar = self.visit(node.target.elts[1]) lines.append('for %s,%s := range %s {' %(idx,tar, iter)) @@ -275,7 +284,12 @@ def _visit_call_helper_go(self, node): if name == '__go__': return 'go %s' %self.visit(node.args[0]) elif name == '__go_make__': - return 'make(%s)' %self.visit(node.args[0]) + if len(node.args)==2: + return 'make(%s, %s)' %(self.visit(node.args[0]), self.visit(node.args[1])) + elif len(node.args)==3: + return 'make(%s, %s, %s)' %(self.visit(node.args[0]), self.visit(node.args[1]), self.visit(node.args[1])) + else: + raise SyntaxError('go make requires 2 or 3 arguments') elif name == '__go_make_chan__': return 'make(chan %s)' %self.visit(node.args[0]) elif name == '__go__array__': @@ -288,6 +302,13 @@ def _visit_call_helper_go(self, node): else: raise SyntaxError(name) + def visit_Return(self, node): + if isinstance(node.value, ast.Tuple): + return 'return %s' % ', '.join(map(self.visit, node.value.elts)) + if node.value: + return 'return %s' % self.visit(node.value) + return 'return' + def _visit_function(self, node): if self._function_stack[0] is node: self._vars = set() @@ -517,7 +538,7 @@ def main(script, insert_runtime=True): script = runtime + '\n' + script tree = ast.parse(script) - return GoGenerator().visit(tree) + #return GoGenerator().visit(tree) try: return GoGenerator().visit(tree) except SyntaxError as err: diff --git a/regtests/go/chan-transfer-speed.py b/regtests/go/chan-transfer-speed.py new file mode 100644 index 0000000..9b0c7fb --- /dev/null +++ b/regtests/go/chan-transfer-speed.py @@ -0,0 +1,107 @@ +# based on the go test by Dennis Francis +# https://github.com/dennisfrancis/gopherjs-channeltransfer-speed + + +import "github.com/gopherjs/gopherjs/js" + + +data_chan = go.channel(int) + +document = js.Global.Get("document") + +def main(): + js.Global.Get("window").Set("onload", setup) + +def setup(): + + go( receive() ) + bt = document.Call("getElementById", "startbt") + bt.Set("onclick", runtests) + + +def runtests(): + var bt = document.Call("getElementById", "startbt") + bt.Set("disabled", true) + + #go func() { + # test_calldepth() + # test_localmem() + #}() + + go( test_calldepth() ) + go( test_localmem() ) + + +def test_calldepth(): + + latency = go.make([]float64, 1000) + perf = js.Global.Get("performance") + #for cd := 1; cd <= 1000; cd++ { + for cd in range(1, 1000): + t0 = perf.Call("now").Float() + #for ii:=0; ii<50; ii++ { + for ii in range(50): + send_func(cd, 1, ii) + t1 = perf.Call("now").Float() + latency[cd-1] = (t1 - t0)/50.0 + print("test1 calldepth =", cd) + plot("ctsgraph1", latency, "Call depth", "Variation of Kilo Channel Transfers per second (KCTps) with call depth") + +def test_localmem(): + + latency = go.make([]float64, 1000) + perf = js.Global.Get("performance") + #for varsz := 1; varsz <= 1000; varsz++ { + for varsz in range(1, 1000): + t0 = perf.Call("now").Float() + #for ii:=0; ii<50; ii++ { + for ii in range(50): + send_func(1, varsz, ii) + + t1 = perf.Call("now").Float() + latency[varsz-1] = (t1 - t0)/50.0 + plot("ctsgraph2", latency, "Local variable size", "Variation of Kilo Channel Transfers per second (KCTps) with local variable size") + + +def plot(id:string, latency:[]float64, xlabel:string, title:string ): + + div = document.Call("getElementById", id) + #options = map[string]interface{}{ + options = map[string]interface{ + #"legend" : "always", + "title" : title, + "showRoller" : true, + "rollPeriod" : 1, + "ylabel" : "KCTps", + "labels" : []string("x", "CTS"), + } + + data = go.make([][]float64, len(latency)) + + #for rowid := 0; rowid < len(latency); rowid++ { + for rowid in range(len(latency)): + data[rowid] = []float64( + float64(rowid + 1), + 1.0/latency[rowid] + ) + + + js.Global.Get("Dygraph").New(div, data, options) + + +def send_func(call_depth:int, varsize:int, data:int ): + locvar = go.make([]int, varsize) + + if call_depth <= 1: + data_chan <- data + return + + send_func(call_depth-1, varsize, data) + + print(locvar) + +def receive(): + int data = 0 + while True: + data = <-data_chan + print("Received data =", data)