2013-04-07 17:22:57 +02:00
|
|
|
# -*- encoding: utf-8 -*-
|
2018-01-01 16:38:33 +01:00
|
|
|
# Copyright 2018 the authors.
|
2017-04-27 23:16:57 +02:00
|
|
|
# This file is part of Hy, which is free software licensed under the Expat
|
|
|
|
# license. See the LICENSE.
|
2013-03-03 22:26:17 +01:00
|
|
|
|
2017-03-06 17:51:25 +01:00
|
|
|
from hy.models import (HyObject, HyExpression, HyKeyword, HyInteger, HyComplex,
|
|
|
|
HyString, HyBytes, HySymbol, HyFloat, HyList, HySet,
|
2018-04-15 05:58:52 +02:00
|
|
|
HyDict, HySequence, wrap_value)
|
2018-04-21 21:49:38 +02:00
|
|
|
from hy.model_patterns import (FORM, SYM, STR, sym, brackets, whole, notpexpr,
|
2018-05-09 06:05:45 +02:00
|
|
|
dolike, pexpr, times)
|
2018-04-21 21:49:38 +02:00
|
|
|
from funcparserlib.parser import some, many, oneplus, maybe, NoParseError
|
2013-12-26 17:36:45 +01:00
|
|
|
from hy.errors import HyCompileError, HyTypeError
|
2013-12-26 04:02:20 +01:00
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
from hy.lex.parser import mangle, unmangle
|
2015-08-10 13:17:40 +02:00
|
|
|
|
2013-12-15 17:47:24 +01:00
|
|
|
import hy.macros
|
2015-04-13 05:35:08 +02:00
|
|
|
from hy._compat import (
|
2018-03-13 17:17:09 +01:00
|
|
|
str_type, string_types, bytes_type, long_type, PY3, PY35, PY37,
|
2017-06-23 01:32:29 +02:00
|
|
|
raise_empty)
|
2017-06-21 05:48:54 +02:00
|
|
|
from hy.macros import require, macroexpand, tag_macroexpand
|
2013-07-06 20:00:11 +02:00
|
|
|
import hy.importer
|
2018-02-09 03:46:03 +01:00
|
|
|
import hy.inspect
|
2013-05-16 15:34:14 +02:00
|
|
|
|
2013-12-26 17:36:45 +01:00
|
|
|
import traceback
|
2013-06-29 17:50:31 +02:00
|
|
|
import importlib
|
2013-03-05 02:40:23 +01:00
|
|
|
import ast
|
2013-03-12 01:17:27 +01:00
|
|
|
import sys
|
2017-08-07 22:43:52 +02:00
|
|
|
import copy
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
from collections import defaultdict
|
2017-11-01 01:50:44 +01:00
|
|
|
from cmath import isnan
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2017-02-17 05:14:06 +01:00
|
|
|
if PY3:
|
|
|
|
import builtins
|
|
|
|
else:
|
|
|
|
import __builtin__ as builtins
|
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
Inf = float('inf')
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-06-02 17:21:03 +02:00
|
|
|
_compile_time_ns = {}
|
|
|
|
|
2013-06-19 03:48:42 +02:00
|
|
|
|
2013-06-02 17:21:03 +02:00
|
|
|
def compile_time_ns(module_name):
|
|
|
|
ns = _compile_time_ns.get(module_name)
|
|
|
|
if ns is None:
|
|
|
|
ns = {'hy': hy, '__name__': module_name}
|
|
|
|
_compile_time_ns[module_name] = ns
|
|
|
|
return ns
|
|
|
|
|
|
|
|
|
2013-06-29 17:50:31 +02:00
|
|
|
_stdlib = {}
|
|
|
|
|
|
|
|
|
|
|
|
def load_stdlib():
|
|
|
|
import hy.core
|
|
|
|
for module in hy.core.STDLIB:
|
|
|
|
mod = importlib.import_module(module)
|
2018-02-26 01:44:20 +01:00
|
|
|
for e in map(ast_str, mod.EXPORTS):
|
2017-02-17 05:14:06 +01:00
|
|
|
if getattr(mod, e) is not getattr(builtins, e, ''):
|
|
|
|
# Don't bother putting a name in _stdlib if it
|
|
|
|
# points to a builtin with the same name. This
|
|
|
|
# prevents pointless imports.
|
|
|
|
_stdlib[e] = module
|
2013-06-29 17:50:31 +02:00
|
|
|
|
|
|
|
|
2018-02-27 20:53:23 +01:00
|
|
|
def ast_str(x, piecewise=False):
|
|
|
|
if piecewise:
|
|
|
|
return ".".join(ast_str(s) if s else "" for s in x.split("."))
|
2018-03-04 23:20:46 +01:00
|
|
|
x = mangle(x)
|
2018-02-27 20:53:23 +01:00
|
|
|
return x if PY3 else x.encode('UTF8')
|
2013-04-07 03:33:52 +02:00
|
|
|
|
|
|
|
|
2018-04-21 22:39:49 +02:00
|
|
|
_compile_table = {}
|
|
|
|
_decoratables = (ast.FunctionDef, ast.ClassDef)
|
|
|
|
if PY35:
|
|
|
|
_decoratables += (ast.AsyncFunctionDef,)
|
|
|
|
# _bad_roots are fake special operators, which are used internally
|
|
|
|
# by other special forms (e.g., `except` in `try`) but can't be
|
|
|
|
# used to construct special forms themselves.
|
|
|
|
_bad_roots = tuple(ast_str(x) for x in (
|
|
|
|
"unquote", "unquote-splice", "unpack-mapping", "except"))
|
|
|
|
|
|
|
|
|
2017-08-30 17:47:53 +02:00
|
|
|
def builds(*types, **kwargs):
|
|
|
|
# A decorator that adds the decorated method to _compile_table for
|
|
|
|
# compiling `types`, but only if kwargs['iff'] (if provided) is
|
|
|
|
# true.
|
|
|
|
if not kwargs.get('iff', True):
|
|
|
|
return lambda fn: fn
|
2013-05-11 19:59:19 +02:00
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
def _dec(fn):
|
2017-08-30 17:47:53 +02:00
|
|
|
for t in types:
|
2017-11-16 02:21:01 +01:00
|
|
|
if isinstance(t, string_types):
|
|
|
|
t = ast_str(t)
|
2017-08-30 17:47:53 +02:00
|
|
|
_compile_table[t] = fn
|
2013-04-16 17:53:02 +02:00
|
|
|
return fn
|
2013-03-05 02:40:23 +01:00
|
|
|
return _dec
|
|
|
|
|
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
def special(names, pattern):
|
|
|
|
pattern = whole(pattern)
|
|
|
|
def dec(fn):
|
|
|
|
for name in names if isinstance(names, list) else [names]:
|
|
|
|
if isinstance(name, tuple):
|
|
|
|
condition, name = name
|
|
|
|
if not condition:
|
|
|
|
continue
|
|
|
|
_compile_table[ast_str(name)] = (fn, pattern)
|
|
|
|
return fn
|
|
|
|
return dec
|
|
|
|
|
|
|
|
|
2017-06-23 01:32:29 +02:00
|
|
|
def spoof_positions(obj):
|
2018-04-09 01:05:23 +02:00
|
|
|
if not isinstance(obj, HyObject):
|
2017-06-27 23:09:31 +02:00
|
|
|
return
|
2017-06-23 01:32:29 +02:00
|
|
|
if not hasattr(obj, "start_column"):
|
|
|
|
obj.start_column = 0
|
|
|
|
if not hasattr(obj, "start_line"):
|
|
|
|
obj.start_line = 0
|
|
|
|
if (hasattr(obj, "__iter__") and
|
|
|
|
not isinstance(obj, (string_types, bytes_type))):
|
|
|
|
for x in obj:
|
|
|
|
spoof_positions(x)
|
|
|
|
|
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
# Provide asty.Foo(x, ...) as shorthand for
|
|
|
|
# ast.Foo(..., lineno=x.start_line, col_offset=x.start_column) or
|
|
|
|
# ast.Foo(..., lineno=x.lineno, col_offset=x.col_offset)
|
|
|
|
class Asty(object):
|
|
|
|
def __getattr__(self, name):
|
|
|
|
setattr(Asty, name, lambda self, x, **kwargs: getattr(ast, name)(
|
|
|
|
lineno=getattr(
|
|
|
|
x, 'start_line', getattr(x, 'lineno', None)),
|
|
|
|
col_offset=getattr(
|
|
|
|
x, 'start_column', getattr(x, 'col_offset', None)),
|
|
|
|
**kwargs))
|
|
|
|
return getattr(self, name)
|
|
|
|
asty = Asty()
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
2013-07-14 19:03:08 +02:00
|
|
|
__slots__ = ("imports", "stmts", "temp_variables",
|
|
|
|
"_expr", "__used_expr", "contains_yield")
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
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
|
2013-07-14 19:03:08 +02:00
|
|
|
self.contains_yield = False
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
self.__used_expr = False
|
|
|
|
|
|
|
|
# XXX: Make sure we only have AST where we should.
|
|
|
|
for kwarg in kwargs:
|
2013-07-14 19:03:08 +02:00
|
|
|
if kwarg not in ["imports", "contains_yield", "stmts", "expr",
|
|
|
|
"temp_variables"]:
|
2013-05-04 09:16:01 +02:00
|
|
|
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.
|
|
|
|
"""
|
2013-05-05 15:33:52 +02:00
|
|
|
if self.expr:
|
2013-05-04 09:16:01 +02:00
|
|
|
return self.expr
|
|
|
|
|
2013-05-05 15:33:52 +02:00
|
|
|
# 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"),
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=lineno,
|
|
|
|
col_offset=col_offset)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
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).
|
|
|
|
|
2013-05-08 18:49:07 +02:00
|
|
|
We drop ast.Names if they are appended to statements, as they
|
|
|
|
can't have any side effect. "Bare" names still get converted to
|
|
|
|
statements.
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
If there is no expression context, return an empty result.
|
|
|
|
"""
|
2013-05-08 18:49:07 +02:00
|
|
|
if self.expr and not (isinstance(self.expr, ast.Name) and self.stmts):
|
2017-08-26 20:14:17 +02:00
|
|
|
return Result() + asty.Expr(self.expr, 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
|
2017-12-29 22:40:19 +01:00
|
|
|
elif PY35 and isinstance(var, ast.AsyncFunctionDef):
|
|
|
|
var.name = new_name
|
2013-05-04 09:16:01 +02:00
|
|
|
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
|
2013-07-14 19:03:08 +02:00
|
|
|
result.contains_yield = False
|
|
|
|
if self.contains_yield or other.contains_yield:
|
|
|
|
result.contains_yield = True
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return result
|
|
|
|
|
|
|
|
def __str__(self):
|
2013-07-14 19:03:08 +02:00
|
|
|
return (
|
|
|
|
"Result(imports=[%s], stmts=[%s], "
|
|
|
|
"expr=%s, contains_yield=%s)"
|
|
|
|
) % (
|
2013-05-04 09:16:01 +02:00
|
|
|
", ".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,
|
2013-07-14 19:03:08 +02:00
|
|
|
self.contains_yield
|
2013-05-04 09:16:01 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
2017-08-11 19:02:46 +02:00
|
|
|
def _nargs(n):
|
|
|
|
return "%d argument%s" % (n, ("" if n == 1 else "s"))
|
|
|
|
|
|
|
|
|
2014-05-01 22:30:40 +02:00
|
|
|
def checkargs(exact=None, min=None, max=None, even=None, multiple=None):
|
2013-04-06 16:33:06 +02:00
|
|
|
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(
|
2017-08-11 19:02:46 +02:00
|
|
|
expression, "`%%s' needs %s, got %%d" % _nargs(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,
|
2017-08-11 19:02:46 +02:00
|
|
|
"`%%s' needs at least %s, got %%d." % _nargs(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,
|
2017-08-11 19:02:46 +02:00
|
|
|
"`%%s' needs at most %s, got %%d" % _nargs(max))
|
2013-04-06 16:33:06 +02:00
|
|
|
|
2013-07-16 14:35:57 +02:00
|
|
|
is_even = not((len(expression) - 1) % 2)
|
2013-07-16 16:12:43 +02:00
|
|
|
if even is not None and is_even != even:
|
2013-07-16 14:35:57 +02:00
|
|
|
even_str = "even" if even else "odd"
|
2013-07-16 16:12:43 +02:00
|
|
|
_raise_wrong_args_number(
|
|
|
|
expression,
|
|
|
|
"`%%s' needs an %s number of arguments, got %%d"
|
|
|
|
% (even_str))
|
2013-04-06 16:33:06 +02:00
|
|
|
|
2014-05-01 22:30:40 +02:00
|
|
|
if multiple is not None:
|
|
|
|
if not (len(expression) - 1) in multiple:
|
|
|
|
choices = ", ".join([str(val) for val in multiple[:-1]])
|
|
|
|
choices += " or %s" % multiple[-1]
|
|
|
|
_raise_wrong_args_number(
|
|
|
|
expression,
|
|
|
|
"`%%s' needs %s arguments, got %%d" % choices)
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
return fn(self, expression)
|
|
|
|
|
|
|
|
return checker
|
|
|
|
return _dec
|
|
|
|
|
|
|
|
|
2017-07-17 22:34:39 +02:00
|
|
|
def is_unpack(kind, x):
|
|
|
|
return (isinstance(x, HyExpression)
|
|
|
|
and len(x) > 0
|
|
|
|
and isinstance(x[0], HySymbol)
|
2018-02-26 01:44:20 +01:00
|
|
|
and x[0] == "unpack-" + kind)
|
2017-07-17 22:34:39 +02:00
|
|
|
|
|
|
|
|
2017-09-21 01:34:56 +02:00
|
|
|
def ends_with_else(expr):
|
|
|
|
return (expr and
|
|
|
|
isinstance(expr[-1], HyExpression) and
|
|
|
|
expr[-1] and
|
|
|
|
isinstance(expr[-1][0], HySymbol) and
|
|
|
|
expr[-1][0] == HySymbol("else"))
|
|
|
|
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
class HyASTCompiler(object):
|
|
|
|
|
2013-05-16 15:34:14 +02:00
|
|
|
def __init__(self, module_name):
|
2013-05-04 09:16:01 +02:00
|
|
|
self.anon_var_count = 0
|
|
|
|
self.imports = defaultdict(set)
|
2013-05-16 15:34:14 +02:00
|
|
|
self.module_name = module_name
|
2015-08-25 18:45:20 +02:00
|
|
|
self.temp_if = None
|
2013-06-29 17:50:31 +02:00
|
|
|
if not module_name.startswith("hy.core"):
|
|
|
|
# everything in core needs to be explicit.
|
|
|
|
load_stdlib()
|
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 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():
|
2013-05-11 20:32:35 +02:00
|
|
|
if None in names:
|
2017-06-23 01:32:29 +02:00
|
|
|
e = HyExpression([
|
2013-05-11 20:32:35 +02:00
|
|
|
HySymbol("import"),
|
2013-05-04 09:16:01 +02:00
|
|
|
HySymbol(module),
|
2013-05-11 20:32:35 +02:00
|
|
|
]).replace(expr)
|
2017-06-23 01:32:29 +02:00
|
|
|
spoof_positions(e)
|
|
|
|
ret += self.compile(e)
|
2013-05-11 20:32:35 +02:00
|
|
|
names = sorted(name for name in names if name)
|
|
|
|
if names:
|
2017-06-23 01:32:29 +02:00
|
|
|
e = HyExpression([
|
2013-05-11 20:32:35 +02:00
|
|
|
HySymbol("import"),
|
|
|
|
HyList([
|
|
|
|
HySymbol(module),
|
|
|
|
HyList([HySymbol(name) for name in names])
|
|
|
|
])
|
|
|
|
]).replace(expr)
|
2017-06-23 01:32:29 +02:00
|
|
|
spoof_positions(e)
|
|
|
|
ret += self.compile(e)
|
2013-05-04 09:16:01 +02:00
|
|
|
self.imports = defaultdict(set)
|
|
|
|
return ret.stmts
|
|
|
|
|
|
|
|
def compile_atom(self, atom_type, atom):
|
2017-11-16 02:21:01 +01:00
|
|
|
if isinstance(atom_type, string_types):
|
|
|
|
atom_type = ast_str(atom_type)
|
2018-04-21 22:39:49 +02:00
|
|
|
if atom_type in _bad_roots:
|
|
|
|
raise HyTypeError(atom, "The special form '{}' "
|
|
|
|
"is not allowed here".format(atom_type))
|
2013-05-04 09:16:01 +02:00
|
|
|
if atom_type in _compile_table:
|
2017-08-28 19:49:18 +02:00
|
|
|
# _compile_table[atom_type] is a method for compiling this
|
|
|
|
# type of atom, so call it. If it has an extra parameter,
|
|
|
|
# pass in `atom_type`.
|
2018-02-09 03:46:03 +01:00
|
|
|
atom_compiler = _compile_table[atom_type]
|
2018-04-16 01:09:46 +02:00
|
|
|
if isinstance(atom_compiler, tuple):
|
|
|
|
# This build method has a pattern.
|
|
|
|
build_method, pattern = atom_compiler
|
|
|
|
try:
|
|
|
|
parse_tree = pattern.parse(atom[1:])
|
|
|
|
except NoParseError as e:
|
|
|
|
raise HyTypeError(atom,
|
|
|
|
"parse error for special form '{}': {}'".format(
|
|
|
|
atom[0], str(e)))
|
|
|
|
ret = build_method(self, atom, atom[0], *parse_tree)
|
|
|
|
else:
|
|
|
|
arity = hy.inspect.get_arity(atom_compiler)
|
|
|
|
# Compliation methods may mutate the atom, so copy it first.
|
|
|
|
atom = copy.copy(atom)
|
|
|
|
ret = (atom_compiler(self, atom, atom_type)
|
|
|
|
if arity == 3
|
|
|
|
else atom_compiler(self, atom))
|
2013-05-04 09:16:01 +02:00
|
|
|
if not isinstance(ret, Result):
|
|
|
|
ret = Result() + ret
|
|
|
|
return ret
|
2017-06-27 23:09:31 +02:00
|
|
|
if not isinstance(atom, HyObject):
|
|
|
|
atom = wrap_value(atom)
|
|
|
|
if isinstance(atom, HyObject):
|
|
|
|
spoof_positions(atom)
|
|
|
|
return self.compile_atom(type(atom), atom)
|
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
|
Much better version of new error messages.
This version is much simpler.
At the point that the exception is raised, we don't have access to
the actual source, just the current expression. but as the
exception percolates up, we can intercept it, add the source and
the re-raise it.
Then at the final point, in the cmdline handler, we can choose to
let the entire traceback print, or just the simpler, direct error
message.
And even with the full traceback, the last bit is nicely formatted
just like the shorter, simpler message.
The error message is colored if clint is installed, but to avoid
yet another dependency, you get monochrome without clint.
I'm sure there is a better way to do the markup, the current method
is kludgy but works.
I wish there was more shared code between HyTypeError and LexException
but they are kind of different in some fundamental ways.
This doesn't work (yet) with runtime errors generated from Python,
like NameError, but I have a method that can catch NameError and turn it
into a more pleasing output.
Finally, there is no obvious way to raise HyTypeError from pure Hy code,
so methods in core/language.hy throw ugly TypeError/ValueError.
2013-12-22 20:56:03 +01:00
|
|
|
except HyTypeError as e:
|
|
|
|
raise
|
2013-04-06 16:33:06 +02:00
|
|
|
except Exception as e:
|
2014-11-01 21:00:41 +01:00
|
|
|
raise_empty(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
|
|
|
|
2017-07-17 22:34:39 +02:00
|
|
|
def _compile_collect(self, exprs, with_kwargs=False, dict_display=False,
|
|
|
|
oldpy_unpack=False):
|
2013-05-05 15:33:52 +02:00
|
|
|
"""Collect the expression contexts from a list of compiled expression.
|
|
|
|
|
|
|
|
This returns a list of the expression contexts, and the sum of the
|
|
|
|
Result objects passed as arguments.
|
|
|
|
|
|
|
|
"""
|
|
|
|
compiled_exprs = []
|
|
|
|
ret = Result()
|
2014-12-22 22:52:34 +01:00
|
|
|
keywords = []
|
2017-07-17 22:34:39 +02:00
|
|
|
oldpy_starargs = None
|
|
|
|
oldpy_kwargs = None
|
2014-12-22 22:52:34 +01:00
|
|
|
|
2014-12-23 21:32:03 +01:00
|
|
|
exprs_iter = iter(exprs)
|
2014-12-22 22:52:34 +01:00
|
|
|
for expr in exprs_iter:
|
2017-07-17 22:34:39 +02:00
|
|
|
|
|
|
|
if not PY35 and oldpy_unpack and is_unpack("iterable", expr):
|
|
|
|
if oldpy_starargs:
|
|
|
|
raise HyTypeError(expr, "Pythons < 3.5 allow only one "
|
|
|
|
"`unpack-iterable` per call")
|
|
|
|
oldpy_starargs = self.compile(expr[1])
|
|
|
|
ret += oldpy_starargs
|
|
|
|
oldpy_starargs = oldpy_starargs.force_expr
|
|
|
|
|
|
|
|
elif is_unpack("mapping", expr):
|
|
|
|
ret += self.compile(expr[1])
|
|
|
|
if PY35:
|
|
|
|
if dict_display:
|
|
|
|
compiled_exprs.append(None)
|
|
|
|
compiled_exprs.append(ret.force_expr)
|
|
|
|
elif with_kwargs:
|
2017-08-26 20:14:17 +02:00
|
|
|
keywords.append(asty.keyword(
|
|
|
|
expr, arg=None, value=ret.force_expr))
|
2017-07-17 22:34:39 +02:00
|
|
|
elif oldpy_unpack:
|
|
|
|
if oldpy_kwargs:
|
|
|
|
raise HyTypeError(expr, "Pythons < 3.5 allow only one "
|
|
|
|
"`unpack-mapping` per call")
|
|
|
|
oldpy_kwargs = ret.force_expr
|
|
|
|
|
|
|
|
elif with_kwargs and isinstance(expr, HyKeyword):
|
2014-12-22 22:52:34 +01:00
|
|
|
try:
|
|
|
|
value = next(exprs_iter)
|
|
|
|
except StopIteration:
|
2015-06-15 22:42:02 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"Keyword argument {kw} needs "
|
2018-02-10 04:56:33 +01:00
|
|
|
"a value.".format(kw=expr))
|
2014-12-22 22:52:34 +01:00
|
|
|
|
2018-02-10 04:56:33 +01:00
|
|
|
if not expr:
|
2017-11-19 17:35:20 +01:00
|
|
|
raise HyTypeError(expr, "Can't call a function with the "
|
|
|
|
"empty keyword")
|
2014-12-22 22:52:34 +01:00
|
|
|
|
2018-02-10 04:56:33 +01:00
|
|
|
compiled_value = self.compile(value)
|
|
|
|
ret += compiled_value
|
|
|
|
|
|
|
|
arg = str_type(expr)[1:]
|
2017-08-26 20:14:17 +02:00
|
|
|
keywords.append(asty.keyword(
|
2018-02-10 04:56:33 +01:00
|
|
|
expr, arg=ast_str(arg), value=compiled_value.force_expr))
|
2017-07-17 22:34:39 +02:00
|
|
|
|
2014-12-22 22:52:34 +01:00
|
|
|
else:
|
|
|
|
ret += self.compile(expr)
|
|
|
|
compiled_exprs.append(ret.force_expr)
|
|
|
|
|
2017-07-17 22:34:39 +02:00
|
|
|
if oldpy_unpack:
|
|
|
|
return compiled_exprs, ret, keywords, oldpy_starargs, oldpy_kwargs
|
|
|
|
else:
|
|
|
|
return compiled_exprs, ret, keywords
|
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
|
|
|
|
2015-10-14 19:38:05 +02:00
|
|
|
def _storeize(self, expr, name, func=None):
|
2013-05-04 09:16:01 +02:00
|
|
|
"""Return a new `name` object with an ast.Store() context"""
|
2013-12-21 23:33:14 +01:00
|
|
|
if not func:
|
|
|
|
func = ast.Store
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
if isinstance(name, Result):
|
|
|
|
if not name.is_expr():
|
2015-10-14 19:38:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"Can't assign or delete a non-expression")
|
2013-05-04 09:16:01 +02:00
|
|
|
name = name.expr
|
|
|
|
|
|
|
|
if isinstance(name, (ast.Tuple, ast.List)):
|
|
|
|
typ = type(name)
|
|
|
|
new_elts = []
|
|
|
|
for x in name.elts:
|
2015-10-14 19:38:05 +02:00
|
|
|
new_elts.append(self._storeize(expr, x, func))
|
2013-05-04 09:16:01 +02:00
|
|
|
new_name = typ(elts=new_elts)
|
|
|
|
elif isinstance(name, ast.Name):
|
2017-08-26 20:14:17 +02:00
|
|
|
new_name = ast.Name(id=name.id)
|
2013-05-04 09:16:01 +02:00
|
|
|
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)
|
2017-07-17 22:34:39 +02:00
|
|
|
elif PY3 and isinstance(name, ast.Starred):
|
|
|
|
new_name = ast.Starred(
|
|
|
|
value=self._storeize(expr, name.value, func))
|
2013-05-04 09:16:01 +02:00
|
|
|
else:
|
2015-10-14 19:38:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"Can't assign or delete a %s" %
|
|
|
|
type(expr).__name__)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2013-12-21 23:33:14 +01:00
|
|
|
new_name.ctx = func()
|
2013-05-04 09:16:01 +02:00
|
|
|
ast.copy_location(new_name, name)
|
|
|
|
return new_name
|
2013-04-03 17:39:31 +02:00
|
|
|
|
2013-05-10 23:25:19 +02:00
|
|
|
def _render_quoted_form(self, form, level):
|
2013-05-10 23:11:31 +02:00
|
|
|
"""
|
|
|
|
Render a quoted form as a new HyExpression.
|
|
|
|
|
|
|
|
`level` is the level of quasiquoting of the current form. We can
|
|
|
|
unquote if level is 0.
|
|
|
|
|
|
|
|
Returns a three-tuple (`imports`, `expression`, `splice`).
|
|
|
|
|
|
|
|
The `splice` return value is used to mark `unquote-splice`d forms.
|
|
|
|
We need to distinguish them as want to concatenate them instead of
|
|
|
|
just nesting them.
|
|
|
|
"""
|
|
|
|
if level == 0:
|
|
|
|
if isinstance(form, HyExpression):
|
2018-02-26 01:44:20 +01:00
|
|
|
if form and form[0] in ("unquote", "unquote-splice"):
|
2013-05-10 23:11:31 +02:00
|
|
|
if len(form) != 2:
|
|
|
|
raise HyTypeError(form,
|
|
|
|
("`%s' needs 1 argument, got %s" %
|
|
|
|
form[0], len(form) - 1))
|
2018-02-26 01:44:20 +01:00
|
|
|
return set(), form[1], (form[0] == "unquote-splice")
|
2013-05-10 23:11:31 +02:00
|
|
|
|
|
|
|
if isinstance(form, HyExpression):
|
2013-05-10 23:25:19 +02:00
|
|
|
if form and form[0] == "quasiquote":
|
2013-05-10 23:11:31 +02:00
|
|
|
level += 1
|
2018-02-26 01:44:20 +01:00
|
|
|
if form and form[0] in ("unquote", "unquote-splice"):
|
2013-05-10 23:11:31 +02:00
|
|
|
level -= 1
|
|
|
|
|
2013-04-09 02:18:15 +02:00
|
|
|
name = form.__class__.__name__
|
2013-05-10 23:11:31 +02:00
|
|
|
imports = set([name])
|
2013-04-11 04:51:58 +02:00
|
|
|
|
2018-04-15 05:58:52 +02:00
|
|
|
if isinstance(form, HySequence):
|
2013-05-11 00:26:37 +02:00
|
|
|
if not form:
|
|
|
|
contents = HyList()
|
|
|
|
else:
|
|
|
|
# If there are arguments, they can be spliced
|
|
|
|
# so we build a sum...
|
|
|
|
contents = HyExpression([HySymbol("+"), HyList()])
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
for x in form:
|
2013-05-10 23:11:31 +02:00
|
|
|
f_imports, f_contents, splice = self._render_quoted_form(x,
|
|
|
|
level)
|
|
|
|
imports.update(f_imports)
|
|
|
|
if splice:
|
2017-08-01 17:50:37 +02:00
|
|
|
to_add = HyExpression([
|
|
|
|
HySymbol("list"),
|
|
|
|
HyExpression([HySymbol("or"), f_contents, HyList()])])
|
2013-05-10 23:11:31 +02:00
|
|
|
else:
|
2013-05-11 00:26:37 +02:00
|
|
|
to_add = HyList([f_contents])
|
|
|
|
|
|
|
|
contents.append(to_add)
|
|
|
|
|
|
|
|
return imports, HyExpression([HySymbol(name),
|
|
|
|
contents]).replace(form), False
|
2013-05-10 23:11:31 +02:00
|
|
|
|
2014-06-04 03:36:49 +02:00
|
|
|
elif isinstance(form, HySymbol):
|
2013-05-04 09:16:01 +02:00
|
|
|
return imports, HyExpression([HySymbol(name),
|
2013-05-10 23:11:31 +02:00
|
|
|
HyString(form)]).replace(form), False
|
|
|
|
|
2018-02-10 04:56:33 +01:00
|
|
|
elif isinstance(form, HyKeyword):
|
|
|
|
return imports, form, False
|
|
|
|
|
2017-09-08 20:23:58 +02:00
|
|
|
elif isinstance(form, HyString):
|
|
|
|
x = [HySymbol(name), form]
|
|
|
|
if form.brackets is not None:
|
2018-04-04 05:36:10 +02:00
|
|
|
x.extend([HyKeyword("brackets"), form.brackets])
|
2017-09-08 20:23:58 +02:00
|
|
|
return imports, HyExpression(x).replace(form), False
|
|
|
|
|
2013-05-10 23:11:31 +02:00
|
|
|
return imports, HyExpression([HySymbol(name),
|
|
|
|
form]).replace(form), False
|
2013-04-09 02:18:15 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(["quote", "quasiquote"], [FORM])
|
|
|
|
def compile_quote(self, expr, root, arg):
|
2018-05-09 06:05:45 +02:00
|
|
|
level = Inf if root == "quote" else 0 # Only quasiquotes can unquote
|
2018-04-16 01:09:46 +02:00
|
|
|
imports, stmts, splice = self._render_quoted_form(arg, level)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(stmts)
|
|
|
|
ret.add_imports("hy", imports)
|
|
|
|
return ret
|
2013-04-09 02:18:15 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special("unpack-iterable", [FORM])
|
|
|
|
def compile_unpack_iterable(self, expr, root, arg):
|
2017-07-17 22:34:39 +02:00
|
|
|
if not PY3:
|
|
|
|
raise HyTypeError(expr, "`unpack-iterable` isn't allowed here")
|
2018-04-16 01:09:46 +02:00
|
|
|
ret = self.compile(arg)
|
2017-08-26 20:14:17 +02:00
|
|
|
ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load())
|
2017-07-17 22:34:39 +02:00
|
|
|
return ret
|
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special([(not PY3, "exec*")], [FORM, maybe(FORM), maybe(FORM)])
|
2017-07-25 19:28:42 +02:00
|
|
|
# Under Python 3, `exec` is a function rather than a statement type, so Hy
|
|
|
|
# doesn't need a special form for it.
|
2018-04-16 01:09:46 +02:00
|
|
|
def compile_exec(self, expr, root, body, globals_, locals_):
|
2017-08-26 20:14:17 +02:00
|
|
|
return asty.Exec(
|
|
|
|
expr,
|
2018-04-16 01:09:46 +02:00
|
|
|
body=self.compile(body).force_expr,
|
|
|
|
globals=self.compile(globals_).force_expr if globals_ is not None else None,
|
|
|
|
locals=self.compile(locals_).force_expr if locals_ is not None else None)
|
|
|
|
|
|
|
|
@special("do", [many(FORM)])
|
|
|
|
def compile_do(self, expr, root, body):
|
|
|
|
return self._compile_branch(body)
|
|
|
|
|
|
|
|
@special("raise", [maybe(FORM), maybe(sym(":from") + FORM)])
|
|
|
|
def compile_raise_expression(self, expr, root, exc, cause):
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = Result()
|
2018-04-16 01:09:46 +02:00
|
|
|
if exc is not None:
|
|
|
|
exc = self.compile(exc)
|
|
|
|
ret += exc
|
|
|
|
exc = exc.force_expr
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
if cause is not None:
|
2014-06-09 22:33:11 +02:00
|
|
|
if not PY3:
|
2018-04-16 01:09:46 +02:00
|
|
|
raise HyTypeError(expr, "raise from only supported in python 3")
|
|
|
|
cause = self.compile(cause)
|
|
|
|
ret += cause
|
|
|
|
cause = cause.force_expr
|
2014-05-01 22:30:40 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
ret += asty.Raise(
|
2018-04-16 01:09:46 +02:00
|
|
|
expr, type=ret.expr, exc=exc,
|
2017-08-26 20:14:17 +02:00
|
|
|
inst=None, tback=None, cause=cause)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
return ret
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2018-04-16 20:59:02 +02:00
|
|
|
@special("try",
|
|
|
|
[many(notpexpr("except", "else", "finally")),
|
|
|
|
many(pexpr(sym("except"),
|
|
|
|
brackets() | brackets(FORM) | brackets(SYM, FORM),
|
|
|
|
many(FORM))),
|
|
|
|
maybe(dolike("else")),
|
|
|
|
maybe(dolike("finally"))])
|
|
|
|
def compile_try_expression(self, expr, root, body, catchers, orelse, finalbody):
|
2018-04-30 18:18:01 +02:00
|
|
|
body = self._compile_branch(body)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2018-04-16 20:59:02 +02:00
|
|
|
return_var = asty.Name(
|
|
|
|
expr, id=ast_str(self.get_anon_var()), ctx=ast.Store())
|
2013-05-09 02:00:09 +02:00
|
|
|
|
2017-05-14 00:52:59 +02:00
|
|
|
handler_results = Result()
|
|
|
|
handlers = []
|
2018-04-16 20:59:02 +02:00
|
|
|
for catcher in catchers:
|
|
|
|
handler_results += self._compile_catch_expression(
|
|
|
|
catcher, return_var, *catcher)
|
2017-05-14 00:52:59 +02:00
|
|
|
handlers.append(handler_results.stmts.pop())
|
2018-04-16 20:59:02 +02:00
|
|
|
|
|
|
|
if orelse is None:
|
|
|
|
orelse = []
|
|
|
|
else:
|
|
|
|
orelse = self._compile_branch(orelse)
|
|
|
|
orelse += asty.Assign(expr, targets=[return_var],
|
2017-08-26 20:14:17 +02:00
|
|
|
value=orelse.force_expr)
|
2017-05-14 03:35:12 +02:00
|
|
|
orelse += orelse.expr_as_stmt()
|
|
|
|
orelse = orelse.stmts
|
2018-04-16 20:59:02 +02:00
|
|
|
|
|
|
|
if finalbody is None:
|
|
|
|
finalbody = []
|
|
|
|
else:
|
|
|
|
finalbody = self._compile_branch(finalbody)
|
2017-05-14 03:35:12 +02:00
|
|
|
finalbody += finalbody.expr_as_stmt()
|
|
|
|
finalbody = finalbody.stmts
|
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(
|
2017-05-14 00:52:59 +02:00
|
|
|
expr,
|
2013-04-09 21:19:24 +02:00
|
|
|
"`try' cannot have `else' without `except'")
|
2017-05-26 03:43:31 +02:00
|
|
|
# Likewise a bare (try) or (try BODY).
|
|
|
|
if not (handlers or finalbody):
|
|
|
|
raise HyTypeError(
|
|
|
|
expr,
|
|
|
|
"`try' must have an `except' or `finally' clause")
|
2013-04-08 15:58:43 +02:00
|
|
|
|
2018-04-16 20:59:02 +02:00
|
|
|
returnable = Result(
|
|
|
|
expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()),
|
|
|
|
temp_variables=[return_var],
|
|
|
|
contains_yield=body.contains_yield)
|
2017-08-26 20:14:17 +02:00
|
|
|
body += body.expr_as_stmt() if orelse else asty.Assign(
|
2018-04-16 20:59:02 +02:00
|
|
|
expr, targets=[return_var], value=body.force_expr)
|
2017-08-26 20:14:17 +02:00
|
|
|
body = body.stmts or [asty.Pass(expr)]
|
2017-05-14 03:35:12 +02:00
|
|
|
|
2017-04-14 01:17:32 +02:00
|
|
|
if PY3:
|
2013-04-09 21:19:24 +02:00
|
|
|
# Python 3.3 features a merge of TryExcept+TryFinally into Try.
|
2018-04-16 20:59:02 +02:00
|
|
|
x = asty.Try(
|
2017-08-26 20:14:17 +02:00
|
|
|
expr,
|
2013-04-09 21:19:24 +02:00
|
|
|
body=body,
|
|
|
|
handlers=handlers,
|
|
|
|
orelse=orelse,
|
2018-04-16 20:59:02 +02:00
|
|
|
finalbody=finalbody)
|
|
|
|
elif finalbody and handlers:
|
|
|
|
x = asty.TryFinally(
|
|
|
|
expr,
|
|
|
|
body=[asty.TryExcept(
|
2017-08-26 20:14:17 +02:00
|
|
|
expr,
|
2018-04-16 20:59:02 +02:00
|
|
|
body=body,
|
|
|
|
handlers=handlers,
|
|
|
|
orelse=orelse)],
|
|
|
|
finalbody=finalbody)
|
|
|
|
elif finalbody:
|
|
|
|
x = asty.TryFinally(
|
|
|
|
expr, body=body, finalbody=finalbody)
|
|
|
|
else:
|
|
|
|
x = asty.TryExcept(
|
|
|
|
expr, body=body, handlers=handlers, orelse=orelse)
|
|
|
|
return handler_results + x + returnable
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2018-04-16 20:59:02 +02:00
|
|
|
def _compile_catch_expression(self, expr, var, exceptions, body):
|
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-05-09 02:00:09 +02:00
|
|
|
|
2013-04-07 17:22:57 +02:00
|
|
|
# [variable [list of exceptions]]
|
|
|
|
# let's pop variable and use it as name
|
2017-05-13 15:37:00 +02:00
|
|
|
name = None
|
2013-04-07 17:22:57 +02:00
|
|
|
if len(exceptions) == 2:
|
2018-04-16 20:59:02 +02:00
|
|
|
name = exceptions[0]
|
|
|
|
name = (ast_str(name) if PY3
|
|
|
|
else self._storeize(name, self.compile(name)))
|
2013-04-07 17:22:57 +02:00
|
|
|
|
2018-04-16 20:59:02 +02:00
|
|
|
exceptions_list = exceptions[-1] if exceptions else HyList()
|
|
|
|
if isinstance(exceptions_list, HyList):
|
2013-04-07 17:22:57 +02:00
|
|
|
if len(exceptions_list):
|
|
|
|
# [FooBar BarFoo] → catch Foobar and BarFoo exceptions
|
2014-12-22 22:52:34 +01:00
|
|
|
elts, _type, _ = self._compile_collect(exceptions_list)
|
2018-04-16 20:59:02 +02:00
|
|
|
_type += asty.Tuple(exceptions_list, elts=elts, ctx=ast.Load())
|
2013-04-07 17:22:57 +02:00
|
|
|
else:
|
2015-12-08 14:43:47 +01:00
|
|
|
# [] → all exceptions caught
|
2013-05-04 09:16:01 +02:00
|
|
|
_type = Result()
|
2013-04-07 17:22:57 +02:00
|
|
|
else:
|
2018-04-16 20:59:02 +02:00
|
|
|
_type = self.compile(exceptions_list)
|
2013-04-07 17:22:57 +02:00
|
|
|
|
2018-04-16 20:59:02 +02:00
|
|
|
body = self._compile_branch(body)
|
2017-08-26 20:14:17 +02:00
|
|
|
body += asty.Assign(expr, targets=[var], value=body.force_expr)
|
2013-05-04 09:16:01 +02:00
|
|
|
body += body.expr_as_stmt()
|
2013-03-14 14:21:03 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
# use _type.expr to get a literal `None`
|
2017-08-26 20:14:17 +02:00
|
|
|
return _type + asty.ExceptHandler(
|
2018-04-16 20:59:02 +02:00
|
|
|
expr, type=_type.expr, name=name,
|
|
|
|
body=body.stmts or [asty.Pass(expr)])
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special("if*", [FORM, FORM, maybe(FORM)])
|
|
|
|
def compile_if(self, expr, _, cond, body, orel_expr):
|
|
|
|
cond = self.compile(cond)
|
|
|
|
body = self.compile(body)
|
2015-09-22 19:17:47 +02:00
|
|
|
|
2015-08-25 18:45:20 +02:00
|
|
|
nested = root = False
|
2018-04-16 01:09:46 +02:00
|
|
|
orel = Result()
|
|
|
|
if orel_expr is not None:
|
2015-08-25 18:45:20 +02:00
|
|
|
if isinstance(orel_expr, HyExpression) and isinstance(orel_expr[0],
|
2015-10-14 03:38:15 +02:00
|
|
|
HySymbol) and orel_expr[0] == 'if*':
|
2015-08-25 18:45:20 +02:00
|
|
|
# Nested ifs: don't waste temporaries
|
|
|
|
root = self.temp_if is None
|
|
|
|
nested = True
|
|
|
|
self.temp_if = self.temp_if or self.get_anon_var()
|
|
|
|
orel = self.compile(orel_expr)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2015-09-22 19:17:47 +02:00
|
|
|
if not cond.stmts and isinstance(cond.force_expr, ast.Name):
|
|
|
|
name = cond.force_expr.id
|
|
|
|
branch = None
|
|
|
|
if name == 'True':
|
|
|
|
branch = body
|
|
|
|
elif name in ('False', 'None'):
|
|
|
|
branch = orel
|
|
|
|
if branch is not None:
|
|
|
|
if self.temp_if and branch.stmts:
|
2018-04-16 01:09:46 +02:00
|
|
|
name = asty.Name(expr,
|
2017-08-26 20:14:17 +02:00
|
|
|
id=ast_str(self.temp_if),
|
|
|
|
ctx=ast.Store())
|
2015-09-22 19:17:47 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
branch += asty.Assign(expr,
|
2017-08-26 20:14:17 +02:00
|
|
|
targets=[name],
|
|
|
|
value=body.force_expr)
|
2015-09-22 19:17:47 +02:00
|
|
|
|
|
|
|
return branch
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
# 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
|
2015-08-25 18:45:20 +02:00
|
|
|
var = self.temp_if or self.get_anon_var()
|
2018-04-16 01:09:46 +02:00
|
|
|
name = asty.Name(expr,
|
2017-08-26 20:14:17 +02:00
|
|
|
id=ast_str(var),
|
|
|
|
ctx=ast.Store())
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
# Store the result of the body
|
2018-04-16 01:09:46 +02:00
|
|
|
body += asty.Assign(expr,
|
2017-08-26 20:14:17 +02:00
|
|
|
targets=[name],
|
|
|
|
value=body.force_expr)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
# and of the else clause
|
2015-08-25 18:45:20 +02:00
|
|
|
if not nested or not orel.stmts or (not root and
|
|
|
|
var != self.temp_if):
|
2018-04-16 01:09:46 +02:00
|
|
|
orel += asty.Assign(expr,
|
2017-08-26 20:14:17 +02:00
|
|
|
targets=[name],
|
|
|
|
value=orel.force_expr)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
# Then build the if
|
2018-04-16 01:09:46 +02:00
|
|
|
ret += asty.If(expr,
|
|
|
|
test=ret.force_expr,
|
|
|
|
body=body.stmts,
|
|
|
|
orelse=orel.stmts)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
# And make our expression context our temp variable
|
2018-04-16 01:09:46 +02:00
|
|
|
expr_name = asty.Name(expr, id=ast_str(var), ctx=ast.Load())
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
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
|
2018-04-16 01:09:46 +02:00
|
|
|
ret += asty.IfExp(expr,
|
|
|
|
test=ret.force_expr,
|
|
|
|
body=body.force_expr,
|
|
|
|
orelse=orel.force_expr)
|
2015-08-25 18:45:20 +02:00
|
|
|
|
|
|
|
if root:
|
|
|
|
self.temp_if = None
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-09 05:07:21 +01:00
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
@special(["break", "continue"], [])
|
|
|
|
def compile_break_or_continue_expression(self, expr, root):
|
|
|
|
return (asty.Break if root == "break" else asty.Continue)(expr)
|
|
|
|
|
|
|
|
@special("assert", [FORM, maybe(FORM)])
|
|
|
|
def compile_assert_expression(self, expr, root, test, msg):
|
|
|
|
ret = self.compile(test)
|
2017-08-26 20:14:17 +02:00
|
|
|
e = ret.force_expr
|
2018-04-22 00:13:15 +02:00
|
|
|
if msg is not None:
|
|
|
|
msg = self.compile(msg).force_expr
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret + asty.Assert(expr, test=e, msg=msg)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(["global", (PY3, "nonlocal")], [oneplus(SYM)])
|
|
|
|
def compile_global_or_nonlocal(self, expr, root, syms):
|
|
|
|
node = asty.Global if root == "global" else asty.Nonlocal
|
|
|
|
return node(expr, names=list(map(ast_str, syms)))
|
2013-04-19 08:40:03 +02:00
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
@special("yield", [maybe(FORM)])
|
|
|
|
def compile_yield_expression(self, expr, root, arg):
|
2017-04-14 01:17:32 +02:00
|
|
|
ret = Result(contains_yield=(not PY3))
|
2018-04-22 00:13:15 +02:00
|
|
|
if arg is not None:
|
|
|
|
ret += self.compile(arg)
|
2017-12-30 20:17:26 +01:00
|
|
|
return ret + asty.Yield(expr, value=ret.force_expr)
|
2014-02-23 17:48:33 +01:00
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
@special([(PY3, "yield-from"), (PY35, "await")], [FORM])
|
|
|
|
def compile_yield_from_or_await_expression(self, expr, root, arg):
|
|
|
|
ret = Result() + self.compile(arg)
|
|
|
|
node = asty.YieldFrom if root == "yield-from" else asty.Await
|
2017-12-30 20:17:26 +01:00
|
|
|
return ret + node(expr, value=ret.force_expr)
|
2017-12-29 22:40:19 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special("get", [FORM, oneplus(FORM)])
|
|
|
|
def compile_index_expression(self, expr, name, obj, indices):
|
|
|
|
indices, ret, _ = self._compile_collect(indices)
|
|
|
|
ret += self.compile(obj)
|
2013-12-30 12:04:40 +01:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
for ix in indices:
|
|
|
|
ret += asty.Subscript(
|
|
|
|
expr,
|
|
|
|
value=ret.force_expr,
|
|
|
|
slice=ast.Index(value=ix),
|
2013-12-30 12:04:40 +01:00
|
|
|
ctx=ast.Load())
|
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret
|
2013-03-19 00:47:48 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(".", [FORM, many(SYM | brackets(FORM))])
|
|
|
|
def compile_attribute_access(self, expr, name, invocant, keys):
|
|
|
|
ret = self.compile(invocant)
|
2014-01-09 02:41:49 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
for attr in keys:
|
2014-01-09 02:41:49 +01:00
|
|
|
if isinstance(attr, HySymbol):
|
2017-08-26 20:14:17 +02:00
|
|
|
ret += asty.Attribute(attr,
|
|
|
|
value=ret.force_expr,
|
|
|
|
attr=ast_str(attr),
|
|
|
|
ctx=ast.Load())
|
2018-04-16 01:09:46 +02:00
|
|
|
else: # attr is a HyList
|
2017-08-26 20:14:17 +02:00
|
|
|
compiled_attr = self.compile(attr[0])
|
|
|
|
ret = compiled_attr + ret + asty.Subscript(
|
|
|
|
attr,
|
2014-01-09 02:41:49 +01:00
|
|
|
value=ret.force_expr,
|
|
|
|
slice=ast.Index(value=compiled_attr.force_expr),
|
|
|
|
ctx=ast.Load())
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special("del", [many(FORM)])
|
|
|
|
def compile_del_expression(self, expr, name, args):
|
|
|
|
if not args:
|
|
|
|
return asty.Pass(expr)
|
2015-08-18 08:51:09 +02:00
|
|
|
|
2013-12-21 23:33:44 +01:00
|
|
|
del_targets = []
|
2015-10-14 19:38:05 +02:00
|
|
|
ret = Result()
|
2018-04-16 01:09:46 +02:00
|
|
|
for target in args:
|
2015-10-14 19:38:05 +02:00
|
|
|
compiled_target = self.compile(target)
|
|
|
|
ret += compiled_target
|
|
|
|
del_targets.append(self._storeize(target, compiled_target,
|
|
|
|
ast.Del))
|
2013-12-21 23:33:44 +01:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret + asty.Delete(expr, targets=del_targets)
|
2013-12-21 23:33:44 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special("cut", [FORM, maybe(FORM), maybe(FORM), maybe(FORM)])
|
|
|
|
def compile_cut_expression(self, expr, name, obj, lower, upper, step):
|
|
|
|
ret = [Result()]
|
|
|
|
def c(e):
|
|
|
|
ret[0] += self.compile(e)
|
|
|
|
return ret[0].force_expr
|
2017-08-26 20:14:17 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
s = asty.Subscript(
|
2017-08-26 20:14:17 +02:00
|
|
|
expr,
|
2018-04-16 01:09:46 +02:00
|
|
|
value=c(obj),
|
|
|
|
slice=ast.Slice(lower=c(lower), upper=c(upper), step=c(step)),
|
2013-03-19 00:47:48 +01:00
|
|
|
ctx=ast.Load())
|
2018-04-16 01:09:46 +02:00
|
|
|
return ret[0] + s
|
2013-03-09 06:55:27 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special("with-decorator", [oneplus(FORM)])
|
|
|
|
def compile_decorate_expression(self, expr, name, args):
|
|
|
|
decs, fn = args[:-1], self.compile(args[-1])
|
2017-12-31 06:04:15 +01:00
|
|
|
if not fn.stmts or not isinstance(fn.stmts[-1], _decoratables):
|
2018-04-16 01:09:46 +02:00
|
|
|
raise HyTypeError(args[-1], "Decorated a non-function")
|
|
|
|
decs, ret, _ = self._compile_collect(decs)
|
|
|
|
fn.stmts[-1].decorator_list = decs + fn.stmts[-1].decorator_list
|
2017-03-26 01:15:10 +01:00
|
|
|
return ret + fn
|
2013-03-10 03:01:59 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(["with*", (PY35, "with/a*")],
|
|
|
|
[brackets(FORM, maybe(FORM)), many(FORM)])
|
|
|
|
def compile_with_expression(self, expr, root, args, body):
|
|
|
|
thing, ctx = (None, args[0]) if args[1] is None else args
|
|
|
|
if thing is not None:
|
|
|
|
thing = self._storeize(thing, self.compile(thing))
|
|
|
|
ctx = self.compile(ctx)
|
2013-04-08 00:35:36 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
body = self._compile_branch(body)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2017-08-04 02:38:26 +02:00
|
|
|
# Store the result of the body in a tempvar
|
2013-07-19 00:43:06 +02:00
|
|
|
var = self.get_anon_var()
|
2017-08-26 20:14:17 +02:00
|
|
|
name = asty.Name(expr, id=ast_str(var), ctx=ast.Store())
|
|
|
|
body += asty.Assign(expr, targets=[name], value=body.force_expr)
|
2017-08-19 16:35:41 +02:00
|
|
|
# Initialize the tempvar to None in case the `with` exits
|
|
|
|
# early with an exception.
|
2017-08-26 20:14:17 +02:00
|
|
|
initial_assign = asty.Assign(
|
|
|
|
expr, targets=[name], value=asty.Name(
|
|
|
|
expr, id=ast_str("None"), ctx=ast.Load()))
|
|
|
|
|
2017-12-30 23:25:26 +01:00
|
|
|
node = asty.With if root == "with*" else asty.AsyncWith
|
|
|
|
the_with = node(expr,
|
|
|
|
context_expr=ctx.force_expr,
|
|
|
|
optional_vars=thing,
|
|
|
|
body=body.stmts)
|
2013-03-24 15:00:07 +01:00
|
|
|
|
2017-04-14 01:17:32 +02:00
|
|
|
if PY3:
|
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
|
|
|
|
2017-08-21 23:01:08 +02:00
|
|
|
ret = Result(stmts=[initial_assign]) + ctx + the_with
|
2017-03-08 17:37:45 +01:00
|
|
|
ret.contains_yield = ret.contains_yield or body.contains_yield
|
2013-07-19 00:43:06 +02:00
|
|
|
# And make our expression context our temp variable
|
2017-08-26 20:14:17 +02:00
|
|
|
expr_name = asty.Name(expr, id=ast_str(var), ctx=ast.Load())
|
2013-07-19 00:43:06 +02:00
|
|
|
|
2017-08-19 16:35:41 +02:00
|
|
|
ret += Result(expr=expr_name)
|
|
|
|
# We don't give the Result any temp_vars because we don't want
|
|
|
|
# Result.rename to touch `name`. Otherwise, initial_assign will
|
|
|
|
# clobber any preexisting value of the renamed-to variable.
|
2013-07-19 00:43:06 +02:00
|
|
|
|
|
|
|
return ret
|
2013-03-24 07:04:44 +01:00
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
@special(",", [many(FORM)])
|
|
|
|
def compile_tuple(self, expr, root, args):
|
|
|
|
elts, ret, _ = self._compile_collect(args)
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret + asty.Tuple(expr, elts=elts, ctx=ast.Load())
|
2013-03-28 01:09:11 +01:00
|
|
|
|
2014-01-15 23:55:33 +01:00
|
|
|
def _compile_generator_iterables(self, trailers):
|
|
|
|
"""Helper to compile the "trailing" parts of comprehensions:
|
|
|
|
generators and conditions"""
|
|
|
|
|
|
|
|
generators = trailers.pop(0)
|
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
cond = self.compile(trailers.pop(0)) if trailers else Result()
|
2013-04-02 04:47:11 +02:00
|
|
|
|
2014-01-15 23:55:33 +01:00
|
|
|
gen_it = iter(generators)
|
|
|
|
paired_gens = zip(gen_it, gen_it)
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2014-01-15 23:55:33 +01:00
|
|
|
gen_res = Result()
|
|
|
|
gen = []
|
|
|
|
for target, iterable in paired_gens:
|
|
|
|
gen_res += self.compile(iterable)
|
|
|
|
gen.append(ast.comprehension(
|
2017-08-26 20:14:17 +02:00
|
|
|
target=self._storeize(target, self.compile(target)),
|
2014-01-15 23:55:33 +01:00
|
|
|
iter=gen_res.force_expr,
|
2016-09-22 07:05:53 +02:00
|
|
|
ifs=[],
|
|
|
|
is_async=False))
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
if cond.expr:
|
2014-01-15 23:55:33 +01:00
|
|
|
gen[-1].ifs.append(cond.expr)
|
|
|
|
|
|
|
|
return gen_res + cond, gen
|
|
|
|
|
2018-04-22 00:30:47 +02:00
|
|
|
@special(["list-comp", "set-comp", "genexpr"], [FORM, FORM, maybe(FORM)])
|
|
|
|
def compile_comprehension(self, expr, form, expression, gen, cond):
|
|
|
|
# (list-comp expr [target iter] cond?)
|
2015-08-10 15:55:11 +02:00
|
|
|
|
2018-04-22 00:30:47 +02:00
|
|
|
if not isinstance(gen, HyList):
|
|
|
|
raise HyTypeError(gen, "Generator expression must be a list.")
|
2014-01-15 23:55:33 +01:00
|
|
|
|
2018-04-22 00:30:47 +02:00
|
|
|
gen_res, gen = self._compile_generator_iterables(
|
|
|
|
[gen] + ([] if cond is None else [cond]))
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2015-08-10 15:55:11 +02:00
|
|
|
if len(gen) == 0:
|
2018-04-22 00:30:47 +02:00
|
|
|
raise HyTypeError(expr, "Generator expression cannot be empty.")
|
2015-08-10 15:55:11 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
ret = self.compile(expression)
|
|
|
|
node_class = (
|
2018-02-26 01:44:20 +01:00
|
|
|
asty.ListComp if form == "list-comp" else
|
|
|
|
asty.SetComp if form == "set-comp" else
|
2017-08-26 20:14:17 +02:00
|
|
|
asty.GeneratorExp)
|
|
|
|
return ret + gen_res + node_class(
|
|
|
|
expr, elt=ret.force_expr, generators=gen)
|
2014-01-15 23:55:33 +01:00
|
|
|
|
2018-04-22 00:30:47 +02:00
|
|
|
@special("dict-comp", [FORM, FORM, FORM, maybe(FORM)])
|
|
|
|
def compile_dict_comprehension(self, expr, root, key, value, gen, cond):
|
|
|
|
key = self.compile(key)
|
|
|
|
value = self.compile(value)
|
2014-01-15 23:55:33 +01:00
|
|
|
|
2018-04-22 00:30:47 +02:00
|
|
|
gen_res, gen = self._compile_generator_iterables(
|
|
|
|
[gen] + ([] if cond is None else [cond]))
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
return key + value + gen_res + asty.DictComp(
|
|
|
|
expr,
|
|
|
|
key=key.force_expr,
|
|
|
|
value=value.force_expr,
|
2017-04-14 01:09:45 +02:00
|
|
|
generators=gen)
|
2014-01-15 23:55:33 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(["not", "~"], [FORM])
|
|
|
|
def compile_unary_operator(self, expr, root, arg):
|
2013-04-06 10:37:21 +02:00
|
|
|
ops = {"not": ast.Not,
|
|
|
|
"~": ast.Invert}
|
2018-04-16 01:09:46 +02:00
|
|
|
operand = self.compile(arg)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
operand += asty.UnaryOp(
|
2018-04-16 01:09:46 +02:00
|
|
|
expr, op=ops[root](), operand=operand.force_expr)
|
2017-08-26 20:14:17 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return operand
|
2013-04-06 10:37:21 +02:00
|
|
|
|
2018-04-18 20:59:01 +02:00
|
|
|
@special(["import", "require"], [many(
|
|
|
|
SYM |
|
|
|
|
brackets(SYM, sym(":as"), SYM) |
|
|
|
|
brackets(SYM, brackets(many(SYM + maybe(sym(":as") + SYM)))))])
|
|
|
|
def compile_import_or_require(self, expr, root, entries):
|
2013-05-11 05:43:34 +02:00
|
|
|
"""
|
2018-04-18 20:59:01 +02:00
|
|
|
TODO for `require`: keep track of what we've imported in this run and
|
|
|
|
then "unimport" it after we've completed `thing' so that we don't
|
|
|
|
pollute other envs.
|
2013-05-11 05:43:34 +02:00
|
|
|
"""
|
2018-04-18 20:59:01 +02:00
|
|
|
ret = Result()
|
|
|
|
|
|
|
|
for entry in entries:
|
|
|
|
assignments = "ALL"
|
|
|
|
prefix = ""
|
|
|
|
|
Give `require` the same features as `import` (#1142)
Give `require` the same features as `import`
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list.
Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again.
There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
* Make `require` syntax stricter and add tests
* Update documentation for `require`
* Documentation wording improvements
* Allow :as in `require` name lists
2016-11-03 08:35:58 +01:00
|
|
|
if isinstance(entry, HySymbol):
|
2018-04-18 20:59:01 +02:00
|
|
|
# e.g., (import foo)
|
|
|
|
module, prefix = entry, entry
|
|
|
|
elif isinstance(entry, HyList) and isinstance(entry[1], HySymbol):
|
|
|
|
# e.g., (import [foo :as bar])
|
|
|
|
module, prefix = entry
|
|
|
|
else:
|
|
|
|
# e.g., (import [foo [bar baz :as MyBaz bing]])
|
|
|
|
# or (import [foo [*]])
|
|
|
|
module, kids = entry
|
|
|
|
kids = kids[0]
|
|
|
|
if (HySymbol('*'), None) in kids:
|
|
|
|
if len(kids) != 1:
|
|
|
|
star = kids[kids.index((HySymbol('*'), None))][0]
|
|
|
|
raise HyTypeError(star, "* in an import name list "
|
|
|
|
"must be on its own")
|
Give `require` the same features as `import` (#1142)
Give `require` the same features as `import`
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list.
Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again.
There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
* Make `require` syntax stricter and add tests
* Update documentation for `require`
* Documentation wording improvements
* Allow :as in `require` name lists
2016-11-03 08:35:58 +01:00
|
|
|
else:
|
2018-04-18 20:59:01 +02:00
|
|
|
assignments = [(k, v or k) for k, v in kids]
|
|
|
|
|
|
|
|
if root == HySymbol("import"):
|
|
|
|
ast_module = ast_str(module, piecewise=True)
|
|
|
|
module = ast_module.lstrip(".")
|
|
|
|
level = len(ast_module) - len(module)
|
|
|
|
if assignments == "ALL" and prefix == "":
|
|
|
|
node = asty.ImportFrom
|
|
|
|
names = [ast.alias(name="*", asname=None)]
|
|
|
|
elif assignments == "ALL":
|
|
|
|
node = asty.Import
|
|
|
|
names = [ast.alias(
|
|
|
|
name=ast_module,
|
|
|
|
asname=ast_str(prefix)
|
|
|
|
if prefix and prefix != module
|
|
|
|
else None)]
|
|
|
|
else:
|
|
|
|
node = asty.ImportFrom
|
|
|
|
names = [
|
|
|
|
ast.alias(
|
|
|
|
name=ast_str(k),
|
|
|
|
asname=None if v == k else ast_str(v))
|
|
|
|
for k, v in assignments]
|
|
|
|
ret += node(
|
|
|
|
expr, module=module or None, names=names, level=level)
|
|
|
|
else: # root == HySymbol("require")
|
Give `require` the same features as `import` (#1142)
Give `require` the same features as `import`
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list.
Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again.
There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
* Make `require` syntax stricter and add tests
* Update documentation for `require`
* Documentation wording improvements
* Allow :as in `require` name lists
2016-11-03 08:35:58 +01:00
|
|
|
__import__(module)
|
2018-04-18 20:59:01 +02:00
|
|
|
require(module, self.module_name,
|
|
|
|
assignments=assignments, prefix=prefix)
|
|
|
|
|
|
|
|
return ret
|
2013-05-11 05:43:34 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(["and", "or"], [many(FORM)])
|
|
|
|
def compile_logical_or_and_and_operator(self, expr, operator, args):
|
2015-07-03 00:52:34 +02:00
|
|
|
ops = {"and": (ast.And, "True"),
|
|
|
|
"or": (ast.Or, "None")}
|
|
|
|
opnode, default = ops[operator]
|
2018-04-16 01:09:46 +02:00
|
|
|
if len(args) == 0:
|
2017-08-26 20:14:17 +02:00
|
|
|
return asty.Name(operator, id=default, ctx=ast.Load())
|
2018-04-16 01:09:46 +02:00
|
|
|
elif len(args) == 1:
|
|
|
|
return self.compile(args[0])
|
2015-02-18 22:40:07 +01:00
|
|
|
ret = Result()
|
2018-04-16 01:09:46 +02:00
|
|
|
values = list(map(self.compile, args))
|
2017-08-26 20:14:17 +02:00
|
|
|
if any(value.stmts for value in values):
|
2015-02-18 22:40:07 +01:00
|
|
|
# Compile it to an if...else sequence
|
|
|
|
var = self.get_anon_var()
|
2017-08-26 20:14:17 +02:00
|
|
|
name = asty.Name(operator, id=var, ctx=ast.Store())
|
|
|
|
expr_name = asty.Name(operator, id=var, ctx=ast.Load())
|
2016-12-01 08:49:51 +01:00
|
|
|
temp_variables = [name, expr_name]
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2015-02-18 22:40:07 +01:00
|
|
|
def make_assign(value, node=None):
|
2017-08-26 20:14:17 +02:00
|
|
|
positioned_name = asty.Name(
|
|
|
|
node or operator, id=var, ctx=ast.Store())
|
2016-12-01 08:49:51 +01:00
|
|
|
temp_variables.append(positioned_name)
|
2017-08-26 20:14:17 +02:00
|
|
|
return asty.Assign(
|
|
|
|
node or operator, targets=[positioned_name], value=value)
|
|
|
|
|
|
|
|
current = root = []
|
2015-02-18 22:40:07 +01:00
|
|
|
for i, value in enumerate(values):
|
|
|
|
if value.stmts:
|
|
|
|
node = value.stmts[0]
|
|
|
|
current.extend(value.stmts)
|
|
|
|
else:
|
|
|
|
node = value.expr
|
|
|
|
current.append(make_assign(value.force_expr, value.force_expr))
|
|
|
|
if i == len(values)-1:
|
|
|
|
# Skip a redundant 'if'.
|
|
|
|
break
|
|
|
|
if operator == "and":
|
|
|
|
cond = expr_name
|
|
|
|
elif operator == "or":
|
2017-08-26 20:14:17 +02:00
|
|
|
cond = asty.UnaryOp(node, op=ast.Not(), operand=expr_name)
|
|
|
|
current.append(asty.If(node, test=cond, body=[], orelse=[]))
|
2015-02-18 22:40:07 +01:00
|
|
|
current = current[-1].body
|
|
|
|
ret = sum(root, ret)
|
2016-12-01 08:49:51 +01:00
|
|
|
ret += Result(expr=expr_name, temp_variables=temp_variables)
|
2015-02-18 22:40:07 +01:00
|
|
|
else:
|
2017-08-26 20:14:17 +02:00
|
|
|
ret += asty.BoolOp(operator,
|
|
|
|
op=opnode(),
|
|
|
|
values=[value.force_expr for value in values])
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-04-07 23:54:56 +02:00
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
c_ops = {"=": ast.Eq, "!=": ast.NotEq,
|
|
|
|
"<": ast.Lt, "<=": ast.LtE,
|
|
|
|
">": ast.Gt, ">=": ast.GtE,
|
|
|
|
"is": ast.Is, "is-not": ast.IsNot,
|
|
|
|
"in": ast.In, "not-in": ast.NotIn}
|
|
|
|
c_ops = {ast_str(k): v for k, v in c_ops.items()}
|
2013-03-06 03:42:54 +01:00
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
@special(["=", "is", "<", "<=", ">", ">="], [oneplus(FORM)])
|
|
|
|
@special(["!=", "is-not"], [times(2, Inf, FORM)])
|
|
|
|
@special(["in", "not-in"], [times(2, 2, FORM)])
|
|
|
|
def compile_compare_op_expression(self, expr, root, args):
|
|
|
|
if len(args) == 1:
|
|
|
|
return (self.compile(args[0]) +
|
|
|
|
asty.Name(expr, id="True", ctx=ast.Load()))
|
2013-03-06 03:42:54 +01:00
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
ops = [self.c_ops[ast_str(root)]() for _ in args[1:]]
|
2018-04-16 01:09:46 +02:00
|
|
|
exprs, ret, _ = self._compile_collect(args)
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret + asty.Compare(
|
2018-04-16 01:09:46 +02:00
|
|
|
expr, left=exprs[0], ops=ops, comparators=exprs[1:])
|
2013-03-06 03:42:54 +01:00
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
m_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}
|
|
|
|
if PY35:
|
|
|
|
m_ops["@"] = ast.MatMult
|
|
|
|
|
|
|
|
@special(["+", "*", "|"], [many(FORM)])
|
|
|
|
@special(["-", "/", "&", (PY35, "@")], [oneplus(FORM)])
|
|
|
|
@special(["**", "//", "<<", ">>"], [times(2, Inf, FORM)])
|
|
|
|
@special(["%", "^"], [times(2, 2, FORM)])
|
|
|
|
def compile_maths_expression(self, expr, root, args):
|
|
|
|
root = unmangle(ast_str(root))
|
2013-03-06 00:28:27 +01:00
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
if len(args) == 0:
|
|
|
|
# Return the identity element for this operator.
|
|
|
|
return asty.Num(expr, n=long_type(
|
|
|
|
{"+": 0, "|": 0, "*": 1}[root]))
|
2013-03-06 00:28:27 +01:00
|
|
|
|
2018-05-09 06:05:45 +02:00
|
|
|
if len(args) == 1:
|
|
|
|
if root == "/":
|
|
|
|
# Compute the reciprocal of the argument.
|
|
|
|
args = [HyInteger(1).replace(expr), args[0]]
|
|
|
|
elif root in ("+", "-"):
|
|
|
|
# Apply unary plus or unary minus to the argument.
|
|
|
|
op = {"+": ast.UAdd, "-": ast.USub}[root]()
|
|
|
|
ret = self.compile(args[0])
|
|
|
|
return ret + asty.UnaryOp(expr, op=op, operand=ret.force_expr)
|
|
|
|
else:
|
|
|
|
# Return the argument unchanged.
|
|
|
|
return self.compile(args[0])
|
|
|
|
|
|
|
|
op = self.m_ops[root]
|
|
|
|
right_associative = root == "**"
|
|
|
|
ret = self.compile(args[-1 if right_associative else 0])
|
|
|
|
for child in args[-2 if right_associative else 1 ::
|
|
|
|
-1 if right_associative else 1]:
|
2013-05-04 09:16:01 +02:00
|
|
|
left_expr = ret.force_expr
|
|
|
|
ret += self.compile(child)
|
|
|
|
right_expr = ret.force_expr
|
2017-04-14 02:42:01 +02:00
|
|
|
if right_associative:
|
|
|
|
left_expr, right_expr = right_expr, left_expr
|
2017-08-26 20:14:17 +02:00
|
|
|
ret += asty.BinOp(expr, left=left_expr, op=op(), right=right_expr)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-06 00:28:27 +01:00
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
a_ops = {x + "=": v for x, v in m_ops.items()}
|
2013-04-14 17:30:12 +02:00
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
@special(list(a_ops.keys()), [FORM, FORM])
|
|
|
|
def compile_augassign_expression(self, expr, root, target, value):
|
|
|
|
op = self.a_ops[unmangle(ast_str(root))]
|
|
|
|
target = self._storeize(target, self.compile(target))
|
|
|
|
ret = self.compile(value)
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret + asty.AugAssign(
|
2018-04-22 00:13:15 +02:00
|
|
|
expr, target=target, value=ret.force_expr, op=op())
|
2013-03-10 04:04:38 +01:00
|
|
|
|
2013-06-19 04:57:51 +02:00
|
|
|
@checkargs(1)
|
|
|
|
def _compile_keyword_call(self, expression):
|
|
|
|
expression.append(expression.pop(0))
|
|
|
|
expression.insert(0, HySymbol("get"))
|
|
|
|
return self.compile(expression)
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
@builds(HyExpression)
|
|
|
|
def compile_expression(self, expression):
|
2013-12-26 03:00:24 +01:00
|
|
|
# Perform macro expansions
|
2015-12-23 21:13:18 +01:00
|
|
|
expression = macroexpand(expression, self)
|
2013-12-26 03:00:24 +01:00
|
|
|
if not isinstance(expression, HyExpression):
|
|
|
|
# Go through compile again if the type changed.
|
|
|
|
return self.compile(expression)
|
|
|
|
|
2014-01-23 20:03:01 +01:00
|
|
|
if expression == []:
|
2017-08-28 19:49:18 +02:00
|
|
|
return self.compile_list(expression, HyList)
|
2014-01-23 20:03:01 +01:00
|
|
|
|
2013-03-05 04:35:07 +01:00
|
|
|
fn = expression[0]
|
2013-05-04 09:16:01 +02:00
|
|
|
func = None
|
2013-06-19 04:57:51 +02:00
|
|
|
if isinstance(fn, HyKeyword):
|
|
|
|
return self._compile_keyword_call(expression)
|
|
|
|
|
2017-07-12 19:57:35 +02:00
|
|
|
if isinstance(fn, HySymbol):
|
2017-07-17 22:34:39 +02:00
|
|
|
# First check if `fn` is a special form, unless it has an
|
2018-02-26 01:44:20 +01:00
|
|
|
# `unpack-iterable` in it, since Python's operators (`+`,
|
2017-07-17 22:34:39 +02:00
|
|
|
# etc.) can't unpack. An exception to this exception is that
|
|
|
|
# tuple literals (`,`) can unpack.
|
|
|
|
if fn == "," or not (
|
|
|
|
any(is_unpack("iterable", x) for x in expression[1:])):
|
|
|
|
ret = self.compile_atom(fn, expression)
|
|
|
|
if ret:
|
|
|
|
return ret
|
2013-06-29 17:50:31 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
if fn.startswith("."):
|
|
|
|
# (.split "test test") -> "test test".split()
|
2016-11-07 18:45:25 +01:00
|
|
|
# (.a.b.c x) -> (.c (. x a b)) -> x.a.b.c()
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2016-11-07 18:45:25 +01:00
|
|
|
# Get the method name (the last named attribute
|
|
|
|
# in the chain of attributes)
|
|
|
|
attrs = [HySymbol(a).replace(fn) for a in fn.split(".")[1:]]
|
|
|
|
fn = attrs.pop()
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2016-11-07 18:45:25 +01:00
|
|
|
# Get the object we're calling the method on
|
|
|
|
# (extracted with the attribute access DSL)
|
2017-01-03 08:24:55 +01:00
|
|
|
i = 1
|
|
|
|
if len(expression) != 2:
|
|
|
|
# If the expression has only one object,
|
|
|
|
# always use that as the callee.
|
|
|
|
# Otherwise, hunt for the first thing that
|
|
|
|
# isn't a keyword argument or its value.
|
|
|
|
while i < len(expression):
|
|
|
|
if isinstance(expression[i], HyKeyword):
|
|
|
|
# Skip the keyword argument and its value.
|
|
|
|
i += 1
|
|
|
|
else:
|
|
|
|
# Use expression[i].
|
|
|
|
break
|
|
|
|
i += 1
|
|
|
|
else:
|
|
|
|
raise HyTypeError(expression,
|
|
|
|
"attribute access requires object")
|
2016-11-07 18:45:25 +01:00
|
|
|
func = self.compile(HyExpression(
|
2017-01-03 08:24:55 +01:00
|
|
|
[HySymbol(".").replace(fn), expression.pop(i)] +
|
2016-11-07 18:45:25 +01:00
|
|
|
attrs))
|
|
|
|
|
|
|
|
# And get the method
|
2017-08-26 20:14:17 +02:00
|
|
|
func += asty.Attribute(fn,
|
|
|
|
value=func.force_expr,
|
|
|
|
attr=ast_str(fn),
|
|
|
|
ctx=ast.Load())
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
if not func:
|
|
|
|
func = self.compile(fn)
|
2014-12-22 22:52:34 +01:00
|
|
|
|
|
|
|
# An exception for pulling together keyword args is if we're doing
|
|
|
|
# a typecheck, eg (type :foo)
|
2017-08-26 20:14:17 +02:00
|
|
|
with_kwargs = fn not in (
|
2018-02-10 04:56:33 +01:00
|
|
|
"type", "HyKeyword", "keyword", "name", "keyword?", "identity")
|
2017-08-21 23:01:08 +02:00
|
|
|
args, ret, keywords, oldpy_star, oldpy_kw = self._compile_collect(
|
2017-07-17 22:34:39 +02:00
|
|
|
expression[1:], with_kwargs, oldpy_unpack=True)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
return func + ret + asty.Call(
|
|
|
|
expression, func=func.expr, args=args, keywords=keywords,
|
|
|
|
starargs=oldpy_star, kwargs=oldpy_kw)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special("setv", [many(FORM + FORM)])
|
|
|
|
def compile_def_expression(self, expr, root, pairs):
|
|
|
|
if len(pairs) == 0:
|
2017-08-26 20:14:17 +02:00
|
|
|
return asty.Name(root, id='None', ctx=ast.Load())
|
2018-04-16 01:09:46 +02:00
|
|
|
result = Result()
|
|
|
|
for pair in pairs:
|
|
|
|
result += self._compile_assign(*pair)
|
|
|
|
return result
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
def _compile_assign(self, name, result):
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2014-06-29 19:19:22 +02:00
|
|
|
str_name = "%s" % name
|
2017-11-16 00:43:46 +01:00
|
|
|
if str_name in (["None"] + (["True", "False"] if PY3 else [])):
|
|
|
|
# Python 2 allows assigning to True and False, although
|
|
|
|
# this is rarely wise.
|
2014-06-29 19:19:22 +02:00
|
|
|
raise HyTypeError(name,
|
2017-11-16 00:43:46 +01:00
|
|
|
"Can't assign to `%s'" % str_name)
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2013-04-24 22:18:05 +02:00
|
|
|
result = self.compile(result)
|
2013-05-04 09:16:01 +02:00
|
|
|
ld_name = self.compile(name)
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2015-08-10 14:19:19 +02:00
|
|
|
if isinstance(ld_name.expr, ast.Call):
|
|
|
|
raise HyTypeError(name,
|
|
|
|
"Can't assign to a callable: `%s'" % str_name)
|
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
if (result.temp_variables
|
|
|
|
and isinstance(name, HyString)
|
|
|
|
and '.' not in name):
|
2014-04-29 16:22:55 +02:00
|
|
|
result.rename(name)
|
2017-04-06 23:41:11 +02:00
|
|
|
# Throw away .expr to ensure that (setv ...) returns None.
|
|
|
|
result.expr = None
|
2014-04-29 16:22:55 +02:00
|
|
|
else:
|
2015-10-14 19:38:05 +02:00
|
|
|
st_name = self._storeize(name, ld_name)
|
2017-08-26 20:14:17 +02:00
|
|
|
result += asty.Assign(
|
|
|
|
name if hasattr(name, "start_line") else result,
|
2014-04-29 16:22:55 +02:00
|
|
|
targets=[st_name],
|
|
|
|
value=result.force_expr)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
return result
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(["for*", (PY35, "for/a*")],
|
|
|
|
[brackets(FORM, FORM), many(notpexpr("else")), maybe(dolike("else"))])
|
|
|
|
def compile_for_expression(self, expr, root, args, body, else_expr):
|
|
|
|
target_name, iterable = args
|
2015-10-14 19:38:05 +02:00
|
|
|
target = self._storeize(target_name, self.compile(target_name))
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
ret = Result()
|
|
|
|
|
|
|
|
orel = Result()
|
2018-04-16 01:09:46 +02:00
|
|
|
if else_expr is not None:
|
|
|
|
for else_body in else_expr:
|
2017-09-14 09:37:32 +02:00
|
|
|
orel += self.compile(else_body)
|
2013-05-04 09:16:01 +02:00
|
|
|
orel += orel.expr_as_stmt()
|
|
|
|
|
|
|
|
ret += self.compile(iterable)
|
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
body = self._compile_branch(body)
|
2013-05-04 09:16:01 +02:00
|
|
|
body += body.expr_as_stmt()
|
|
|
|
|
2017-12-30 23:31:06 +01:00
|
|
|
node = asty.For if root == 'for*' else asty.AsyncFor
|
2018-04-16 01:09:46 +02:00
|
|
|
ret += node(expr,
|
2017-12-30 23:31:06 +01:00
|
|
|
target=target,
|
|
|
|
iter=ret.force_expr,
|
2018-04-16 01:09:46 +02:00
|
|
|
body=body.stmts or [asty.Pass(expr)],
|
2017-12-30 23:31:06 +01:00
|
|
|
orelse=orel.stmts)
|
2013-03-07 04:13:14 +01:00
|
|
|
|
2013-07-14 19:03:08 +02:00
|
|
|
ret.contains_yield = body.contains_yield
|
|
|
|
|
2013-03-07 04:13:14 +01:00
|
|
|
return ret
|
2013-03-07 04:09:13 +01:00
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
@special(["while"], [FORM, many(notpexpr("else")), maybe(dolike("else"))])
|
|
|
|
def compile_while_expression(self, expr, root, cond, body, else_expr):
|
2017-11-02 15:31:58 +01:00
|
|
|
cond_compiled = self.compile(cond)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2017-11-02 15:31:58 +01:00
|
|
|
if cond_compiled.stmts:
|
|
|
|
# We need to ensure the statements for the condition are
|
|
|
|
# executed on every iteration. Rewrite the loop to use a
|
|
|
|
# single anonymous variable as the condition.
|
|
|
|
def e(*x): return HyExpression(x)
|
|
|
|
s = HySymbol
|
|
|
|
cond_var = s(self.get_anon_var())
|
|
|
|
return self.compile(e(
|
|
|
|
s('do'),
|
|
|
|
e(s('setv'), cond_var, 1),
|
|
|
|
e(s('while'), cond_var,
|
|
|
|
# Cast the condition to a bool in case it's mutable and
|
|
|
|
# changes its truth value, but use (not (not ...)) instead of
|
|
|
|
# `bool` in case `bool` has been redefined.
|
|
|
|
e(s('setv'), cond_var, e(s('not'), e(s('not'), cond))),
|
2018-04-16 01:09:46 +02:00
|
|
|
e(s('if*'), cond_var, e(s('do'), *body)),
|
|
|
|
*([e(s('else'), *else_expr)] if else_expr is not None else []))).replace(expr)) # noqa
|
2017-11-02 15:31:58 +01:00
|
|
|
|
|
|
|
orel = Result()
|
|
|
|
if else_expr is not None:
|
2018-04-16 01:09:46 +02:00
|
|
|
for else_body in else_expr:
|
2017-09-13 23:31:28 +02:00
|
|
|
orel += self.compile(else_body)
|
|
|
|
orel += orel.expr_as_stmt()
|
|
|
|
|
2018-04-16 01:09:46 +02:00
|
|
|
body = self._compile_branch(body)
|
2013-05-04 09:16:01 +02:00
|
|
|
body += body.expr_as_stmt()
|
2013-04-03 19:55:09 +02:00
|
|
|
|
2017-11-02 15:31:58 +01:00
|
|
|
ret = cond_compiled + asty.While(
|
|
|
|
expr, test=cond_compiled.force_expr,
|
2018-04-16 01:00:36 +02:00
|
|
|
body=body.stmts or [asty.Pass(expr)],
|
|
|
|
orelse=orel.stmts)
|
2013-07-14 19:03:08 +02:00
|
|
|
ret.contains_yield = body.contains_yield
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
|
|
|
|
2018-04-21 21:49:38 +02:00
|
|
|
NASYM = some(lambda x: isinstance(x, HySymbol) and x not in (
|
|
|
|
"&optional", "&rest", "&kwonly", "&kwargs"))
|
|
|
|
@special(["fn", "fn*", (PY35, "fn/a")], [
|
|
|
|
# The starred version is for internal use (particularly, in the
|
|
|
|
# definition of `defn`). It ensures that a FunctionDef is
|
|
|
|
# produced rather than a Lambda.
|
|
|
|
brackets(
|
|
|
|
many(NASYM),
|
|
|
|
maybe(sym("&optional") + many(NASYM | brackets(SYM, FORM))),
|
|
|
|
maybe(sym("&rest") + NASYM),
|
|
|
|
maybe(sym("&kwonly") + many(NASYM | brackets(SYM, FORM))),
|
|
|
|
maybe(sym("&kwargs") + NASYM)),
|
|
|
|
maybe(STR),
|
|
|
|
many(FORM)])
|
|
|
|
def compile_function_def(self, expr, root, params, docstring, body):
|
2017-09-16 22:06:58 +02:00
|
|
|
|
2018-04-21 21:49:38 +02:00
|
|
|
force_functiondef = root in ("fn*", "fn/a")
|
|
|
|
node = asty.AsyncFunctionDef if root == "fn/a" else asty.FunctionDef
|
2013-05-07 19:37:14 +02:00
|
|
|
|
2018-04-21 21:49:38 +02:00
|
|
|
mandatory, optional, rest, kwonly, kwargs = params
|
|
|
|
optional, defaults, ret = self._parse_optional_args(optional)
|
|
|
|
if kwonly is not None and not PY3:
|
|
|
|
raise HyTypeError(params, "&kwonly parameters require Python 3")
|
|
|
|
kwonly, kw_defaults, ret2 = self._parse_optional_args(kwonly, True)
|
|
|
|
ret += ret2
|
|
|
|
main_args = mandatory + optional
|
2017-09-16 22:06:58 +02:00
|
|
|
|
2017-08-26 21:12:06 +02:00
|
|
|
if PY3:
|
2014-05-05 20:17:14 +02:00
|
|
|
# Python 3.4+ requires that args are an ast.arg object, rather
|
2014-01-02 00:56:09 +01:00
|
|
|
# than an ast.Name or bare string.
|
2018-04-21 21:49:38 +02:00
|
|
|
main_args, kwonly, [rest], [kwargs] = (
|
2017-08-26 20:14:17 +02:00
|
|
|
[[x and asty.arg(x, arg=ast_str(x), annotation=None)
|
|
|
|
for x in o]
|
2018-04-21 21:49:38 +02:00
|
|
|
for o in (main_args or [], kwonly or [], [rest], [kwargs])])
|
2014-01-02 00:56:09 +01:00
|
|
|
else:
|
2018-04-21 21:49:38 +02:00
|
|
|
main_args = [asty.Name(x, id=ast_str(x), ctx=ast.Param())
|
|
|
|
for x in main_args]
|
|
|
|
rest = rest and ast_str(rest)
|
|
|
|
kwargs = kwargs and ast_str(kwargs)
|
2015-12-09 01:43:12 +01:00
|
|
|
|
2013-05-07 19:37:14 +02:00
|
|
|
args = ast.arguments(
|
2018-04-21 21:49:38 +02:00
|
|
|
args=main_args, defaults=defaults,
|
|
|
|
vararg=rest,
|
|
|
|
kwonlyargs=kwonly, kw_defaults=kw_defaults,
|
|
|
|
kwarg=kwargs)
|
|
|
|
|
|
|
|
if docstring is not None:
|
|
|
|
if not body:
|
|
|
|
# Reinterpret the docstring as the return value of the
|
|
|
|
# function. Thus, (fn [] "hello") returns "hello" and has no
|
|
|
|
# docstring, instead of returning None and having a docstring
|
|
|
|
# "hello".
|
|
|
|
body = [docstring]
|
|
|
|
docstring = None
|
|
|
|
elif not PY37:
|
|
|
|
# The docstring needs to be represented in the AST as a body
|
|
|
|
# statement.
|
|
|
|
body = [docstring] + body
|
|
|
|
docstring = None
|
|
|
|
body = self._compile_branch(body)
|
|
|
|
|
2018-03-13 17:17:09 +01:00
|
|
|
if not force_functiondef and not body.stmts and docstring is None:
|
2018-04-21 21:49:38 +02:00
|
|
|
return ret + asty.Lambda(expr, args=args, body=body.force_expr)
|
2013-05-07 19:37:14 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
if body.expr:
|
2017-04-14 01:17:32 +02:00
|
|
|
if body.contains_yield and not PY3:
|
2016-01-01 12:55:50 +01:00
|
|
|
# Prior to PEP 380 (introduced in Python 3.3)
|
|
|
|
# generators may not have a value in a return
|
|
|
|
# statement.
|
2013-07-14 19:03:08 +02:00
|
|
|
body += body.expr_as_stmt()
|
|
|
|
else:
|
2017-08-26 20:14:17 +02:00
|
|
|
body += asty.Return(body.expr, value=body.expr)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2017-08-26 19:43:21 +02:00
|
|
|
name = self.get_anon_var()
|
2013-05-07 19:37:14 +02:00
|
|
|
|
2018-04-21 21:49:38 +02:00
|
|
|
ret += node(expr,
|
2017-12-29 22:40:19 +01:00
|
|
|
name=name,
|
|
|
|
args=args,
|
2018-04-21 21:49:38 +02:00
|
|
|
body=body.stmts or [asty.Pass(expr)],
|
2018-03-13 17:17:09 +01:00
|
|
|
decorator_list=[],
|
|
|
|
docstring=(None if docstring is None else
|
|
|
|
str_type(docstring)))
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2018-04-21 21:49:38 +02:00
|
|
|
ast_name = asty.Name(expr, id=name, ctx=ast.Load())
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += Result(expr=ast_name, temp_variables=[ast_name, ret.stmts[-1]])
|
2013-03-05 04:35:07 +01:00
|
|
|
return ret
|
|
|
|
|
2018-04-21 21:49:38 +02:00
|
|
|
def _parse_optional_args(self, expr, allow_no_default=False):
|
|
|
|
# [a b [c 5] d] → ([a, b, c, d], [None, None, 5, d], <ret>)
|
|
|
|
names, defaults, ret = [], [], Result()
|
|
|
|
for x in expr or []:
|
|
|
|
sym, value = (
|
|
|
|
x if isinstance(x, HyList)
|
|
|
|
else (x, None) if allow_no_default
|
|
|
|
else (x, HySymbol('None').replace(x)))
|
|
|
|
names.append(sym)
|
|
|
|
if value is None:
|
|
|
|
defaults.append(None)
|
|
|
|
else:
|
|
|
|
ret += self.compile(value)
|
|
|
|
defaults.append(ret.force_expr)
|
|
|
|
return names, defaults, ret
|
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
@special("return", [maybe(FORM)])
|
|
|
|
def compile_return(self, expr, root, arg):
|
2017-08-11 22:56:44 +02:00
|
|
|
ret = Result()
|
2018-04-22 00:13:15 +02:00
|
|
|
if arg is None:
|
2018-02-08 21:08:09 +01:00
|
|
|
return asty.Return(expr, value=None)
|
2018-04-22 00:13:15 +02:00
|
|
|
ret += self.compile(arg)
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret + asty.Return(expr, value=ret.force_expr)
|
2017-08-11 22:56:44 +02:00
|
|
|
|
2018-04-19 20:19:40 +02:00
|
|
|
@special("defclass", [
|
|
|
|
SYM,
|
|
|
|
maybe(brackets(many(FORM)) + maybe(STR) +
|
|
|
|
maybe(brackets(many(SYM + FORM))) + many(FORM))])
|
|
|
|
def compile_class_expression(self, expr, root, name, rest):
|
|
|
|
base_list, docstring, attrs, body = rest or ([[]], None, None, [])
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2018-04-19 20:19:40 +02:00
|
|
|
bases_expr, bases, keywords = (
|
|
|
|
self._compile_collect(base_list[0], with_kwargs=PY3))
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2018-04-19 20:19:40 +02:00
|
|
|
bodyr = Result()
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2018-04-19 20:19:40 +02:00
|
|
|
if docstring is not None:
|
2018-03-24 20:19:29 +01:00
|
|
|
if not PY37:
|
2018-04-19 20:19:40 +02:00
|
|
|
bodyr += self.compile(docstring).expr_as_stmt()
|
2018-03-24 20:19:29 +01:00
|
|
|
docstring = None
|
2013-07-15 01:25:57 +02:00
|
|
|
|
2018-04-19 20:19:40 +02:00
|
|
|
if attrs is not None:
|
|
|
|
bodyr += self.compile(self._rewire_init(HyExpression(
|
|
|
|
[HySymbol("setv")] +
|
|
|
|
[x for pair in attrs[0] for x in pair]).replace(attrs)))
|
2015-08-04 16:43:07 +02:00
|
|
|
|
2018-04-19 20:19:40 +02:00
|
|
|
for e in body:
|
|
|
|
e = self.compile(self._rewire_init(macroexpand(e, self)))
|
|
|
|
bodyr += e + e.expr_as_stmt()
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
return bases + asty.ClassDef(
|
2018-04-19 20:19:40 +02:00
|
|
|
expr,
|
2013-04-24 22:18:05 +02:00
|
|
|
decorator_list=[],
|
2018-04-19 20:19:40 +02:00
|
|
|
name=ast_str(name),
|
2018-02-12 00:26:29 +01:00
|
|
|
keywords=keywords,
|
2013-04-24 22:18:05 +02:00
|
|
|
starargs=None,
|
|
|
|
kwargs=None,
|
|
|
|
bases=bases_expr,
|
2018-04-19 20:19:40 +02:00
|
|
|
body=bodyr.stmts or [asty.Pass(expr)],
|
2018-03-24 20:19:29 +01:00
|
|
|
docstring=(None if docstring is None else str_type(docstring)))
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2018-04-19 20:19:40 +02:00
|
|
|
def _rewire_init(self, expr):
|
|
|
|
"Given a (setv …) form, append None to definitions of __init__."
|
|
|
|
|
|
|
|
if not (isinstance(expr, HyExpression)
|
|
|
|
and len(expr) > 1
|
|
|
|
and isinstance(expr[0], HySymbol)
|
|
|
|
and expr[0] == HySymbol("setv")):
|
|
|
|
return expr
|
|
|
|
|
|
|
|
new_args = []
|
|
|
|
pairs = list(expr[1:])
|
|
|
|
while pairs:
|
|
|
|
k, v = (pairs.pop(0), pairs.pop(0))
|
|
|
|
if ast_str(k) == "__init__" and isinstance(v, HyExpression):
|
|
|
|
v += HyExpression([HySymbol("None")])
|
|
|
|
new_args.extend([k, v])
|
|
|
|
return HyExpression([HySymbol("setv")] + new_args).replace(expr)
|
|
|
|
|
2018-04-22 00:13:15 +02:00
|
|
|
@special("dispatch-tag-macro", [STR, FORM])
|
|
|
|
def compile_dispatch_tag_macro(self, expr, root, tag, arg):
|
|
|
|
return self.compile(tag_macroexpand(
|
|
|
|
HyString(mangle(tag)).replace(tag),
|
|
|
|
arg,
|
|
|
|
self))
|
|
|
|
|
|
|
|
@special(["eval-and-compile", "eval-when-compile"], [many(FORM)])
|
|
|
|
def compile_eval_and_compile(self, expr, root, body):
|
|
|
|
new_expr = HyExpression([HySymbol("do").replace(root)]).replace(expr)
|
|
|
|
hy.importer.hy_eval(new_expr + body,
|
2013-06-02 17:21:03 +02:00
|
|
|
compile_time_ns(self.module_name),
|
|
|
|
self.module_name)
|
2018-04-22 00:13:15 +02:00
|
|
|
return (self._compile_branch(body)
|
|
|
|
if ast_str(root) == "eval_and_compile"
|
2017-09-20 01:35:33 +02:00
|
|
|
else Result())
|
2013-06-05 11:41:58 +02:00
|
|
|
|
2017-08-30 17:47:53 +02:00
|
|
|
@builds(HyInteger, HyFloat, HyComplex)
|
2017-11-01 01:50:44 +01:00
|
|
|
def compile_numeric_literal(self, x, building):
|
2017-08-28 19:49:18 +02:00
|
|
|
f = {HyInteger: long_type,
|
|
|
|
HyFloat: float,
|
|
|
|
HyComplex: complex}[building]
|
2017-11-01 01:50:44 +01:00
|
|
|
# Work around https://github.com/berkerpeksag/astor/issues/85 :
|
|
|
|
# astor can't generate Num nodes with NaN, so we have
|
|
|
|
# to build an expression that evaluates to NaN.
|
|
|
|
def nn(number):
|
|
|
|
return asty.Num(x, n=number)
|
|
|
|
if isnan(x):
|
|
|
|
def nan(): return asty.BinOp(
|
|
|
|
x, left=nn(1e900), op=ast.Sub(), right=nn(1e900))
|
|
|
|
if f is complex:
|
|
|
|
return asty.Call(
|
|
|
|
x,
|
|
|
|
func=asty.Name(x, id="complex", ctx=ast.Load()),
|
|
|
|
keywords=[],
|
|
|
|
args=[nan() if isnan(x.real) else nn(x.real),
|
|
|
|
nan() if isnan(x.imag) else nn(x.imag)])
|
|
|
|
return nan()
|
|
|
|
return nn(f(x))
|
2013-03-06 00:39:34 +01:00
|
|
|
|
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)
|
2016-11-30 06:00:48 +01:00
|
|
|
|
|
|
|
if not glob:
|
|
|
|
raise HyTypeError(symbol, 'cannot access attribute on '
|
|
|
|
'anything other than a name '
|
2017-12-24 20:02:23 +01:00
|
|
|
'(in order to get attributes of '
|
2016-11-30 06:00:48 +01:00
|
|
|
'expressions, use '
|
|
|
|
'`(. <expression> {attr})` or '
|
|
|
|
'`(.{attr} <expression>)`)'.format(
|
|
|
|
attr=local))
|
|
|
|
|
|
|
|
if not local:
|
|
|
|
raise HyTypeError(symbol, 'cannot access empty attribute')
|
|
|
|
|
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
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
return asty.Attribute(
|
|
|
|
symbol,
|
2013-05-04 09:16:01 +02:00
|
|
|
value=ret,
|
2013-04-07 03:33:52 +02:00
|
|
|
attr=ast_str(local),
|
2017-08-26 20:14:17 +02:00
|
|
|
ctx=ast.Load())
|
2013-03-10 01:46:32 +01:00
|
|
|
|
2018-02-26 01:44:20 +01:00
|
|
|
if ast_str(symbol) in _stdlib:
|
2018-02-27 20:53:23 +01:00
|
|
|
self.imports[_stdlib[ast_str(symbol)]].add(ast_str(symbol))
|
2013-07-08 04:36:26 +02:00
|
|
|
|
2017-08-26 20:14:17 +02:00
|
|
|
return asty.Name(symbol, id=ast_str(symbol), ctx=ast.Load())
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2018-02-10 04:56:33 +01:00
|
|
|
@builds(HyKeyword)
|
2018-04-04 05:36:10 +02:00
|
|
|
def compile_keyword(self, obj):
|
2018-02-10 04:56:33 +01:00
|
|
|
ret = Result()
|
|
|
|
ret += asty.Call(
|
2018-04-04 05:36:10 +02:00
|
|
|
obj,
|
|
|
|
func=asty.Name(obj, id="HyKeyword", ctx=ast.Load()),
|
|
|
|
args=[asty.Str(obj, s=obj.name)],
|
2018-02-10 04:56:33 +01:00
|
|
|
keywords=[])
|
|
|
|
ret.add_imports("hy", {"HyKeyword"})
|
|
|
|
return ret
|
|
|
|
|
|
|
|
@builds(HyString, HyBytes)
|
2017-08-28 19:49:18 +02:00
|
|
|
def compile_string(self, string, building):
|
|
|
|
node = asty.Bytes if PY3 and building is HyBytes else asty.Str
|
|
|
|
f = bytes_type if building is HyBytes else str_type
|
|
|
|
return node(string, s=f(string))
|
|
|
|
|
2017-08-30 17:47:53 +02:00
|
|
|
@builds(HyList, HySet)
|
2017-08-28 19:49:18 +02:00
|
|
|
def compile_list(self, expression, building):
|
|
|
|
elts, ret, _ = self._compile_collect(expression)
|
|
|
|
node = {HyList: asty.List, HySet: asty.Set}[building]
|
|
|
|
return ret + node(expression, elts=elts, ctx=ast.Load())
|
2013-04-10 17:44:08 +02:00
|
|
|
|
2013-03-09 06:55:27 +01:00
|
|
|
@builds(HyDict)
|
|
|
|
def compile_dict(self, m):
|
2017-07-17 22:34:39 +02:00
|
|
|
keyvalues, ret, _ = self._compile_collect(m, dict_display=True)
|
2017-08-26 20:14:17 +02:00
|
|
|
return ret + asty.Dict(m, keys=keyvalues[::2], values=keyvalues[1::2])
|
2013-04-11 05:06:55 +02:00
|
|
|
|
2013-04-11 05:00:42 +02:00
|
|
|
|
2013-05-16 15:34:14 +02:00
|
|
|
def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
|
2013-05-04 09:16:01 +02:00
|
|
|
"""
|
|
|
|
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.
|
|
|
|
"""
|
|
|
|
|
|
|
|
body = []
|
|
|
|
expr = None
|
|
|
|
|
2017-06-23 01:32:29 +02:00
|
|
|
if not isinstance(tree, HyObject):
|
2017-06-27 23:09:31 +02:00
|
|
|
tree = wrap_value(tree)
|
|
|
|
if not isinstance(tree, HyObject):
|
|
|
|
raise HyCompileError("`tree` must be a HyObject or capable of "
|
|
|
|
"being promoted to one")
|
|
|
|
spoof_positions(tree)
|
2017-03-06 17:51:25 +01:00
|
|
|
|
2017-06-23 01:32:29 +02:00
|
|
|
compiler = HyASTCompiler(module_name)
|
|
|
|
result = compiler.compile(tree)
|
|
|
|
expr = result.force_expr
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2017-06-23 01:32:29 +02:00
|
|
|
if not get_expr:
|
|
|
|
result += result.expr_as_stmt()
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2018-03-13 21:29:41 +01:00
|
|
|
module_docstring = None
|
|
|
|
if (PY37 and result.stmts and
|
|
|
|
isinstance(result.stmts[0], ast.Expr) and
|
|
|
|
isinstance(result.stmts[0].value, ast.Str)):
|
|
|
|
module_docstring = result.stmts.pop(0).value.s
|
|
|
|
|
2017-06-23 01:32:29 +02:00
|
|
|
body = compiler.imports_as_stmts(tree) + result.stmts
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2018-03-13 21:29:41 +01:00
|
|
|
ret = root(body=body, docstring=(
|
|
|
|
None if module_docstring is None else module_docstring))
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
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
|