2013-04-07 17:22:57 +02:00
|
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
#
|
2013-03-18 15:27:14 +01:00
|
|
|
# Copyright (c) 2013 Paul Tagliamonte <paultag@debian.org>
|
2013-04-07 17:22:57 +02:00
|
|
|
# Copyright (c) 2013 Julien Danjou <julien@danjou.info>
|
2013-05-04 09:16:01 +02:00
|
|
|
# Copyright (c) 2013 Nicolas Dandrimont <nicolas.dandrimont@crans.org>
|
2013-03-03 22:26:17 +01:00
|
|
|
#
|
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
# copy of this software and associated documentation files (the "Software"),
|
|
|
|
# to deal in the Software without restriction, including without limitation
|
|
|
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
# and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
# Software is furnished to do so, subject to the following conditions:
|
|
|
|
#
|
|
|
|
# The above copyright notice and this permission notice shall be included in
|
|
|
|
# all copies or substantial portions of the Software.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
# DEALINGS IN THE SOFTWARE.
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
from hy.errors import HyError
|
2013-03-05 01:12:57 +01:00
|
|
|
|
2013-04-23 03:47:43 +02:00
|
|
|
from hy.models.lambdalist import HyLambdaListKeyword
|
2013-03-05 02:40:23 +01:00
|
|
|
from hy.models.expression import HyExpression
|
2013-04-23 03:47:43 +02:00
|
|
|
from hy.models.keyword import HyKeyword
|
2013-03-06 00:39:34 +01:00
|
|
|
from hy.models.integer import HyInteger
|
2013-04-10 14:26:16 +02:00
|
|
|
from hy.models.complex import HyComplex
|
2013-03-05 02:40:23 +01:00
|
|
|
from hy.models.string import HyString
|
2013-03-06 04:08:53 +01:00
|
|
|
from hy.models.symbol import HySymbol
|
2013-04-23 03:47:43 +02:00
|
|
|
from hy.models.float import HyFloat
|
2013-03-06 04:08:53 +01:00
|
|
|
from hy.models.list import HyList
|
2013-03-09 06:55:27 +01:00
|
|
|
from hy.models.dict import HyDict
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-01 19:50:06 +02:00
|
|
|
from hy.core import process
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
from hy.util import str_type
|
2013-04-05 01:32:56 +02:00
|
|
|
|
2013-04-07 04:49:48 +02:00
|
|
|
import codecs
|
2013-05-04 09:16:01 +02:00
|
|
|
import traceback
|
2013-03-05 02:40:23 +01:00
|
|
|
import ast
|
2013-03-12 01:17:27 +01:00
|
|
|
import sys
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
from collections import defaultdict
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
|
|
|
|
class HyCompileError(HyError):
|
2013-04-09 23:06:04 +02:00
|
|
|
def __init__(self, exception, traceback=None):
|
2013-04-09 16:05:04 +02:00
|
|
|
self.exception = exception
|
2013-04-09 23:06:04 +02:00
|
|
|
self.traceback = traceback
|
2013-04-09 16:05:04 +02:00
|
|
|
|
|
|
|
def __str__(self):
|
2013-04-09 23:06:04 +02:00
|
|
|
if isinstance(self.exception, HyTypeError):
|
|
|
|
return str(self.exception)
|
|
|
|
if self.traceback:
|
|
|
|
tb = "".join(traceback.format_tb(self.traceback)).strip()
|
|
|
|
else:
|
|
|
|
tb = "No traceback available. 😟"
|
|
|
|
return("Internal Compiler Bug 😱\n⤷ %s: %s\nCompilation traceback:\n%s"
|
|
|
|
% (self.exception.__class__.__name__,
|
|
|
|
self.exception, tb))
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-04-09 23:06:04 +02:00
|
|
|
class HyTypeError(TypeError):
|
|
|
|
def __init__(self, expression, message):
|
|
|
|
super(HyTypeError, self).__init__(message)
|
|
|
|
self.expression = expression
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-04-09 23:06:04 +02:00
|
|
|
def __str__(self):
|
2013-04-24 22:34:14 +02:00
|
|
|
return (super(HyTypeError, self).__str__() + " (line %s, column %d)"
|
2013-04-09 23:06:04 +02:00
|
|
|
% (self.expression.start_line,
|
|
|
|
self.expression.start_column))
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
_compile_table = {}
|
|
|
|
|
|
|
|
|
2013-04-07 03:33:52 +02:00
|
|
|
def ast_str(foobar):
|
|
|
|
if sys.version_info[0] >= 3:
|
|
|
|
return str(foobar)
|
|
|
|
|
2013-04-07 04:49:48 +02:00
|
|
|
try:
|
|
|
|
return str(foobar)
|
|
|
|
except UnicodeEncodeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
enc = codecs.getencoder('punycode')
|
|
|
|
foobar, _ = enc(foobar)
|
2013-04-23 03:49:19 +02:00
|
|
|
return "hy_%s" % (str(foobar).replace("-", "_"))
|
2013-04-07 03:33:52 +02:00
|
|
|
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
def builds(_type):
|
|
|
|
def _dec(fn):
|
|
|
|
_compile_table[_type] = fn
|
2013-04-16 17:53:02 +02:00
|
|
|
return fn
|
2013-03-05 02:40:23 +01:00
|
|
|
return _dec
|
|
|
|
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
class Result(object):
|
|
|
|
"""
|
|
|
|
Smart representation of the result of a hy->AST compilation
|
|
|
|
|
|
|
|
This object tries to reconcile the hy world, where everything can be used
|
|
|
|
as an expression, with the Python world, where statements and expressions
|
|
|
|
need to coexist.
|
|
|
|
|
|
|
|
To do so, we represent a compiler result as a list of statements `stmts`,
|
|
|
|
terminated by an expression context `expr`. The expression context is used
|
|
|
|
when the compiler needs to use the result as an expression.
|
|
|
|
|
|
|
|
Results are chained by addition: adding two results together returns a
|
|
|
|
Result representing the succession of the two Results' statements, with
|
|
|
|
the second Result's expression context.
|
|
|
|
|
|
|
|
We make sure that a non-empty expression context does not get clobbered by
|
|
|
|
adding more results, by checking accesses to the expression context. We
|
|
|
|
assume that the context has been used, or deliberately ignored, if it has
|
|
|
|
been accessed.
|
|
|
|
|
|
|
|
The Result object is interoperable with python AST objects: when an AST
|
|
|
|
object gets added to a Result object, it gets converted on-the-fly.
|
|
|
|
"""
|
|
|
|
__slots__ = ("imports", "stmts", "temp_variables", "_expr", "__used_expr")
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
if args:
|
|
|
|
# emulate kw-only args for future bits.
|
|
|
|
raise TypeError("Yo: Hacker: don't pass me real args, dingus")
|
|
|
|
|
|
|
|
self.imports = defaultdict(set)
|
|
|
|
self.stmts = []
|
|
|
|
self.temp_variables = []
|
|
|
|
self._expr = None
|
|
|
|
|
|
|
|
self.__used_expr = False
|
|
|
|
|
|
|
|
# XXX: Make sure we only have AST where we should.
|
|
|
|
for kwarg in kwargs:
|
|
|
|
if kwarg not in ["imports", "stmts", "expr", "temp_variables"]:
|
|
|
|
raise TypeError(
|
|
|
|
"%s() got an unexpected keyword argument '%s'" % (
|
|
|
|
self.__class__.__name__, kwarg))
|
|
|
|
setattr(self, kwarg, kwargs[kwarg])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def expr(self):
|
|
|
|
self.__used_expr = True
|
|
|
|
return self._expr
|
|
|
|
|
|
|
|
@expr.setter
|
|
|
|
def expr(self, value):
|
|
|
|
self.__used_expr = False
|
|
|
|
self._expr = value
|
|
|
|
|
|
|
|
def add_imports(self, mod, imports):
|
|
|
|
"""Autoimport `imports` from `mod`"""
|
|
|
|
self.imports[mod].update(imports)
|
|
|
|
|
|
|
|
def is_expr(self):
|
|
|
|
"""Check whether I am a pure expression"""
|
|
|
|
return self._expr and not (self.imports or self.stmts)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def force_expr(self):
|
|
|
|
"""Force the expression context of the Result.
|
|
|
|
|
|
|
|
If there is no expression context, we return a "None" expression.
|
|
|
|
"""
|
|
|
|
if not self.expr:
|
|
|
|
# Spoof the position of the last statement for our generated None
|
|
|
|
lineno = 0
|
|
|
|
col_offset = 0
|
|
|
|
if self.stmts:
|
|
|
|
lineno = self.stmts[-1].lineno
|
|
|
|
col_offset = self.stmts[-1].col_offset
|
|
|
|
|
|
|
|
return ast.Name(id=ast_str("None"),
|
|
|
|
arg=ast_str("None"),
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=lineno,
|
|
|
|
col_offset=col_offset)
|
|
|
|
# XXX: Likely raise Exception here - this will assertionfail
|
|
|
|
# pypy since the ast will be out of numerical order.
|
|
|
|
else:
|
|
|
|
return self.expr
|
|
|
|
|
|
|
|
def expr_as_stmt(self):
|
|
|
|
"""Convert the Result's expression context to a statement
|
|
|
|
|
|
|
|
This is useful when we want to use the stored expression in a
|
|
|
|
statement context (for instance in a code branch).
|
|
|
|
|
|
|
|
If there is no expression context, return an empty result.
|
|
|
|
"""
|
2013-05-03 22:01:21 +02:00
|
|
|
if self.expr:
|
2013-05-04 09:16:01 +02:00
|
|
|
return Result() + ast.Expr(lineno=self.expr.lineno,
|
|
|
|
col_offset=self.expr.col_offset,
|
|
|
|
value=self.expr)
|
2013-05-03 22:01:21 +02:00
|
|
|
return Result()
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
def rename(self, new_name):
|
|
|
|
"""Rename the Result's temporary variables to a `new_name`.
|
|
|
|
|
|
|
|
We know how to handle ast.Names and ast.FunctionDefs.
|
|
|
|
"""
|
|
|
|
new_name = ast_str(new_name)
|
|
|
|
for var in self.temp_variables:
|
|
|
|
if isinstance(var, ast.Name):
|
|
|
|
var.id = new_name
|
|
|
|
var.arg = new_name
|
|
|
|
elif isinstance(var, ast.FunctionDef):
|
|
|
|
var.name = new_name
|
|
|
|
else:
|
|
|
|
raise TypeError("Don't know how to rename a %s!" % (
|
|
|
|
var.__class__.__name__))
|
|
|
|
self.temp_variables = []
|
|
|
|
|
|
|
|
def __add__(self, other):
|
|
|
|
# If we add an ast statement, convert it first
|
|
|
|
if isinstance(other, ast.stmt):
|
|
|
|
return self + Result(stmts=[other])
|
|
|
|
|
|
|
|
# If we add an ast expression, clobber the expression context
|
|
|
|
if isinstance(other, ast.expr):
|
|
|
|
return self + Result(expr=other)
|
|
|
|
|
|
|
|
if isinstance(other, ast.excepthandler):
|
|
|
|
return self + Result(stmts=[other])
|
|
|
|
|
|
|
|
if not isinstance(other, Result):
|
|
|
|
raise TypeError("Can't add %r with non-compiler result %r" % (
|
|
|
|
self, other))
|
|
|
|
|
|
|
|
# Check for expression context clobbering
|
|
|
|
if self.expr and not self.__used_expr:
|
|
|
|
traceback.print_stack()
|
|
|
|
print("Bad boy clobbered expr %s with %s" % (
|
|
|
|
ast.dump(self.expr),
|
|
|
|
ast.dump(other.expr)))
|
|
|
|
|
|
|
|
# Fairly obvious addition
|
|
|
|
result = Result()
|
|
|
|
result.imports = other.imports
|
|
|
|
result.stmts = self.stmts + other.stmts
|
|
|
|
result.expr = other.expr
|
|
|
|
result.temp_variables = other.temp_variables
|
|
|
|
return result
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "Result(imports=[%s], stmts=[%s], expr=%s)" % (
|
|
|
|
", ".join(ast.dump(x) for x in self.imports),
|
|
|
|
", ".join(ast.dump(x) for x in self.stmts),
|
|
|
|
ast.dump(self.expr) if self.expr else None,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def _collect(results):
|
|
|
|
"""Collect the expression contexts from a list of results
|
|
|
|
|
|
|
|
This returns a list of the expression contexts, and the sum of the Result
|
|
|
|
objects passed as arguments.
|
|
|
|
"""
|
|
|
|
compiled_exprs = []
|
|
|
|
ret = Result()
|
|
|
|
for result in results:
|
|
|
|
ret += result
|
|
|
|
compiled_exprs.append(ret.force_expr)
|
|
|
|
return compiled_exprs, ret
|
|
|
|
|
|
|
|
|
|
|
|
def _branch(results):
|
|
|
|
"""Make a branch out of a list of Result objects
|
|
|
|
|
|
|
|
This generates a Result from the given sequence of Results, forcing each
|
|
|
|
expression context as a statement before the next result is used.
|
|
|
|
|
|
|
|
We keep the expression context of the last argument for the returned Result
|
|
|
|
"""
|
|
|
|
results = list(results)
|
|
|
|
ret = Result()
|
|
|
|
for result in results[:-1]:
|
|
|
|
ret += result
|
|
|
|
ret += result.expr_as_stmt()
|
|
|
|
|
|
|
|
for result in results[-1:]:
|
|
|
|
ret += result
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def _raise_wrong_args_number(expression, error):
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(expression,
|
|
|
|
error % (expression.pop(0),
|
|
|
|
len(expression)))
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def checkargs(exact=None, min=None, max=None):
|
|
|
|
def _dec(fn):
|
|
|
|
def checker(self, expression):
|
|
|
|
if exact is not None and (len(expression) - 1) != exact:
|
2013-04-10 02:44:05 +02:00
|
|
|
_raise_wrong_args_number(
|
|
|
|
expression, "`%%s' needs %d arguments, got %%d" % exact)
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
if min is not None and (len(expression) - 1) < min:
|
2013-04-10 02:44:52 +02:00
|
|
|
_raise_wrong_args_number(
|
|
|
|
expression,
|
2013-04-06 19:15:32 +02:00
|
|
|
"`%%s' needs at least %d arguments, got %%d" % (min))
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
if max is not None and (len(expression) - 1) > max:
|
2013-04-10 02:44:52 +02:00
|
|
|
_raise_wrong_args_number(
|
|
|
|
expression,
|
2013-04-06 19:15:32 +02:00
|
|
|
"`%%s' needs at most %d arguments, got %%d" % (max))
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
return fn(self, expression)
|
|
|
|
|
|
|
|
return checker
|
|
|
|
return _dec
|
|
|
|
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
class HyASTCompiler(object):
|
|
|
|
|
|
|
|
def __init__(self):
|
2013-03-05 04:35:07 +01:00
|
|
|
self.anon_fn_count = 0
|
2013-05-04 09:16:01 +02:00
|
|
|
self.anon_var_count = 0
|
|
|
|
self.imports = defaultdict(set)
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
def get_anon_var(self):
|
|
|
|
self.anon_var_count += 1
|
|
|
|
return "_hy_anon_var_%s" % self.anon_var_count
|
|
|
|
|
|
|
|
def get_anon_fn(self):
|
|
|
|
self.anon_fn_count += 1
|
|
|
|
return "_hy_anon_fn_%d" % self.anon_fn_count
|
|
|
|
|
|
|
|
def update_imports(self, result):
|
|
|
|
"""Retrieve the imports from the result object"""
|
|
|
|
for mod in result.imports:
|
|
|
|
self.imports[mod].update(result.imports[mod])
|
|
|
|
|
|
|
|
def imports_as_stmts(self, expr):
|
|
|
|
"""Convert the Result's imports to statements"""
|
|
|
|
ret = Result()
|
|
|
|
for module, names in self.imports.items():
|
|
|
|
ret += self.compile([
|
|
|
|
HyExpression([
|
|
|
|
HySymbol("import"),
|
|
|
|
HyList([
|
|
|
|
HySymbol(module),
|
|
|
|
HyList([HySymbol(name) for name in sorted(names)])
|
|
|
|
])
|
|
|
|
]).replace(expr)
|
|
|
|
])
|
|
|
|
self.imports = defaultdict(set)
|
|
|
|
return ret.stmts
|
|
|
|
|
|
|
|
def compile_atom(self, atom_type, atom):
|
|
|
|
if atom_type in _compile_table:
|
2013-05-01 19:50:06 +02:00
|
|
|
atom = process(atom)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = _compile_table[atom_type](self, atom)
|
|
|
|
if not isinstance(ret, Result):
|
|
|
|
ret = Result() + ret
|
|
|
|
return ret
|
|
|
|
else:
|
|
|
|
return None
|
2013-04-21 00:17:30 +02:00
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
def compile(self, tree):
|
2013-04-06 16:33:06 +02:00
|
|
|
try:
|
2013-04-22 22:46:43 +02:00
|
|
|
_type = type(tree)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile_atom(_type, tree)
|
|
|
|
if ret:
|
|
|
|
self.update_imports(ret)
|
|
|
|
return ret
|
2013-04-09 16:05:04 +02:00
|
|
|
except HyCompileError:
|
|
|
|
# compile calls compile, so we're going to have multiple raise
|
|
|
|
# nested; so let's re-raise this exception, let's not wrap it in
|
|
|
|
# another HyCompileError!
|
|
|
|
raise
|
2013-04-06 16:33:06 +02:00
|
|
|
except Exception as e:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyCompileError(e, sys.exc_info()[2])
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
raise HyCompileError(Exception("Unknown type: `%s'" % _type))
|
2013-04-04 02:18:56 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
def _compile_collect(self, exprs):
|
|
|
|
return _collect(self.compile(expr) for expr in exprs)
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
def _compile_branch(self, exprs):
|
|
|
|
return _branch(self.compile(expr) for expr in exprs)
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-04-03 17:39:31 +02:00
|
|
|
def _parse_lambda_list(self, exprs):
|
2013-04-19 04:27:38 +02:00
|
|
|
""" Return FunctionDef parameter values from lambda list."""
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = Result()
|
2013-04-09 21:23:50 +02:00
|
|
|
args = []
|
2013-04-19 04:27:38 +02:00
|
|
|
defaults = []
|
2013-04-11 18:00:27 +02:00
|
|
|
varargs = None
|
2013-04-19 04:27:38 +02:00
|
|
|
kwargs = None
|
2013-04-09 21:23:50 +02:00
|
|
|
lambda_keyword = None
|
|
|
|
|
2013-04-19 16:34:17 +02:00
|
|
|
for expr in exprs:
|
2013-04-09 21:23:50 +02:00
|
|
|
|
|
|
|
if isinstance(expr, HyLambdaListKeyword):
|
|
|
|
if expr not in expr._valid_types:
|
|
|
|
raise HyCompileError("{0} is not a valid "
|
|
|
|
"lambda-keyword.".format(repr(expr)))
|
|
|
|
if expr == "&rest" and lambda_keyword is None:
|
|
|
|
lambda_keyword = expr
|
2013-04-18 23:47:08 +02:00
|
|
|
elif expr == "&optional":
|
2013-04-09 21:23:50 +02:00
|
|
|
lambda_keyword = expr
|
2013-04-18 23:47:08 +02:00
|
|
|
elif expr == "&key":
|
|
|
|
lambda_keyword = expr
|
|
|
|
elif expr == "&kwargs":
|
2013-04-09 21:23:50 +02:00
|
|
|
lambda_keyword = expr
|
|
|
|
else:
|
|
|
|
raise HyCompileError("{0} is in an invalid "
|
|
|
|
"position.".format(repr(expr)))
|
|
|
|
# we don't actually care about this token, so we set
|
|
|
|
# our state and continue to the next token...
|
|
|
|
continue
|
|
|
|
|
|
|
|
if lambda_keyword is None:
|
2013-04-10 22:52:28 +02:00
|
|
|
args.append(expr)
|
2013-04-09 21:23:50 +02:00
|
|
|
elif lambda_keyword == "&rest":
|
2013-04-11 18:00:27 +02:00
|
|
|
if varargs:
|
2013-04-09 21:23:50 +02:00
|
|
|
raise HyCompileError("There can only be one "
|
|
|
|
"&rest argument")
|
2013-04-11 18:00:27 +02:00
|
|
|
varargs = str(expr)
|
2013-04-18 23:47:08 +02:00
|
|
|
elif lambda_keyword == "&key":
|
|
|
|
if type(expr) != HyDict:
|
2013-04-19 04:27:38 +02:00
|
|
|
raise TypeError("There can only be one &key "
|
|
|
|
"argument")
|
2013-04-18 23:47:08 +02:00
|
|
|
else:
|
2013-04-19 04:27:38 +02:00
|
|
|
if len(defaults) > 0:
|
2013-04-19 00:39:49 +02:00
|
|
|
raise HyCompileError("There can only be "
|
|
|
|
"one &key argument")
|
2013-04-19 04:27:38 +02:00
|
|
|
# As you can see, Python has a funny way of
|
|
|
|
# defining keyword arguments.
|
2013-04-19 16:34:17 +02:00
|
|
|
for k, v in expr.items():
|
2013-04-19 04:27:38 +02:00
|
|
|
args.append(k)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += self.compile(v)
|
|
|
|
defaults.append(ret.force_expr)
|
2013-04-09 21:23:50 +02:00
|
|
|
elif lambda_keyword == "&optional":
|
2013-04-18 23:47:08 +02:00
|
|
|
# not implemented yet.
|
2013-04-09 21:23:50 +02:00
|
|
|
pass
|
2013-04-18 23:47:08 +02:00
|
|
|
elif lambda_keyword == "&kwargs":
|
2013-04-19 04:27:38 +02:00
|
|
|
if kwargs:
|
|
|
|
raise HyCompileError("There can only be one "
|
|
|
|
"&kwargs argument")
|
2013-04-18 23:47:08 +02:00
|
|
|
kwargs = str(expr)
|
2013-04-09 21:23:50 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret, args, defaults, varargs, kwargs
|
|
|
|
|
|
|
|
def _storeize(self, name):
|
|
|
|
"""Return a new `name` object with an ast.Store() context"""
|
|
|
|
if isinstance(name, Result):
|
|
|
|
if not name.is_expr():
|
|
|
|
raise TypeError("Can't assign to a non-expr")
|
|
|
|
name = name.expr
|
|
|
|
|
|
|
|
if isinstance(name, (ast.Tuple, ast.List)):
|
|
|
|
typ = type(name)
|
|
|
|
new_elts = []
|
|
|
|
for x in name.elts:
|
|
|
|
new_elts.append(self._storeize(x))
|
|
|
|
new_name = typ(elts=new_elts)
|
|
|
|
elif isinstance(name, ast.Name):
|
|
|
|
new_name = ast.Name(id=name.id, arg=name.arg)
|
|
|
|
elif isinstance(name, ast.Subscript):
|
|
|
|
new_name = ast.Subscript(value=name.value, slice=name.slice)
|
|
|
|
elif isinstance(name, ast.Attribute):
|
|
|
|
new_name = ast.Attribute(value=name.value, attr=name.attr)
|
|
|
|
else:
|
|
|
|
raise TypeError("Can't assign to a %s object" % type(name))
|
|
|
|
|
|
|
|
new_name.ctx = ast.Store()
|
|
|
|
ast.copy_location(new_name, name)
|
|
|
|
return new_name
|
2013-04-03 17:39:31 +02:00
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
@builds(list)
|
|
|
|
def compile_raw_list(self, entries):
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self._compile_branch(entries)
|
|
|
|
ret += ret.expr_as_stmt()
|
|
|
|
return ret
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-04-09 02:18:15 +02:00
|
|
|
def _render_quoted_form(self, form):
|
|
|
|
name = form.__class__.__name__
|
2013-05-04 09:16:01 +02:00
|
|
|
imports = [name]
|
2013-04-11 04:51:58 +02:00
|
|
|
|
2013-04-09 02:18:15 +02:00
|
|
|
if isinstance(form, HyList):
|
2013-05-04 09:16:01 +02:00
|
|
|
contents = []
|
|
|
|
for x in form:
|
|
|
|
form_imports, form_contents = self._render_quoted_form(x)
|
|
|
|
imports += form_imports
|
|
|
|
contents.append(form_contents)
|
|
|
|
return imports, HyExpression(
|
2013-04-09 02:18:15 +02:00
|
|
|
[HySymbol(name),
|
2013-05-04 09:16:01 +02:00
|
|
|
HyList(contents)]
|
2013-04-09 02:18:15 +02:00
|
|
|
).replace(form)
|
|
|
|
elif isinstance(form, HySymbol):
|
2013-05-04 09:16:01 +02:00
|
|
|
return imports, HyExpression([HySymbol(name),
|
|
|
|
HyString(form)]).replace(form)
|
|
|
|
return imports, HyExpression([HySymbol(name), form]).replace(form)
|
2013-04-09 02:18:15 +02:00
|
|
|
|
|
|
|
@builds("quote")
|
2013-04-10 02:34:46 +02:00
|
|
|
@checkargs(exact=1)
|
2013-04-09 02:18:15 +02:00
|
|
|
def compile_quote(self, entries):
|
2013-05-04 09:16:01 +02:00
|
|
|
imports, stmts = self._render_quoted_form(entries[1])
|
|
|
|
ret = self.compile(stmts)
|
|
|
|
ret.add_imports("hy", imports)
|
|
|
|
return ret
|
2013-04-09 02:18:15 +02:00
|
|
|
|
2013-04-10 03:33:09 +02:00
|
|
|
@builds("eval")
|
|
|
|
@checkargs(exact=1)
|
|
|
|
def compile_eval(self, expr):
|
|
|
|
expr.pop(0)
|
2013-04-11 04:51:58 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(HyExpression([
|
2013-04-10 03:40:54 +02:00
|
|
|
HySymbol("hy_eval")] + expr + [
|
|
|
|
HyExpression([HySymbol("locals")])]).replace(expr))
|
2013-04-10 03:33:09 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret.add_imports("hy.importer", ["hy_eval"])
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("do")
|
|
|
|
@builds("progn")
|
2013-05-04 09:16:01 +02:00
|
|
|
def compile_progn(self, expression):
|
|
|
|
expression.pop(0)
|
|
|
|
return self._compile_branch(expression)
|
2013-03-09 05:41:04 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("throw")
|
2013-04-07 18:24:01 +02:00
|
|
|
@builds("raise")
|
2013-04-09 16:50:27 +02:00
|
|
|
@checkargs(max=1)
|
2013-03-12 00:14:20 +01:00
|
|
|
def compile_throw_expression(self, expr):
|
|
|
|
expr.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = Result()
|
|
|
|
if expr:
|
|
|
|
ret += self.compile(expr.pop(0))
|
|
|
|
|
|
|
|
# Use ret.expr to get a literal `None`
|
|
|
|
ret += ast.Raise(
|
2013-03-12 00:14:20 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
2013-05-04 09:16:01 +02:00
|
|
|
type=ret.expr,
|
|
|
|
exc=ret.expr,
|
2013-03-12 00:14:20 +01:00
|
|
|
inst=None,
|
2013-05-04 09:16:01 +02:00
|
|
|
tback=None,
|
|
|
|
cause=None)
|
|
|
|
|
|
|
|
return ret
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("try")
|
2013-03-12 00:14:20 +01:00
|
|
|
def compile_try_expression(self, expr):
|
|
|
|
expr.pop(0) # try
|
2013-03-12 01:17:27 +01:00
|
|
|
|
2013-04-08 19:22:30 +02:00
|
|
|
try:
|
|
|
|
body = expr.pop(0)
|
|
|
|
except IndexError:
|
|
|
|
body = []
|
|
|
|
|
|
|
|
# (try something…)
|
2013-05-04 09:16:01 +02:00
|
|
|
body = self.compile(body)
|
|
|
|
|
|
|
|
# XXX we will likely want to make this a tempvar
|
|
|
|
body += body.expr_as_stmt()
|
|
|
|
body = body.stmts
|
|
|
|
if not body:
|
|
|
|
body = [ast.Pass(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)]
|
2013-04-07 19:06:02 +02:00
|
|
|
|
2013-04-08 15:58:43 +02:00
|
|
|
orelse = []
|
2013-04-09 21:19:24 +02:00
|
|
|
finalbody = []
|
|
|
|
handlers = []
|
2013-05-04 09:16:01 +02:00
|
|
|
handler_results = Result()
|
2013-04-09 21:19:24 +02:00
|
|
|
|
|
|
|
for e in expr:
|
|
|
|
if not len(e):
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(e, "Empty list not allowed in `try'")
|
2013-04-09 21:19:24 +02:00
|
|
|
|
|
|
|
if e[0] in (HySymbol("except"), HySymbol("catch")):
|
2013-05-04 09:16:01 +02:00
|
|
|
handler_results += self.compile(e)
|
|
|
|
handlers.append(handler_results.stmts.pop())
|
2013-04-09 21:19:24 +02:00
|
|
|
elif e[0] == HySymbol("else"):
|
|
|
|
if orelse:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(
|
|
|
|
e,
|
2013-04-09 21:19:24 +02:00
|
|
|
"`try' cannot have more than one `else'")
|
|
|
|
else:
|
2013-05-04 09:16:01 +02:00
|
|
|
orelse = self._compile_branch(e[1:])
|
|
|
|
# XXX tempvar magic
|
|
|
|
orelse += orelse.expr_as_stmt()
|
|
|
|
orelse = orelse.stmts
|
2013-04-09 21:19:24 +02:00
|
|
|
elif e[0] == HySymbol("finally"):
|
|
|
|
if finalbody:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(
|
|
|
|
e,
|
2013-04-09 21:19:24 +02:00
|
|
|
"`try' cannot have more than one `finally'")
|
|
|
|
else:
|
2013-05-04 09:16:01 +02:00
|
|
|
finalbody = self._compile_branch(e[1:])
|
|
|
|
# XXX tempvar magic
|
|
|
|
finalbody += finalbody.expr_as_stmt()
|
|
|
|
finalbody = finalbody.stmts
|
2013-04-09 21:19:24 +02:00
|
|
|
else:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(e, "Unknown expression in `try'")
|
2013-04-09 21:19:24 +02:00
|
|
|
|
|
|
|
# Using (else) without (except) is verboten!
|
|
|
|
if orelse and not handlers:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(
|
|
|
|
e,
|
2013-04-09 21:19:24 +02:00
|
|
|
"`try' cannot have `else' without `except'")
|
|
|
|
|
|
|
|
# (try) or (try BODY)
|
|
|
|
# Generate a default handler for Python >= 3.3 and pypy
|
|
|
|
if not handlers and not finalbody and not orelse:
|
2013-04-08 00:36:08 +02:00
|
|
|
handlers = [ast.ExceptHandler(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
type=None,
|
|
|
|
name=None,
|
|
|
|
body=[ast.Pass(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)])]
|
2013-04-08 15:58:43 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = handler_results
|
|
|
|
|
2013-04-09 21:19:24 +02:00
|
|
|
if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
|
|
|
|
# Python 3.3 features a merge of TryExcept+TryFinally into Try.
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret + ast.Try(
|
2013-04-09 21:19:24 +02:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
body=body,
|
|
|
|
handlers=handlers,
|
|
|
|
orelse=orelse,
|
|
|
|
finalbody=finalbody)
|
|
|
|
|
|
|
|
if finalbody:
|
|
|
|
if handlers:
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret + ast.TryFinally(
|
2013-04-09 21:19:24 +02:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
body=[ast.TryExcept(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
handlers=handlers,
|
|
|
|
body=body,
|
|
|
|
orelse=orelse)],
|
|
|
|
finalbody=finalbody)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret + ast.TryFinally(
|
2013-04-09 21:19:24 +02:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
body=body,
|
|
|
|
finalbody=finalbody)
|
2013-04-08 00:36:08 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret + ast.TryExcept(
|
2013-03-12 00:14:20 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
2013-04-08 00:36:08 +02:00
|
|
|
handlers=handlers,
|
2013-04-09 21:19:24 +02:00
|
|
|
body=body,
|
2013-04-08 15:58:43 +02:00
|
|
|
orelse=orelse)
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("catch")
|
2013-04-07 18:24:01 +02:00
|
|
|
@builds("except")
|
2013-03-12 00:14:20 +01:00
|
|
|
def compile_catch_expression(self, expr):
|
2013-04-09 16:56:45 +02:00
|
|
|
catch = expr.pop(0) # catch
|
2013-04-07 17:22:57 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
exceptions = expr.pop(0)
|
|
|
|
except IndexError:
|
2013-04-09 16:56:45 +02:00
|
|
|
exceptions = HyList()
|
2013-04-07 17:22:57 +02:00
|
|
|
# exceptions catch should be either:
|
|
|
|
# [[list of exceptions]]
|
|
|
|
# or
|
|
|
|
# [variable [list of exceptions]]
|
|
|
|
# or
|
|
|
|
# [variable exception]
|
|
|
|
# or
|
|
|
|
# [exception]
|
|
|
|
# or
|
|
|
|
# []
|
2013-04-09 16:56:45 +02:00
|
|
|
if not isinstance(exceptions, HyList):
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(exceptions,
|
|
|
|
"`%s' exceptions list is not a list" % catch)
|
2013-04-07 17:22:57 +02:00
|
|
|
if len(exceptions) > 2:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(exceptions,
|
|
|
|
"`%s' exceptions list is too long" % catch)
|
2013-04-07 17:22:57 +02:00
|
|
|
|
|
|
|
# [variable [list of exceptions]]
|
|
|
|
# let's pop variable and use it as name
|
|
|
|
if len(exceptions) == 2:
|
|
|
|
name = exceptions.pop(0)
|
|
|
|
if sys.version_info[0] >= 3:
|
|
|
|
# Python3 features a change where the Exception handler
|
|
|
|
# moved the name from a Name() to a pure Python String type.
|
|
|
|
#
|
|
|
|
# We'll just make sure it's a pure "string", and let it work
|
|
|
|
# it's magic.
|
|
|
|
name = ast_str(name)
|
|
|
|
else:
|
|
|
|
# Python2 requires an ast.Name, set to ctx Store.
|
|
|
|
name = self._storeize(self.compile(name))
|
|
|
|
else:
|
|
|
|
name = None
|
|
|
|
|
|
|
|
try:
|
|
|
|
exceptions_list = exceptions.pop(0)
|
|
|
|
except IndexError:
|
|
|
|
exceptions_list = []
|
|
|
|
|
|
|
|
if isinstance(exceptions_list, list):
|
|
|
|
if len(exceptions_list):
|
|
|
|
# [FooBar BarFoo] → catch Foobar and BarFoo exceptions
|
2013-05-04 09:16:01 +02:00
|
|
|
elts, _type = self._compile_collect(exceptions_list)
|
|
|
|
_type += ast.Tuple(elts=elts,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
ctx=ast.Load())
|
2013-04-07 17:22:57 +02:00
|
|
|
else:
|
|
|
|
# [] → all exceptions catched
|
2013-05-04 09:16:01 +02:00
|
|
|
_type = Result()
|
2013-04-07 17:22:57 +02:00
|
|
|
elif isinstance(exceptions_list, HySymbol):
|
|
|
|
_type = self.compile(exceptions_list)
|
|
|
|
else:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(exceptions,
|
|
|
|
"`%s' needs a valid exception list" % catch)
|
2013-04-07 17:22:57 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
body = self._compile_branch(expr)
|
|
|
|
# XXX tempvar handling magic
|
|
|
|
body += body.expr_as_stmt()
|
2013-03-14 14:21:03 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
body = body.stmts
|
|
|
|
if not body:
|
|
|
|
body = [ast.Pass(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)]
|
|
|
|
|
|
|
|
# use _type.expr to get a literal `None`
|
|
|
|
return _type + ast.ExceptHandler(
|
2013-03-12 00:14:20 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
2013-05-04 09:16:01 +02:00
|
|
|
type=_type.expr,
|
2013-03-14 14:21:03 +01:00
|
|
|
name=name,
|
2013-04-07 17:22:57 +02:00
|
|
|
body=body)
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("if")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=2, max=3)
|
2013-05-04 09:16:01 +02:00
|
|
|
def compile_if(self, expression):
|
|
|
|
expression.pop(0)
|
|
|
|
cond = self.compile(expression.pop(0))
|
|
|
|
|
|
|
|
body = self.compile(expression.pop(0))
|
|
|
|
orel = Result()
|
|
|
|
if expression:
|
|
|
|
orel = self.compile(expression.pop(0))
|
|
|
|
|
|
|
|
# We want to hoist the statements from the condition
|
|
|
|
ret = cond
|
|
|
|
|
|
|
|
if body.stmts or orel.stmts:
|
|
|
|
# We have statements in our bodies
|
|
|
|
# Get a temporary variable for the result storage
|
|
|
|
var = self.get_anon_var()
|
|
|
|
name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
|
|
|
ctx=ast.Store(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
# Store the result of the body
|
|
|
|
body += ast.Assign(targets=[name],
|
|
|
|
value=body.force_expr,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
# and of the else clause
|
|
|
|
orel += ast.Assign(targets=[name],
|
|
|
|
value=orel.force_expr,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
# Then build the if
|
|
|
|
ret += ast.If(test=ret.force_expr,
|
|
|
|
body=body.stmts,
|
|
|
|
orelse=orel.stmts,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
# And make our expression context our temp variable
|
|
|
|
expr_name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
ret += Result(expr=expr_name, temp_variables=[expr_name, name])
|
2013-04-06 16:33:06 +02:00
|
|
|
else:
|
2013-05-04 09:16:01 +02:00
|
|
|
# Just make that an if expression
|
|
|
|
ret += ast.IfExp(test=ret.force_expr,
|
|
|
|
body=body.force_expr,
|
|
|
|
orelse=orel.force_expr,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
return ret
|
2013-03-09 05:07:21 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("print")
|
2013-03-10 20:39:27 +01:00
|
|
|
def compile_print_expression(self, expr):
|
2013-04-02 05:06:59 +02:00
|
|
|
call = expr.pop(0) # print
|
2013-05-04 09:16:01 +02:00
|
|
|
values, ret = self._compile_collect(expr)
|
|
|
|
|
2013-04-02 01:51:21 +02:00
|
|
|
if sys.version_info[0] >= 3:
|
2013-04-02 05:06:59 +02:00
|
|
|
call = self.compile(call)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += call
|
|
|
|
ret += ast.Call(func=call.expr,
|
|
|
|
args=values,
|
|
|
|
keywords=[],
|
|
|
|
starargs=None,
|
|
|
|
kwargs=None,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
else:
|
|
|
|
ret += ast.Print(
|
2013-04-02 01:51:21 +02:00
|
|
|
lineno=expr.start_line,
|
2013-05-04 09:16:01 +02:00
|
|
|
col_offset=expr.start_column,
|
|
|
|
dest=None,
|
|
|
|
values=values,
|
|
|
|
nl=True)
|
2013-04-02 01:51:21 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-10 20:39:27 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("assert")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(1)
|
2013-03-06 03:42:54 +01:00
|
|
|
def compile_assert_expression(self, expr):
|
|
|
|
expr.pop(0) # assert
|
|
|
|
e = expr.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(e)
|
|
|
|
ret += ast.Assert(test=ret.force_expr,
|
2013-03-06 03:42:54 +01:00
|
|
|
msg=None,
|
|
|
|
lineno=e.start_line,
|
|
|
|
col_offset=e.start_column)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
|
|
|
|
2013-04-19 08:40:03 +02:00
|
|
|
@builds("global")
|
|
|
|
@checkargs(1)
|
2013-04-24 01:25:02 +02:00
|
|
|
def compile_global_expression(self, expr):
|
|
|
|
expr.pop(0) # global
|
2013-04-19 08:40:03 +02:00
|
|
|
e = expr.pop(0)
|
|
|
|
return ast.Global(names=[ast_str(e)],
|
|
|
|
lineno=e.start_line,
|
|
|
|
col_offset=e.start_column)
|
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("lambda")
|
2013-05-04 09:16:01 +02:00
|
|
|
@checkargs(2)
|
2013-03-09 21:57:13 +01:00
|
|
|
def compile_lambda_expression(self, expr):
|
|
|
|
expr.pop(0)
|
|
|
|
sig = expr.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
body = self.compile(expr.pop(0))
|
|
|
|
|
|
|
|
body += ast.Lambda(
|
2013-03-09 21:57:13 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
args=ast.arguments(args=[
|
2013-04-07 03:33:52 +02:00
|
|
|
ast.Name(arg=ast_str(x), id=ast_str(x),
|
2013-03-09 21:57:13 +01:00
|
|
|
ctx=ast.Param(),
|
|
|
|
lineno=x.start_line,
|
|
|
|
col_offset=x.start_column)
|
|
|
|
for x in sig],
|
|
|
|
vararg=None,
|
|
|
|
kwarg=None,
|
|
|
|
defaults=[],
|
|
|
|
kwonlyargs=[],
|
|
|
|
kw_defaults=[]),
|
2013-05-04 09:16:01 +02:00
|
|
|
body=body.force_expr)
|
|
|
|
|
|
|
|
return body
|
2013-03-09 21:57:13 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("yield")
|
2013-04-13 05:48:58 +02:00
|
|
|
@checkargs(max=1)
|
2013-03-15 01:55:11 +01:00
|
|
|
def compile_yield_expression(self, expr):
|
|
|
|
expr.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = Result()
|
|
|
|
|
2013-04-13 05:48:58 +02:00
|
|
|
value = None
|
|
|
|
if expr != []:
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += self.compile(expr.pop(0))
|
|
|
|
value = ret.force_expr
|
|
|
|
|
|
|
|
ret += ast.Yield(
|
2013-04-13 05:48:58 +02:00
|
|
|
value=value,
|
2013-03-15 01:55:11 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("import")
|
2013-03-10 01:46:32 +01:00
|
|
|
def compile_import_expression(self, expr):
|
2013-04-15 03:54:15 +02:00
|
|
|
def _compile_import(expr, module, names=None, importer=ast.Import):
|
2013-05-04 09:16:01 +02:00
|
|
|
if not names:
|
|
|
|
names = [ast.alias(name=ast_str(module), asname=None)]
|
|
|
|
ret = importer(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
module=ast_str(module),
|
|
|
|
names=names,
|
|
|
|
level=0)
|
|
|
|
return Result() + ret
|
Making (import) a lot smarter
With these changes, the import function will become a lot smarter, and
will combine all of import, import-from and import-as in a hyly lispy
syntax:
(import sys os whatever_else)
(import [sys [exit argv]] [os :as real_os]
[whatever_else [some_function :as sf]])
That is, each argument of import can be:
- A plain symbol, which will be imported
- A list, which will be handled specially
If the argument is a list, the first element will always be the module
name to import, the second member can be either of these:
- A list of symbols to import
- The ':as' keyword
- Nothing
If it is the ':as' keyword, the third argument must be an alias. If it
is a list of symbols to import, we'll iterate through that list too. If
any symbol is followed by an ':as' keyword, we'll pick all three, and
treat the third member as an alias. If there is nothing else in the
list, we'll import the module as-is.
All this combined fixes #113.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
2013-04-13 12:50:25 +02:00
|
|
|
|
2013-03-10 01:46:32 +01:00
|
|
|
expr.pop(0) # index
|
2013-05-04 09:16:01 +02:00
|
|
|
rimports = Result()
|
Making (import) a lot smarter
With these changes, the import function will become a lot smarter, and
will combine all of import, import-from and import-as in a hyly lispy
syntax:
(import sys os whatever_else)
(import [sys [exit argv]] [os :as real_os]
[whatever_else [some_function :as sf]])
That is, each argument of import can be:
- A plain symbol, which will be imported
- A list, which will be handled specially
If the argument is a list, the first element will always be the module
name to import, the second member can be either of these:
- A list of symbols to import
- The ':as' keyword
- Nothing
If it is the ':as' keyword, the third argument must be an alias. If it
is a list of symbols to import, we'll iterate through that list too. If
any symbol is followed by an ':as' keyword, we'll pick all three, and
treat the third member as an alias. If there is nothing else in the
list, we'll import the module as-is.
All this combined fixes #113.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
2013-04-13 12:50:25 +02:00
|
|
|
while len(expr) > 0:
|
|
|
|
iexpr = expr.pop(0)
|
|
|
|
|
2013-04-15 03:54:15 +02:00
|
|
|
if isinstance(iexpr, HySymbol):
|
Making (import) a lot smarter
With these changes, the import function will become a lot smarter, and
will combine all of import, import-from and import-as in a hyly lispy
syntax:
(import sys os whatever_else)
(import [sys [exit argv]] [os :as real_os]
[whatever_else [some_function :as sf]])
That is, each argument of import can be:
- A plain symbol, which will be imported
- A list, which will be handled specially
If the argument is a list, the first element will always be the module
name to import, the second member can be either of these:
- A list of symbols to import
- The ':as' keyword
- Nothing
If it is the ':as' keyword, the third argument must be an alias. If it
is a list of symbols to import, we'll iterate through that list too. If
any symbol is followed by an ':as' keyword, we'll pick all three, and
treat the third member as an alias. If there is nothing else in the
list, we'll import the module as-is.
All this combined fixes #113.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
2013-04-13 12:50:25 +02:00
|
|
|
rimports += _compile_import(expr, iexpr)
|
2013-04-15 03:54:15 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
if isinstance(iexpr, HyList) and len(iexpr) == 1:
|
Making (import) a lot smarter
With these changes, the import function will become a lot smarter, and
will combine all of import, import-from and import-as in a hyly lispy
syntax:
(import sys os whatever_else)
(import [sys [exit argv]] [os :as real_os]
[whatever_else [some_function :as sf]])
That is, each argument of import can be:
- A plain symbol, which will be imported
- A list, which will be handled specially
If the argument is a list, the first element will always be the module
name to import, the second member can be either of these:
- A list of symbols to import
- The ':as' keyword
- Nothing
If it is the ':as' keyword, the third argument must be an alias. If it
is a list of symbols to import, we'll iterate through that list too. If
any symbol is followed by an ':as' keyword, we'll pick all three, and
treat the third member as an alias. If there is nothing else in the
list, we'll import the module as-is.
All this combined fixes #113.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
2013-04-13 12:50:25 +02:00
|
|
|
rimports += _compile_import(expr, iexpr.pop(0))
|
2013-04-15 03:54:15 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
if isinstance(iexpr, HyList) and iexpr:
|
Making (import) a lot smarter
With these changes, the import function will become a lot smarter, and
will combine all of import, import-from and import-as in a hyly lispy
syntax:
(import sys os whatever_else)
(import [sys [exit argv]] [os :as real_os]
[whatever_else [some_function :as sf]])
That is, each argument of import can be:
- A plain symbol, which will be imported
- A list, which will be handled specially
If the argument is a list, the first element will always be the module
name to import, the second member can be either of these:
- A list of symbols to import
- The ':as' keyword
- Nothing
If it is the ':as' keyword, the third argument must be an alias. If it
is a list of symbols to import, we'll iterate through that list too. If
any symbol is followed by an ':as' keyword, we'll pick all three, and
treat the third member as an alias. If there is nothing else in the
list, we'll import the module as-is.
All this combined fixes #113.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
2013-04-13 12:50:25 +02:00
|
|
|
module = iexpr.pop(0)
|
2013-04-15 03:54:15 +02:00
|
|
|
entry = iexpr[0]
|
|
|
|
if isinstance(entry, HyKeyword) and entry == HyKeyword(":as"):
|
2013-05-04 09:16:01 +02:00
|
|
|
if not len(iexpr) == 2:
|
|
|
|
raise HyTypeError(iexpr,
|
|
|
|
"garbage after aliased import")
|
2013-04-15 03:54:15 +02:00
|
|
|
iexpr.pop(0) # :as
|
|
|
|
alias = iexpr.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
names = [ast.alias(name=ast_str(module),
|
|
|
|
asname=ast_str(alias))]
|
|
|
|
rimports += _compile_import(expr, ast_str(module), names)
|
2013-04-15 03:54:15 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
if isinstance(entry, HyList):
|
Making (import) a lot smarter
With these changes, the import function will become a lot smarter, and
will combine all of import, import-from and import-as in a hyly lispy
syntax:
(import sys os whatever_else)
(import [sys [exit argv]] [os :as real_os]
[whatever_else [some_function :as sf]])
That is, each argument of import can be:
- A plain symbol, which will be imported
- A list, which will be handled specially
If the argument is a list, the first element will always be the module
name to import, the second member can be either of these:
- A list of symbols to import
- The ':as' keyword
- Nothing
If it is the ':as' keyword, the third argument must be an alias. If it
is a list of symbols to import, we'll iterate through that list too. If
any symbol is followed by an ':as' keyword, we'll pick all three, and
treat the third member as an alias. If there is nothing else in the
list, we'll import the module as-is.
All this combined fixes #113.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
2013-04-13 12:50:25 +02:00
|
|
|
names = []
|
2013-04-15 03:54:15 +02:00
|
|
|
while entry:
|
|
|
|
sym = entry.pop(0)
|
|
|
|
if entry and isinstance(entry[0], HyKeyword):
|
|
|
|
entry.pop(0)
|
|
|
|
alias = ast_str(entry.pop(0))
|
Making (import) a lot smarter
With these changes, the import function will become a lot smarter, and
will combine all of import, import-from and import-as in a hyly lispy
syntax:
(import sys os whatever_else)
(import [sys [exit argv]] [os :as real_os]
[whatever_else [some_function :as sf]])
That is, each argument of import can be:
- A plain symbol, which will be imported
- A list, which will be handled specially
If the argument is a list, the first element will always be the module
name to import, the second member can be either of these:
- A list of symbols to import
- The ':as' keyword
- Nothing
If it is the ':as' keyword, the third argument must be an alias. If it
is a list of symbols to import, we'll iterate through that list too. If
any symbol is followed by an ':as' keyword, we'll pick all three, and
treat the third member as an alias. If there is nothing else in the
list, we'll import the module as-is.
All this combined fixes #113.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
2013-04-13 12:50:25 +02:00
|
|
|
else:
|
|
|
|
alias = None
|
2013-05-04 09:16:01 +02:00
|
|
|
names.append(ast.alias(name=ast_str(sym),
|
|
|
|
asname=alias))
|
2013-04-15 03:54:15 +02:00
|
|
|
|
|
|
|
rimports += _compile_import(expr, module,
|
|
|
|
names, ast.ImportFrom)
|
|
|
|
continue
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
raise HyTypeError(
|
|
|
|
entry,
|
|
|
|
"Unknown entry (`%s`) in the HyList" % (entry)
|
|
|
|
)
|
2013-04-15 03:54:15 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return rimports
|
2013-03-10 01:46:32 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("get")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(2)
|
2013-03-09 06:55:27 +01:00
|
|
|
def compile_index_expression(self, expr):
|
|
|
|
expr.pop(0) # index
|
|
|
|
val = self.compile(expr.pop(0)) # target
|
|
|
|
sli = self.compile(expr.pop(0)) # slice
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return val + sli + ast.Subscript(
|
2013-03-09 06:55:27 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
2013-05-04 09:16:01 +02:00
|
|
|
value=val.force_expr,
|
|
|
|
slice=ast.Index(value=sli.force_expr),
|
2013-03-09 06:55:27 +01:00
|
|
|
ctx=ast.Load())
|
2013-03-19 00:47:48 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("slice")
|
2013-05-04 09:16:01 +02:00
|
|
|
@checkargs(min=1, max=4)
|
2013-03-19 00:47:48 +01:00
|
|
|
def compile_slice_expression(self, expr):
|
|
|
|
expr.pop(0) # index
|
|
|
|
val = self.compile(expr.pop(0)) # target
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
low = Result()
|
2013-03-19 00:47:48 +01:00
|
|
|
if expr != []:
|
|
|
|
low = self.compile(expr.pop(0))
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
high = Result()
|
2013-03-19 00:47:48 +01:00
|
|
|
if expr != []:
|
|
|
|
high = self.compile(expr.pop(0))
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
step = Result()
|
|
|
|
if expr != []:
|
|
|
|
step = self.compile(expr.pop(0))
|
|
|
|
|
|
|
|
# use low.expr, high.expr and step.expr to use a literal `None`.
|
|
|
|
return val + low + high + step + ast.Subscript(
|
2013-03-19 00:47:48 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
2013-05-04 09:16:01 +02:00
|
|
|
value=val.force_expr,
|
|
|
|
slice=ast.Slice(lower=low.expr,
|
|
|
|
upper=high.expr,
|
|
|
|
step=step.expr),
|
2013-03-19 00:47:48 +01:00
|
|
|
ctx=ast.Load())
|
2013-03-09 06:55:27 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("assoc")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(3)
|
2013-03-10 20:39:27 +01:00
|
|
|
def compile_assoc_expression(self, expr):
|
2013-03-10 17:59:16 +01:00
|
|
|
expr.pop(0) # assoc
|
|
|
|
# (assoc foo bar baz) => foo[bar] = baz
|
2013-05-04 09:16:01 +02:00
|
|
|
target = self.compile(expr.pop(0))
|
|
|
|
key = self.compile(expr.pop(0))
|
|
|
|
val = self.compile(expr.pop(0))
|
2013-03-10 17:59:16 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return target + key + val + ast.Assign(
|
2013-03-10 17:59:16 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
targets=[
|
|
|
|
ast.Subscript(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
2013-05-04 09:16:01 +02:00
|
|
|
value=target.force_expr,
|
|
|
|
slice=ast.Index(value=key.force_expr),
|
2013-03-10 17:59:16 +01:00
|
|
|
ctx=ast.Store())],
|
2013-05-04 09:16:01 +02:00
|
|
|
value=val.force_expr)
|
2013-03-10 17:59:16 +01:00
|
|
|
|
2013-04-28 17:14:22 +02:00
|
|
|
@builds("with_decorator")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=1)
|
2013-03-10 03:01:59 +01:00
|
|
|
def compile_decorate_expression(self, expr):
|
2013-04-28 17:14:22 +02:00
|
|
|
expr.pop(0) # with-decorator
|
2013-03-10 03:01:59 +01:00
|
|
|
fn = self.compile(expr.pop(-1))
|
2013-05-04 09:16:01 +02:00
|
|
|
if not fn.stmts or not isinstance(fn.stmts[-1], ast.FunctionDef):
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(expr, "Decorated a non-function")
|
2013-05-04 09:16:01 +02:00
|
|
|
decorators, ret = self._compile_collect(expr)
|
|
|
|
fn.stmts[-1].decorator_list = decorators
|
|
|
|
return ret + fn
|
2013-03-10 03:01:59 +01:00
|
|
|
|
2013-04-08 00:35:36 +02:00
|
|
|
@builds("with")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=2)
|
2013-04-08 00:35:36 +02:00
|
|
|
def compile_with_expression(self, expr):
|
|
|
|
expr.pop(0) # with
|
|
|
|
|
|
|
|
args = expr.pop(0)
|
|
|
|
if len(args) > 2 or len(args) < 1:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(expr, "with needs [arg (expr)] or [(expr)]")
|
2013-04-08 00:35:36 +02:00
|
|
|
|
|
|
|
args.reverse()
|
|
|
|
ctx = self.compile(args.pop(0))
|
|
|
|
|
|
|
|
thing = None
|
|
|
|
if args != []:
|
|
|
|
thing = self._storeize(self.compile(args.pop(0)))
|
2013-03-24 07:04:44 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
body = self._compile_branch(expr)
|
|
|
|
body += body.expr_as_stmt()
|
|
|
|
|
|
|
|
if not body.stmts:
|
|
|
|
body += ast.Pass(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
|
|
|
the_with = ast.With(context_expr=ctx.force_expr,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
optional_vars=thing,
|
|
|
|
body=body.stmts)
|
2013-03-24 15:00:07 +01:00
|
|
|
|
|
|
|
if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
|
2013-05-04 09:16:01 +02:00
|
|
|
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
|
|
|
|
optional_vars=thing)]
|
2013-03-24 07:04:44 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ctx + the_with
|
2013-03-24 07:04:44 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds(",")
|
2013-03-28 01:09:11 +01:00
|
|
|
def compile_tuple(self, expr):
|
|
|
|
expr.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
elts, ret = self._compile_collect(expr)
|
|
|
|
ret += ast.Tuple(elts=elts,
|
2013-03-28 01:09:11 +01:00
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
ctx=ast.Load())
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-28 01:09:11 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("list_comp")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=2, max=3)
|
2013-04-02 04:47:11 +02:00
|
|
|
def compile_list_comprehension(self, expr):
|
2013-04-03 02:46:32 +02:00
|
|
|
# (list-comp expr (target iter) cond?)
|
2013-04-02 04:47:11 +02:00
|
|
|
expr.pop(0)
|
2013-04-03 02:46:32 +02:00
|
|
|
expression = expr.pop(0)
|
|
|
|
tar_it = iter(expr.pop(0))
|
|
|
|
targets = zip(tar_it, tar_it)
|
2013-04-02 04:47:11 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
cond = self.compile(expr.pop(0)) if expr != [] else Result()
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
generator_res = Result()
|
|
|
|
generators = []
|
2013-04-03 02:46:32 +02:00
|
|
|
for target, iterable in targets:
|
2013-05-04 09:16:01 +02:00
|
|
|
comp_target = self.compile(target)
|
|
|
|
target = self._storeize(comp_target)
|
|
|
|
generator_res += self.compile(iterable)
|
|
|
|
generators.append(ast.comprehension(
|
|
|
|
target=target,
|
|
|
|
iter=generator_res.force_expr,
|
2013-04-03 02:46:32 +02:00
|
|
|
ifs=[]))
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
if cond.expr:
|
|
|
|
generators[-1].ifs.append(cond.expr)
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
compiled_expression = self.compile(expression)
|
|
|
|
ret = compiled_expression + generator_res + cond
|
|
|
|
ret += ast.ListComp(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
elt=compiled_expression.force_expr,
|
|
|
|
generators=generators)
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-04-02 04:47:11 +02:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("kwapply")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(2)
|
2013-03-10 03:14:30 +01:00
|
|
|
def compile_kwapply_expression(self, expr):
|
|
|
|
expr.pop(0) # kwapply
|
|
|
|
call = self.compile(expr.pop(0))
|
|
|
|
kwargs = expr.pop(0)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
if type(call.expr) != ast.Call:
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(expr, "kwapplying a non-call")
|
2013-03-10 03:14:30 +01:00
|
|
|
|
2013-04-11 03:44:23 +02:00
|
|
|
if type(kwargs) != HyDict:
|
|
|
|
raise TypeError("kwapplying with a non-dict")
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
keywords = []
|
|
|
|
ret = Result()
|
|
|
|
for x in kwargs:
|
|
|
|
ret += self.compile(kwargs[x])
|
|
|
|
keywords.append(ast.keyword(arg=ast_str(x),
|
|
|
|
value=ret.force_expr))
|
2013-03-10 03:14:30 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
call.expr.keywords = keywords
|
|
|
|
|
|
|
|
return ret + call
|
2013-03-10 03:14:30 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("not")
|
|
|
|
@builds("~")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(1)
|
2013-04-06 10:37:21 +02:00
|
|
|
def compile_unary_operator(self, expression):
|
|
|
|
ops = {"not": ast.Not,
|
|
|
|
"~": ast.Invert}
|
|
|
|
operator = expression.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
operand = self.compile(expression.pop(0))
|
|
|
|
|
|
|
|
operand += ast.UnaryOp(op=ops[operator](),
|
|
|
|
operand=operand.expr,
|
|
|
|
lineno=operator.start_line,
|
|
|
|
col_offset=operator.start_column)
|
|
|
|
return operand
|
2013-04-06 10:37:21 +02:00
|
|
|
|
2013-04-07 23:54:56 +02:00
|
|
|
@builds("and")
|
2013-04-08 00:00:20 +02:00
|
|
|
@builds("or")
|
2013-04-07 23:54:56 +02:00
|
|
|
@checkargs(min=2)
|
2013-04-08 00:00:20 +02:00
|
|
|
def compile_logical_or_and_and_operator(self, expression):
|
|
|
|
ops = {"and": ast.And,
|
|
|
|
"or": ast.Or}
|
2013-04-07 23:54:56 +02:00
|
|
|
operator = expression.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
values, ret = self._compile_collect(expression)
|
|
|
|
|
|
|
|
ret += ast.BoolOp(op=ops[operator](),
|
2013-04-07 23:54:56 +02:00
|
|
|
lineno=operator.start_line,
|
|
|
|
col_offset=operator.start_column,
|
|
|
|
values=values)
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-04-07 23:54:56 +02:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("=")
|
|
|
|
@builds("!=")
|
|
|
|
@builds("<")
|
|
|
|
@builds("<=")
|
|
|
|
@builds(">")
|
|
|
|
@builds(">=")
|
|
|
|
@builds("is")
|
|
|
|
@builds("in")
|
|
|
|
@builds("is_not")
|
|
|
|
@builds("not_in")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=2)
|
2013-03-06 03:42:54 +01:00
|
|
|
def compile_compare_op_expression(self, expression):
|
2013-03-10 03:01:59 +01:00
|
|
|
ops = {"=": ast.Eq, "!=": ast.NotEq,
|
|
|
|
"<": ast.Lt, "<=": ast.LtE,
|
|
|
|
">": ast.Gt, ">=": ast.GtE,
|
2013-04-07 02:02:08 +02:00
|
|
|
"is": ast.Is, "is_not": ast.IsNot,
|
|
|
|
"in": ast.In, "not_in": ast.NotIn}
|
2013-03-06 03:42:54 +01:00
|
|
|
|
|
|
|
inv = expression.pop(0)
|
|
|
|
op = ops[inv]
|
|
|
|
ops = [op() for x in range(1, len(expression))]
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
e = expression[0]
|
|
|
|
exprs, ret = self._compile_collect(expression)
|
|
|
|
|
|
|
|
return ret + ast.Compare(left=exprs[0],
|
|
|
|
ops=ops,
|
|
|
|
comparators=exprs[1:],
|
|
|
|
lineno=e.start_line,
|
|
|
|
col_offset=e.start_column)
|
2013-03-06 03:42:54 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("+")
|
|
|
|
@builds("%")
|
|
|
|
@builds("/")
|
2013-04-11 10:09:15 +02:00
|
|
|
@builds("//")
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("*")
|
2013-04-14 17:37:18 +02:00
|
|
|
@builds("**")
|
|
|
|
@builds("<<")
|
|
|
|
@builds(">>")
|
|
|
|
@builds("|")
|
|
|
|
@builds("^")
|
|
|
|
@builds("&")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=2)
|
2013-03-06 00:28:27 +01:00
|
|
|
def compile_maths_expression(self, expression):
|
|
|
|
ops = {"+": ast.Add,
|
|
|
|
"/": ast.Div,
|
2013-04-11 10:09:15 +02:00
|
|
|
"//": ast.FloorDiv,
|
2013-03-06 00:28:27 +01:00
|
|
|
"*": ast.Mult,
|
2013-03-19 02:46:58 +01:00
|
|
|
"-": ast.Sub,
|
2013-04-14 17:37:18 +02:00
|
|
|
"%": ast.Mod,
|
|
|
|
"**": ast.Pow,
|
|
|
|
"<<": ast.LShift,
|
|
|
|
">>": ast.RShift,
|
|
|
|
"|": ast.BitOr,
|
|
|
|
"^": ast.BitXor,
|
|
|
|
"&": ast.BitAnd}
|
2013-03-06 00:28:27 +01:00
|
|
|
|
|
|
|
inv = expression.pop(0)
|
|
|
|
op = ops[inv]
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(expression.pop(0))
|
2013-03-06 00:28:27 +01:00
|
|
|
for child in expression:
|
2013-05-04 09:16:01 +02:00
|
|
|
left_expr = ret.force_expr
|
|
|
|
ret += self.compile(child)
|
|
|
|
right_expr = ret.force_expr
|
|
|
|
ret += ast.BinOp(left=left_expr,
|
2013-03-06 00:28:27 +01:00
|
|
|
op=op(),
|
2013-05-04 09:16:01 +02:00
|
|
|
right=right_expr,
|
2013-03-06 00:28:27 +01:00
|
|
|
lineno=child.start_line,
|
|
|
|
col_offset=child.start_column)
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-06 00:28:27 +01:00
|
|
|
|
2013-04-16 17:43:40 +02:00
|
|
|
@builds("-")
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_maths_expression_sub(self, expression):
|
|
|
|
if len(expression) > 2:
|
|
|
|
return self.compile_maths_expression(expression)
|
|
|
|
else:
|
|
|
|
arg = expression[1]
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(arg)
|
|
|
|
ret += ast.UnaryOp(op=ast.USub(),
|
|
|
|
operand=ret.force_expr,
|
2013-04-16 17:43:40 +02:00
|
|
|
lineno=arg.start_line,
|
|
|
|
col_offset=arg.start_column)
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-04-16 17:43:40 +02:00
|
|
|
|
2013-04-14 17:30:12 +02:00
|
|
|
@builds("+=")
|
|
|
|
@builds("/=")
|
|
|
|
@builds("//=")
|
|
|
|
@builds("*=")
|
|
|
|
@builds("_=")
|
|
|
|
@builds("%=")
|
|
|
|
@builds("**=")
|
|
|
|
@builds("<<=")
|
|
|
|
@builds(">>=")
|
|
|
|
@builds("|=")
|
|
|
|
@builds("^=")
|
|
|
|
@builds("&=")
|
|
|
|
@checkargs(2)
|
|
|
|
def compile_augassign_expression(self, expression):
|
|
|
|
ops = {"+=": ast.Add,
|
|
|
|
"/=": ast.Div,
|
|
|
|
"//=": ast.FloorDiv,
|
|
|
|
"*=": ast.Mult,
|
|
|
|
"_=": ast.Sub,
|
|
|
|
"%=": ast.Mod,
|
|
|
|
"**=": ast.Pow,
|
|
|
|
"<<=": ast.LShift,
|
|
|
|
">>=": ast.RShift,
|
|
|
|
"|=": ast.BitOr,
|
|
|
|
"^=": ast.BitXor,
|
|
|
|
"&=": ast.BitAnd}
|
|
|
|
|
|
|
|
op = ops[expression[0]]
|
|
|
|
|
|
|
|
target = self._storeize(self.compile(expression[1]))
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(expression[2])
|
2013-04-14 17:30:12 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += ast.AugAssign(
|
2013-04-14 17:30:12 +02:00
|
|
|
target=target,
|
2013-05-04 09:16:01 +02:00
|
|
|
value=ret.force_expr,
|
2013-04-14 17:30:12 +02:00
|
|
|
op=op(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-10 04:04:38 +01:00
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
@builds(HyExpression)
|
|
|
|
def compile_expression(self, expression):
|
2013-03-05 04:35:07 +01:00
|
|
|
fn = expression[0]
|
2013-05-04 09:16:01 +02:00
|
|
|
func = None
|
2013-03-09 22:42:07 +01:00
|
|
|
if isinstance(fn, HyString):
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile_atom(fn, expression)
|
|
|
|
if ret:
|
|
|
|
return ret
|
|
|
|
if fn.startswith("."):
|
|
|
|
# (.split "test test") -> "test test".split()
|
|
|
|
|
|
|
|
# Get the attribute name
|
|
|
|
ofn = fn
|
|
|
|
fn = HySymbol(ofn[1:])
|
|
|
|
fn.replace(ofn)
|
|
|
|
|
|
|
|
# Get the object we want to take an attribute from
|
|
|
|
func = self.compile(expression.pop(1))
|
|
|
|
|
|
|
|
# And get the attribute
|
|
|
|
func += ast.Attribute(lineno=fn.start_line,
|
|
|
|
col_offset=fn.start_column,
|
|
|
|
value=func.force_expr,
|
|
|
|
attr=ast_str(fn),
|
|
|
|
ctx=ast.Load())
|
|
|
|
|
|
|
|
if not func:
|
|
|
|
func = self.compile(fn)
|
|
|
|
args, ret = self._compile_collect(expression[1:])
|
|
|
|
|
|
|
|
ret += ast.Call(func=func.expr,
|
|
|
|
args=args,
|
2013-03-05 02:40:23 +01:00
|
|
|
keywords=[],
|
|
|
|
starargs=None,
|
|
|
|
kwargs=None,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return func + ret
|
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("def")
|
|
|
|
@builds("setf")
|
|
|
|
@builds("setv")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(2)
|
2013-03-06 00:16:04 +01:00
|
|
|
def compile_def_expression(self, expression):
|
2013-05-04 09:16:01 +02:00
|
|
|
expression.pop(0)
|
2013-03-06 00:16:04 +01:00
|
|
|
name = expression.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
result = self.compile(expression.pop(0))
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
if result.temp_variables and isinstance(name, HyString):
|
|
|
|
result.rename(name)
|
|
|
|
return result
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ld_name = self.compile(name)
|
|
|
|
st_name = self._storeize(ld_name)
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
result += ast.Assign(
|
2013-03-06 00:16:04 +01:00
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column,
|
2013-05-04 09:16:01 +02:00
|
|
|
targets=[st_name], value=result.force_expr)
|
|
|
|
|
|
|
|
result += ld_name
|
|
|
|
return result
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("foreach")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=1)
|
2013-03-07 04:09:13 +01:00
|
|
|
def compile_for_expression(self, expression):
|
2013-05-04 09:16:01 +02:00
|
|
|
expression.pop(0) # for
|
|
|
|
target_name, iterable = expression.pop(0)
|
|
|
|
target = self._storeize(self.compile(target_name))
|
|
|
|
|
|
|
|
ret = Result()
|
|
|
|
|
|
|
|
orel = Result()
|
|
|
|
# (foreach [] body (else …))
|
|
|
|
if expression and expression[-1][0] == HySymbol("else"):
|
|
|
|
else_expr = expression.pop()
|
|
|
|
if len(else_expr) > 2:
|
|
|
|
raise HyTypeError(
|
|
|
|
else_expr,
|
|
|
|
"`else' statement in `foreach' is too long")
|
|
|
|
elif len(else_expr) == 2:
|
|
|
|
orel += self.compile(else_expr[1])
|
|
|
|
orel += orel.expr_as_stmt()
|
|
|
|
|
|
|
|
ret += self.compile(iterable)
|
|
|
|
|
|
|
|
body = self._compile_branch(expression)
|
|
|
|
body += body.expr_as_stmt()
|
|
|
|
|
|
|
|
ret += ast.For(lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column,
|
|
|
|
target=target,
|
|
|
|
iter=ret.force_expr,
|
|
|
|
body=body.stmts,
|
|
|
|
orelse=orel.stmts)
|
2013-03-07 04:13:14 +01:00
|
|
|
|
|
|
|
return ret
|
2013-03-07 04:09:13 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("while")
|
2013-04-06 16:33:06 +02:00
|
|
|
@checkargs(min=2)
|
2013-04-03 19:55:09 +02:00
|
|
|
def compile_while_expression(self, expr):
|
|
|
|
expr.pop(0) # "while"
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(expr.pop(0))
|
|
|
|
|
|
|
|
body = self._compile_branch(expr)
|
|
|
|
body += body.expr_as_stmt()
|
2013-04-03 19:55:09 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += ast.While(test=ret.force_expr,
|
|
|
|
body=body.stmts,
|
2013-04-03 19:55:09 +02:00
|
|
|
orelse=[],
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
|
|
|
|
2013-03-06 04:08:53 +01:00
|
|
|
@builds(HyList)
|
2013-05-04 09:16:01 +02:00
|
|
|
def compile_list(self, expression):
|
|
|
|
elts, ret = self._compile_collect(expression)
|
|
|
|
ret += ast.List(elts=elts,
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
return ret
|
2013-03-06 04:08:53 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("fn")
|
2013-04-20 03:31:32 +02:00
|
|
|
@checkargs(min=1)
|
2013-05-04 09:16:01 +02:00
|
|
|
def compile_function_def(self, expression):
|
|
|
|
expression.pop(0)
|
|
|
|
|
|
|
|
name = self.get_anon_fn()
|
|
|
|
|
|
|
|
arglist = expression.pop(0)
|
|
|
|
ret, args, defaults, stararg, kwargs = self._parse_lambda_list(arglist)
|
|
|
|
body = self._compile_branch(expression)
|
|
|
|
if body.expr:
|
|
|
|
body += ast.Return(value=body.expr,
|
|
|
|
lineno=body.expr.lineno,
|
|
|
|
col_offset=body.expr.col_offset)
|
|
|
|
|
|
|
|
if not body.stmts:
|
|
|
|
body += ast.Pass(lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
ret += ast.FunctionDef(name=name,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column,
|
|
|
|
args=ast.arguments(
|
|
|
|
args=[
|
|
|
|
ast.Name(
|
|
|
|
arg=ast_str(x), id=ast_str(x),
|
|
|
|
ctx=ast.Param(),
|
|
|
|
lineno=x.start_line,
|
|
|
|
col_offset=x.start_column)
|
|
|
|
for x in args],
|
|
|
|
vararg=stararg,
|
|
|
|
kwarg=kwargs,
|
|
|
|
kwonlyargs=[],
|
|
|
|
kw_defaults=[],
|
|
|
|
defaults=defaults),
|
|
|
|
body=body.stmts,
|
|
|
|
decorator_list=[])
|
|
|
|
|
|
|
|
ast_name = ast.Name(id=name,
|
|
|
|
arg=name,
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
ret += Result(expr=ast_name, temp_variables=[ast_name, ret.stmts[-1]])
|
2013-03-05 04:35:07 +01:00
|
|
|
|
|
|
|
return ret
|
|
|
|
|
2013-03-06 00:39:34 +01:00
|
|
|
@builds(HyInteger)
|
2013-04-10 14:26:16 +02:00
|
|
|
def compile_integer(self, number):
|
|
|
|
return ast.Num(n=int(number),
|
|
|
|
lineno=number.start_line,
|
|
|
|
col_offset=number.start_column)
|
|
|
|
|
|
|
|
@builds(HyFloat)
|
2013-04-12 15:11:56 +02:00
|
|
|
def compile_float(self, number):
|
2013-04-10 14:26:16 +02:00
|
|
|
return ast.Num(n=float(number),
|
|
|
|
lineno=number.start_line,
|
|
|
|
col_offset=number.start_column)
|
|
|
|
|
|
|
|
@builds(HyComplex)
|
2013-04-12 15:11:56 +02:00
|
|
|
def compile_complex(self, number):
|
2013-04-10 14:26:16 +02:00
|
|
|
return ast.Num(n=complex(number),
|
2013-03-06 00:39:34 +01:00
|
|
|
lineno=number.start_line,
|
|
|
|
col_offset=number.start_column)
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
@builds(HySymbol)
|
|
|
|
def compile_symbol(self, symbol):
|
2013-03-10 01:46:32 +01:00
|
|
|
if "." in symbol:
|
|
|
|
glob, local = symbol.rsplit(".", 1)
|
2013-05-04 09:16:01 +02:00
|
|
|
glob = HySymbol(glob).replace(symbol)
|
|
|
|
ret = self.compile_symbol(glob)
|
2013-03-10 01:46:32 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = ast.Attribute(
|
2013-03-10 01:46:32 +01:00
|
|
|
lineno=symbol.start_line,
|
|
|
|
col_offset=symbol.start_column,
|
2013-05-04 09:16:01 +02:00
|
|
|
value=ret,
|
2013-04-07 03:33:52 +02:00
|
|
|
attr=ast_str(local),
|
2013-03-10 01:46:32 +01:00
|
|
|
ctx=ast.Load()
|
|
|
|
)
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-10 01:46:32 +01:00
|
|
|
|
2013-04-07 03:33:52 +02:00
|
|
|
return ast.Name(id=ast_str(symbol),
|
|
|
|
arg=ast_str(symbol),
|
2013-03-14 14:21:03 +01:00
|
|
|
ctx=ast.Load(),
|
2013-03-05 02:40:23 +01:00
|
|
|
lineno=symbol.start_line,
|
|
|
|
col_offset=symbol.start_column)
|
|
|
|
|
|
|
|
@builds(HyString)
|
|
|
|
def compile_string(self, string):
|
2013-05-04 09:16:01 +02:00
|
|
|
return ast.Str(s=str_type(string),
|
|
|
|
lineno=string.start_line,
|
2013-03-05 02:40:23 +01:00
|
|
|
col_offset=string.start_column)
|
|
|
|
|
2013-04-10 17:44:08 +02:00
|
|
|
@builds(HyKeyword)
|
|
|
|
def compile_keyword(self, keyword):
|
2013-05-04 09:16:01 +02:00
|
|
|
return ast.Str(s=str_type(keyword),
|
|
|
|
lineno=keyword.start_line,
|
2013-04-10 17:44:08 +02:00
|
|
|
col_offset=keyword.start_column)
|
|
|
|
|
2013-03-09 06:55:27 +01:00
|
|
|
@builds(HyDict)
|
|
|
|
def compile_dict(self, m):
|
2013-05-04 09:16:01 +02:00
|
|
|
keyvalues, ret = self._compile_collect(sum(m.items(), ()))
|
2013-04-11 04:51:58 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += ast.Dict(lineno=m.start_line,
|
|
|
|
col_offset=m.start_column,
|
|
|
|
keys=keyvalues[::2],
|
|
|
|
values=keyvalues[1::2])
|
|
|
|
return ret
|
2013-04-11 05:06:55 +02:00
|
|
|
|
2013-04-11 05:00:42 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
def hy_compile(tree, root=ast.Module, get_expr=False):
|
|
|
|
"""
|
|
|
|
Compile a HyObject tree into a Python AST Module.
|
|
|
|
|
|
|
|
If `get_expr` is True, return a tuple (module, last_expression), where
|
|
|
|
`last_expression` is the.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if hasattr(sys, "subversion"):
|
|
|
|
implementation = sys.subversion[0].lower()
|
|
|
|
elif hasattr(sys, "implementation"):
|
|
|
|
implementation = sys.implementation.name.lower()
|
|
|
|
|
|
|
|
body = []
|
|
|
|
expr = None
|
|
|
|
|
|
|
|
if tree:
|
|
|
|
compiler = HyASTCompiler()
|
|
|
|
result = compiler.compile(tree)
|
|
|
|
expr = result.force_expr
|
|
|
|
|
|
|
|
if not get_expr:
|
|
|
|
result += result.expr_as_stmt()
|
|
|
|
|
|
|
|
if isinstance(tree, list):
|
|
|
|
spoof_tree = tree[0]
|
|
|
|
else:
|
|
|
|
spoof_tree = tree
|
|
|
|
body = compiler.imports_as_stmts(spoof_tree) + result.stmts
|
|
|
|
|
|
|
|
ret = root(body=body)
|
|
|
|
|
|
|
|
# PyPy _really_ doesn't like the ast going backwards...
|
|
|
|
if implementation != "cpython":
|
|
|
|
for node in ast.walk(ret):
|
|
|
|
node.lineno = 1
|
|
|
|
node.col_offset = 1
|
|
|
|
|
|
|
|
if get_expr:
|
|
|
|
expr = ast.Expression(body=expr)
|
|
|
|
ret = (ret, expr)
|
2013-04-11 04:51:58 +02:00
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
return ret
|