2013-04-07 17:22:57 +02:00
|
|
|
# -*- encoding: utf-8 -*-
|
2017-04-27 23:16:57 +02:00
|
|
|
# Copyright 2017 the authors.
|
|
|
|
# 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,
|
2017-06-27 23:09:31 +02:00
|
|
|
HyDict, HyCons, wrap_value)
|
2013-12-26 17:36:45 +01:00
|
|
|
from hy.errors import HyCompileError, HyTypeError
|
2013-12-26 04:02:20 +01:00
|
|
|
|
2015-08-10 13:17:40 +02:00
|
|
|
from hy.lex.parser import hy_symbol_mangle
|
|
|
|
|
2013-12-15 17:47:24 +01:00
|
|
|
import hy.macros
|
2015-04-13 05:35:08 +02:00
|
|
|
from hy._compat import (
|
2017-06-23 01:32:29 +02:00
|
|
|
str_type, string_types, bytes_type, long_type, PY3, PY34, PY35,
|
|
|
|
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
|
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
|
|
|
|
import codecs
|
2013-03-05 02:40:23 +01:00
|
|
|
import ast
|
2013-03-12 01:17:27 +01:00
|
|
|
import sys
|
2014-06-29 19:19:22 +02:00
|
|
|
import keyword
|
2013-03-05 02:40:23 +01:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
from collections import defaultdict
|
|
|
|
|
2017-02-17 05:14:06 +01:00
|
|
|
if PY3:
|
|
|
|
import builtins
|
|
|
|
else:
|
|
|
|
import __builtin__ as builtins
|
|
|
|
|
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)
|
|
|
|
for e in 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
|
|
|
|
|
|
|
|
2014-06-29 19:19:22 +02:00
|
|
|
# True, False and None included here since they
|
|
|
|
# are assignable in Python 2.* but become
|
|
|
|
# keywords in Python 3.*
|
|
|
|
def _is_hy_builtin(name, module_name):
|
2016-11-24 03:35:17 +01:00
|
|
|
extras = ['True', 'False', 'None']
|
2014-06-29 19:19:22 +02:00
|
|
|
if name in extras or keyword.iskeyword(name):
|
|
|
|
return True
|
|
|
|
# for non-Hy modules, check for pre-existing name in
|
|
|
|
# _compile_table
|
|
|
|
if not module_name.startswith("hy."):
|
|
|
|
return name in _compile_table
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
_compile_table = {}
|
|
|
|
|
|
|
|
|
2013-04-07 03:33:52 +02:00
|
|
|
def ast_str(foobar):
|
2014-01-04 02:02:36 +01:00
|
|
|
if PY3:
|
2013-04-07 03:33:52 +02:00
|
|
|
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):
|
2013-05-11 19:59:19 +02:00
|
|
|
|
|
|
|
unpythonic_chars = ["-"]
|
|
|
|
really_ok = ["-"]
|
2013-05-13 18:44:50 +02:00
|
|
|
if any(x in unpythonic_chars for x in str_type(_type)):
|
2013-05-11 19:59:19 +02:00
|
|
|
if _type not in really_ok:
|
2014-12-22 18:01:32 +01:00
|
|
|
raise TypeError("Dear Hypster: `build' needs to be *post* "
|
|
|
|
"translated strings... `%s' sucks." % (_type))
|
2013-05-11 19:59:19 +02:00
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2015-04-13 05:35:08 +02:00
|
|
|
def builds_if(_type, condition):
|
|
|
|
if condition:
|
|
|
|
return builds(_type)
|
|
|
|
else:
|
|
|
|
return lambda fn: fn
|
|
|
|
|
|
|
|
|
2017-06-23 01:32:29 +02:00
|
|
|
def spoof_positions(obj):
|
2017-06-27 23:09:31 +02:00
|
|
|
if not isinstance(obj, HyObject) or isinstance(obj, HyCons):
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
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"),
|
|
|
|
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.
|
|
|
|
|
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):
|
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
|
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
|
|
|
|
|
|
|
|
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(
|
|
|
|
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,
|
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
|
|
|
"`%%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
|
|
|
|
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
|
|
|
|
|
|
|
|
|
2013-03-05 02:40:23 +01:00
|
|
|
class HyASTCompiler(object):
|
|
|
|
|
2013-05-16 15:34:14 +02:00
|
|
|
def __init__(self, module_name):
|
2017-04-14 02:42:01 +02:00
|
|
|
self.allow_builtins = module_name.startswith("hy.core")
|
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-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 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():
|
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):
|
|
|
|
if atom_type in _compile_table:
|
|
|
|
ret = _compile_table[atom_type](self, atom)
|
|
|
|
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
|
|
|
|
2014-12-22 22:52:34 +01:00
|
|
|
def _compile_collect(self, exprs, with_kwargs=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 = []
|
|
|
|
|
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:
|
|
|
|
if with_kwargs and isinstance(expr, HyKeyword):
|
|
|
|
try:
|
|
|
|
value = next(exprs_iter)
|
|
|
|
except StopIteration:
|
2015-06-15 22:42:02 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"Keyword argument {kw} needs "
|
2015-08-11 10:43:00 +02:00
|
|
|
"a value.".format(kw=str(expr[1:])))
|
2014-12-22 22:52:34 +01:00
|
|
|
|
|
|
|
compiled_value = self.compile(value)
|
2015-04-27 00:57:08 +02:00
|
|
|
ret += compiled_value
|
2014-12-22 22:52:34 +01:00
|
|
|
|
|
|
|
# no unicode for py2 in ast names
|
|
|
|
keyword = str(expr[2:])
|
|
|
|
if "-" in keyword and keyword != "-":
|
|
|
|
keyword = keyword.replace("-", "_")
|
|
|
|
|
|
|
|
keywords.append(ast.keyword(arg=keyword,
|
|
|
|
value=compiled_value.force_expr,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column))
|
|
|
|
else:
|
|
|
|
ret += self.compile(expr)
|
|
|
|
compiled_exprs.append(ret.force_expr)
|
|
|
|
|
|
|
|
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
|
|
|
|
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."""
|
2015-03-15 22:59:54 +01:00
|
|
|
ll_keywords = ("&rest", "&optional", "&key", "&kwonly", "&kwargs")
|
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
|
2015-03-15 22:59:54 +01:00
|
|
|
kwonlyargs = []
|
|
|
|
kwonlydefaults = []
|
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
|
|
|
|
2014-06-04 03:36:49 +02:00
|
|
|
if expr in ll_keywords:
|
2016-10-11 22:31:22 +02:00
|
|
|
if expr == "&optional":
|
2013-05-08 20:56:16 +02:00
|
|
|
if len(defaults) > 0:
|
2013-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"There can only be &optional "
|
|
|
|
"arguments or one &key argument")
|
2013-04-09 21:23:50 +02:00
|
|
|
lambda_keyword = expr
|
2016-10-11 22:31:22 +02:00
|
|
|
elif expr in ("&rest", "&key", "&kwonly", "&kwargs"):
|
2013-04-09 21:23:50 +02:00
|
|
|
lambda_keyword = expr
|
|
|
|
else:
|
2013-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"{0} is in an invalid "
|
|
|
|
"position.".format(repr(expr)))
|
2013-04-09 21:23:50 +02:00
|
|
|
# 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-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"There can only be one "
|
|
|
|
"&rest argument")
|
2015-12-09 01:43:12 +01:00
|
|
|
varargs = expr
|
2013-04-18 23:47:08 +02:00
|
|
|
elif lambda_keyword == "&key":
|
|
|
|
if type(expr) != HyDict:
|
2013-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"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-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"There can only be &optional "
|
|
|
|
"arguments or 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-05-14 11:39:20 +02:00
|
|
|
it = iter(expr)
|
|
|
|
for k, v in zip(it, it):
|
2016-04-11 17:38:13 +02:00
|
|
|
if not isinstance(k, HyString):
|
|
|
|
raise HyTypeError(expr,
|
|
|
|
"Only strings can be used "
|
|
|
|
"as parameter names")
|
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-05-08 20:56:16 +02:00
|
|
|
if isinstance(expr, HyList):
|
|
|
|
if not len(expr) == 2:
|
2013-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"optional args should be bare names "
|
|
|
|
"or 2-item lists")
|
2013-05-08 20:56:16 +02:00
|
|
|
k, v = expr
|
|
|
|
else:
|
|
|
|
k = expr
|
|
|
|
v = HySymbol("None").replace(k)
|
2016-04-11 17:38:13 +02:00
|
|
|
if not isinstance(k, HyString):
|
|
|
|
raise HyTypeError(expr,
|
|
|
|
"Only strings can be used as "
|
|
|
|
"parameter names")
|
2013-05-08 20:56:16 +02:00
|
|
|
args.append(k)
|
|
|
|
ret += self.compile(v)
|
|
|
|
defaults.append(ret.force_expr)
|
2015-03-15 22:59:54 +01:00
|
|
|
elif lambda_keyword == "&kwonly":
|
|
|
|
if not PY3:
|
|
|
|
raise HyTypeError(expr,
|
|
|
|
"keyword-only arguments are only "
|
|
|
|
"available under Python 3")
|
|
|
|
if isinstance(expr, HyList):
|
|
|
|
if len(expr) != 2:
|
|
|
|
raise HyTypeError(expr,
|
|
|
|
"keyword-only args should be bare "
|
|
|
|
"names or 2-item lists")
|
|
|
|
k, v = expr
|
|
|
|
kwonlyargs.append(k)
|
|
|
|
ret += self.compile(v)
|
|
|
|
kwonlydefaults.append(ret.force_expr)
|
|
|
|
else:
|
|
|
|
k = expr
|
|
|
|
kwonlyargs.append(k)
|
|
|
|
kwonlydefaults.append(None)
|
2013-04-18 23:47:08 +02:00
|
|
|
elif lambda_keyword == "&kwargs":
|
2013-04-19 04:27:38 +02:00
|
|
|
if kwargs:
|
2013-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"There can only be one "
|
|
|
|
"&kwargs argument")
|
2015-12-09 01:43:12 +01:00
|
|
|
kwargs = expr
|
2013-04-09 21:23:50 +02:00
|
|
|
|
2015-03-15 22:59:54 +01:00
|
|
|
return ret, args, defaults, varargs, kwonlyargs, kwonlydefaults, kwargs
|
2013-05-04 09:16:01 +02: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):
|
|
|
|
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:
|
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):
|
|
|
|
if form and form[0] in ("unquote", "unquote_splice"):
|
|
|
|
if len(form) != 2:
|
|
|
|
raise HyTypeError(form,
|
|
|
|
("`%s' needs 1 argument, got %s" %
|
|
|
|
form[0], len(form) - 1))
|
|
|
|
return set(), form[1], (form[0] == "unquote_splice")
|
|
|
|
|
|
|
|
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
|
|
|
|
if form and form[0] in ("unquote", "unquote_splice"):
|
|
|
|
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
|
|
|
|
2015-06-26 23:47:35 +02:00
|
|
|
if isinstance(form, (HyList, HyDict, HySet)):
|
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:
|
2013-09-22 15:08:43 +02:00
|
|
|
to_add = HyExpression([HySymbol("list"), f_contents])
|
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
|
|
|
|
2013-05-16 18:59:20 +02:00
|
|
|
elif isinstance(form, HyCons):
|
|
|
|
ret = HyExpression([HySymbol(name)])
|
|
|
|
nimport, contents, splice = self._render_quoted_form(form.car,
|
|
|
|
level)
|
|
|
|
if splice:
|
|
|
|
raise HyTypeError(form, "Can't splice dotted lists yet")
|
|
|
|
imports.update(nimport)
|
|
|
|
ret.append(contents)
|
|
|
|
|
|
|
|
nimport, contents, splice = self._render_quoted_form(form.cdr,
|
|
|
|
level)
|
|
|
|
if splice:
|
|
|
|
raise HyTypeError(form, "Can't splice the cdr of a cons")
|
|
|
|
imports.update(nimport)
|
|
|
|
ret.append(contents)
|
|
|
|
|
|
|
|
return imports, ret.replace(form), False
|
|
|
|
|
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
|
|
|
|
|
|
|
|
return imports, HyExpression([HySymbol(name),
|
|
|
|
form]).replace(form), False
|
2013-04-09 02:18:15 +02:00
|
|
|
|
|
|
|
@builds("quote")
|
2013-05-10 23:25:19 +02:00
|
|
|
@builds("quasiquote")
|
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-10 23:25:19 +02:00
|
|
|
if entries[0] == "quote":
|
|
|
|
# Never allow unquoting
|
|
|
|
level = float("inf")
|
|
|
|
else:
|
|
|
|
level = 0
|
|
|
|
imports, stmts, splice = self._render_quoted_form(entries[1], 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
|
|
|
|
2013-05-10 23:11:31 +02:00
|
|
|
@builds("unquote")
|
2013-05-11 19:59:19 +02:00
|
|
|
@builds("unquote_splicing")
|
2013-05-10 23:11:31 +02:00
|
|
|
def compile_unquote(self, expr):
|
|
|
|
raise HyTypeError(expr,
|
|
|
|
"`%s' can't be used at the top-level" % expr[0])
|
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("do")
|
2015-08-09 09:00:51 +02:00
|
|
|
def compile_do(self, expression):
|
2013-05-04 09:16:01 +02:00
|
|
|
expression.pop(0)
|
|
|
|
return self._compile_branch(expression)
|
2013-03-09 05:41:04 +01:00
|
|
|
|
2013-04-07 18:24:01 +02:00
|
|
|
@builds("raise")
|
2014-05-01 22:30:40 +02:00
|
|
|
@checkargs(multiple=[0, 1, 3])
|
2015-08-09 06:04:02 +02:00
|
|
|
def compile_raise_expression(self, expr):
|
2013-03-12 00:14:20 +01:00
|
|
|
expr.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = Result()
|
|
|
|
if expr:
|
|
|
|
ret += self.compile(expr.pop(0))
|
|
|
|
|
2014-05-01 22:30:40 +02:00
|
|
|
cause = None
|
2014-06-09 22:33:11 +02:00
|
|
|
if len(expr) == 2 and expr[0] == HyKeyword(":from"):
|
|
|
|
if not PY3:
|
|
|
|
raise HyCompileError(
|
|
|
|
"raise from only supported in python 3")
|
|
|
|
expr.pop(0)
|
|
|
|
cause = self.compile(expr.pop(0))
|
|
|
|
cause = cause.expr
|
2014-05-01 22:30:40 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
# 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,
|
2014-05-01 22:30:40 +02:00
|
|
|
cause=cause)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
return ret
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("try")
|
2017-06-23 01:32:29 +02:00
|
|
|
@checkargs(min=2)
|
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 something…)
|
2017-05-13 15:37:00 +02:00
|
|
|
body = self.compile(expr.pop(0) if expr else [])
|
2013-05-04 09:16:01 +02:00
|
|
|
|
2013-05-09 02:00:09 +02:00
|
|
|
var = self.get_anon_var()
|
|
|
|
name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
|
|
|
ctx=ast.Store(),
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
|
|
|
expr_name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
2014-06-22 18:19:56 +02:00
|
|
|
returnable = Result(expr=expr_name, temp_variables=[expr_name, name],
|
|
|
|
contains_yield=body.contains_yield)
|
2013-05-09 02:00:09 +02:00
|
|
|
|
2017-05-14 00:52:59 +02:00
|
|
|
if not all(expr):
|
|
|
|
raise HyTypeError(expr, "Empty list not allowed in `try'")
|
|
|
|
handler_results = Result()
|
|
|
|
handlers = []
|
|
|
|
while expr and expr[0][0] == HySymbol("except"):
|
|
|
|
handler_results += self._compile_catch_expression(expr.pop(0),
|
|
|
|
name)
|
|
|
|
handlers.append(handler_results.stmts.pop())
|
2013-04-08 15:58:43 +02:00
|
|
|
orelse = []
|
2017-05-14 00:52:59 +02:00
|
|
|
if expr and expr[0][0] == HySymbol("else"):
|
2017-05-14 03:35:12 +02:00
|
|
|
orelse = self._compile_branch(expr.pop(0)[1:])
|
|
|
|
orelse += ast.Assign(targets=[name],
|
|
|
|
value=orelse.force_expr,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
orelse += orelse.expr_as_stmt()
|
|
|
|
orelse = orelse.stmts
|
2013-04-09 21:19:24 +02:00
|
|
|
finalbody = []
|
2017-05-14 00:52:59 +02:00
|
|
|
if expr and expr[0][0] == HySymbol("finally"):
|
2017-05-14 03:35:12 +02:00
|
|
|
finalbody = self._compile_branch(expr.pop(0)[1:])
|
|
|
|
finalbody += finalbody.expr_as_stmt()
|
|
|
|
finalbody = finalbody.stmts
|
2017-05-14 00:52:59 +02:00
|
|
|
if expr:
|
|
|
|
if expr[0][0] in ("except", "else", "finally"):
|
2017-05-14 03:35:12 +02:00
|
|
|
raise HyTypeError(expr, "Incorrect order "
|
|
|
|
"of `except'/`else'/`finally' in `try'")
|
2017-05-14 00:52:59 +02:00
|
|
|
raise HyTypeError(expr, "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(
|
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
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = handler_results
|
|
|
|
|
2017-05-14 03:35:12 +02:00
|
|
|
body += body.expr_as_stmt() if orelse else ast.Assign(
|
|
|
|
targets=[name],
|
|
|
|
value=body.force_expr,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
2017-05-26 03:43:31 +02:00
|
|
|
|
2017-05-14 03:35:12 +02:00
|
|
|
body = body.stmts or [ast.Pass(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)]
|
|
|
|
|
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.
|
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,
|
2013-05-09 02:00:09 +02:00
|
|
|
finalbody=finalbody) + returnable
|
2013-04-09 21:19:24 +02:00
|
|
|
|
|
|
|
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)],
|
2013-05-09 02:00:09 +02:00
|
|
|
finalbody=finalbody) + returnable
|
2013-04-09 21:19:24 +02:00
|
|
|
|
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,
|
2013-05-09 02:00:09 +02:00
|
|
|
finalbody=finalbody) + returnable
|
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-05-09 02:00:09 +02:00
|
|
|
orelse=orelse) + returnable
|
2013-03-12 00:14:20 +01:00
|
|
|
|
2013-04-07 18:24:01 +02:00
|
|
|
@builds("except")
|
2013-05-09 02:00:09 +02:00
|
|
|
def magic_internal_form(self, expr):
|
2013-05-13 18:09:05 +02:00
|
|
|
raise HyTypeError(expr,
|
|
|
|
"Error: `%s' can't be used like that." % (expr[0]))
|
2013-05-09 02:00:09 +02:00
|
|
|
|
|
|
|
def _compile_catch_expression(self, expr, var):
|
2013-04-09 16:56:45 +02:00
|
|
|
catch = expr.pop(0) # catch
|
2013-04-07 17:22:57 +02:00
|
|
|
|
2017-05-13 15:37:00 +02:00
|
|
|
exceptions = expr.pop(0) if expr else HyList()
|
2013-05-09 02:00:09 +02:00
|
|
|
|
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-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
|
2017-05-13 15:37:00 +02:00
|
|
|
name = None
|
2013-04-07 17:22:57 +02:00
|
|
|
if len(exceptions) == 2:
|
|
|
|
name = exceptions.pop(0)
|
2013-05-13 18:09:05 +02:00
|
|
|
if not isinstance(name, HySymbol):
|
2013-05-17 17:07:36 +02:00
|
|
|
raise HyTypeError(
|
|
|
|
exceptions,
|
|
|
|
"Exception storage target name must be a symbol.")
|
|
|
|
|
2014-01-04 02:02:36 +01:00
|
|
|
if PY3:
|
2013-04-07 17:22:57 +02:00
|
|
|
# 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.
|
2015-10-14 19:38:05 +02:00
|
|
|
name = self._storeize(name, self.compile(name))
|
2013-04-07 17:22:57 +02:00
|
|
|
|
2017-05-13 15:37:00 +02:00
|
|
|
exceptions_list = exceptions.pop(0) if exceptions else []
|
2013-04-07 17:22:57 +02:00
|
|
|
|
|
|
|
if isinstance(exceptions_list, list):
|
|
|
|
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)
|
2013-05-04 09:16:01 +02:00
|
|
|
_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:
|
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
|
|
|
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)
|
2013-05-09 02:41:16 +02:00
|
|
|
body += ast.Assign(targets=[var],
|
2013-05-09 02:00:09 +02:00
|
|
|
value=body.force_expr,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
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
|
|
|
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
|
|
|
|
2015-10-14 03:38:15 +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))
|
2015-09-22 19:17:47 +02:00
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
orel = Result()
|
2015-08-25 18:45:20 +02:00
|
|
|
nested = root = False
|
2013-05-04 09:16:01 +02:00
|
|
|
if expression:
|
2015-08-25 18:45:20 +02:00
|
|
|
orel_expr = expression.pop(0)
|
|
|
|
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:
|
|
|
|
name = ast.Name(id=ast_str(self.temp_if),
|
|
|
|
arg=ast_str(self.temp_if),
|
|
|
|
ctx=ast.Store(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
branch += ast.Assign(targets=[name],
|
|
|
|
value=body.force_expr,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
|
|
|
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()
|
2013-05-04 09:16:01 +02:00
|
|
|
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
|
2015-08-25 18:45:20 +02:00
|
|
|
if not nested or not orel.stmts or (not root and
|
|
|
|
var != self.temp_if):
|
|
|
|
orel += ast.Assign(targets=[name],
|
|
|
|
value=orel.force_expr,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
# 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)
|
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
|
|
|
|
2013-06-24 02:39:46 +02:00
|
|
|
@builds("break")
|
|
|
|
def compile_break_expression(self, expr):
|
2013-06-24 03:26:40 +02:00
|
|
|
ret = ast.Break(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
2013-06-24 02:39:46 +02:00
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
@builds("continue")
|
|
|
|
def compile_continue_expression(self, expr):
|
2013-06-24 03:26:40 +02:00
|
|
|
ret = ast.Continue(lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
2013-06-24 02:39:46 +02:00
|
|
|
|
|
|
|
return ret
|
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("assert")
|
2015-02-22 17:34:19 +01:00
|
|
|
@checkargs(min=1, max=2)
|
2013-03-06 03:42:54 +01:00
|
|
|
def compile_assert_expression(self, expr):
|
|
|
|
expr.pop(0) # assert
|
|
|
|
e = expr.pop(0)
|
2015-02-22 17:34:19 +01:00
|
|
|
if len(expr) == 1:
|
|
|
|
msg = self.compile(expr.pop(0)).force_expr
|
|
|
|
else:
|
|
|
|
msg = None
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(e)
|
|
|
|
ret += ast.Assert(test=ret.force_expr,
|
2015-02-22 17:34:19 +01:00
|
|
|
msg=msg,
|
2013-03-06 03:42:54 +01:00
|
|
|
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")
|
2014-12-14 21:13:44 +01:00
|
|
|
@checkargs(min=1)
|
2013-04-24 01:25:02 +02:00
|
|
|
def compile_global_expression(self, expr):
|
|
|
|
expr.pop(0) # global
|
2014-12-14 21:13:44 +01:00
|
|
|
names = []
|
|
|
|
while len(expr) > 0:
|
|
|
|
identifier = expr.pop(0)
|
|
|
|
name = ast_str(identifier)
|
|
|
|
names.append(name)
|
|
|
|
if not isinstance(identifier, HySymbol):
|
|
|
|
raise HyTypeError(identifier, "(global) arguments must "
|
|
|
|
" be Symbols")
|
|
|
|
|
|
|
|
return ast.Global(names=names,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
2013-04-19 08:40:03 +02:00
|
|
|
|
2014-12-14 21:14:19 +01:00
|
|
|
@builds("nonlocal")
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_nonlocal_expression(self, expr):
|
|
|
|
if not PY3:
|
|
|
|
raise HyCompileError(
|
|
|
|
"nonlocal only supported in python 3!")
|
|
|
|
|
|
|
|
expr.pop(0) # nonlocal
|
|
|
|
names = []
|
|
|
|
while len(expr) > 0:
|
|
|
|
identifier = expr.pop(0)
|
|
|
|
name = ast_str(identifier)
|
|
|
|
names.append(name)
|
|
|
|
if not isinstance(identifier, HySymbol):
|
|
|
|
raise HyTypeError(identifier, "(nonlocal) arguments must "
|
|
|
|
"be Symbols.")
|
|
|
|
|
|
|
|
return ast.Nonlocal(names=names,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
2013-04-19 08:40:03 +02: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)
|
2017-04-14 01:17:32 +02:00
|
|
|
ret = Result(contains_yield=(not PY3))
|
2013-05-04 09:16:01 +02:00
|
|
|
|
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
|
|
|
|
|
2014-02-23 17:48:33 +01:00
|
|
|
@builds("yield_from")
|
|
|
|
@checkargs(max=1)
|
|
|
|
def compile_yield_from_expression(self, expr):
|
2017-04-14 01:17:32 +02:00
|
|
|
if not PY3:
|
2014-02-23 17:48:33 +01:00
|
|
|
raise HyCompileError(
|
|
|
|
"yield-from only supported in python 3.3+!")
|
|
|
|
|
|
|
|
expr.pop(0)
|
|
|
|
ret = Result(contains_yield=True)
|
|
|
|
|
|
|
|
value = None
|
|
|
|
if expr != []:
|
|
|
|
ret += self.compile(expr.pop(0))
|
|
|
|
value = ret.force_expr
|
|
|
|
|
|
|
|
ret += ast.YieldFrom(
|
|
|
|
value=value,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2014-02-11 16:57:31 +01:00
|
|
|
if not isinstance(iexpr, (HySymbol, HyList)):
|
|
|
|
raise HyTypeError(iexpr, "(import) requires a Symbol "
|
|
|
|
"or a List.")
|
|
|
|
|
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-12-30 12:04:40 +01:00
|
|
|
@checkargs(min=2)
|
2013-03-09 06:55:27 +01:00
|
|
|
def compile_index_expression(self, expr):
|
|
|
|
expr.pop(0) # index
|
|
|
|
|
2013-12-30 12:04:40 +01:00
|
|
|
val = self.compile(expr.pop(0))
|
2014-12-22 22:52:34 +01:00
|
|
|
slices, ret, _ = self._compile_collect(expr)
|
2013-12-30 12:04:40 +01:00
|
|
|
|
|
|
|
if val.stmts:
|
|
|
|
ret += val
|
|
|
|
|
|
|
|
for sli in slices:
|
|
|
|
val = Result() + ast.Subscript(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
value=val.force_expr,
|
|
|
|
slice=ast.Index(value=sli),
|
|
|
|
ctx=ast.Load())
|
|
|
|
|
|
|
|
return ret + val
|
2013-03-19 00:47:48 +01:00
|
|
|
|
2014-01-09 02:41:49 +01:00
|
|
|
@builds(".")
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_attribute_access(self, expr):
|
|
|
|
expr.pop(0) # dot
|
|
|
|
|
|
|
|
ret = self.compile(expr.pop(0))
|
|
|
|
|
|
|
|
for attr in expr:
|
|
|
|
if isinstance(attr, HySymbol):
|
|
|
|
ret += ast.Attribute(lineno=attr.start_line,
|
|
|
|
col_offset=attr.start_column,
|
|
|
|
value=ret.force_expr,
|
|
|
|
attr=ast_str(attr),
|
|
|
|
ctx=ast.Load())
|
|
|
|
elif type(attr) == HyList:
|
|
|
|
if len(attr) != 1:
|
|
|
|
raise HyTypeError(
|
|
|
|
attr,
|
|
|
|
"The attribute access DSL only accepts HySymbols "
|
|
|
|
"and one-item lists, got {0}-item list instead".format(
|
|
|
|
len(attr),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
compiled_attr = self.compile(attr.pop(0))
|
|
|
|
ret = compiled_attr + ret + ast.Subscript(
|
|
|
|
lineno=attr.start_line,
|
|
|
|
col_offset=attr.start_column,
|
|
|
|
value=ret.force_expr,
|
|
|
|
slice=ast.Index(value=compiled_attr.force_expr),
|
|
|
|
ctx=ast.Load())
|
|
|
|
else:
|
|
|
|
raise HyTypeError(
|
|
|
|
attr,
|
|
|
|
"The attribute access DSL only accepts HySymbols "
|
|
|
|
"and one-item lists, got {0} instead".format(
|
|
|
|
type(attr).__name__,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
2013-12-21 23:33:44 +01:00
|
|
|
@builds("del")
|
|
|
|
def compile_del_expression(self, expr):
|
2015-08-18 08:51:09 +02:00
|
|
|
root = expr.pop(0)
|
|
|
|
if not expr:
|
|
|
|
result = Result()
|
|
|
|
result += ast.Name(id='None', ctx=ast.Load(),
|
|
|
|
lineno=root.start_line,
|
|
|
|
col_offset=root.start_column)
|
|
|
|
return result
|
|
|
|
|
2013-12-21 23:33:44 +01:00
|
|
|
del_targets = []
|
2015-10-14 19:38:05 +02:00
|
|
|
ret = Result()
|
|
|
|
for target in expr:
|
|
|
|
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
|
|
|
|
|
|
|
return ret + ast.Delete(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
targets=del_targets)
|
|
|
|
|
2014-09-05 05:29:57 +02:00
|
|
|
@builds("cut")
|
2013-05-04 09:16:01 +02:00
|
|
|
@checkargs(min=1, max=4)
|
2014-09-05 05:29:57 +02:00
|
|
|
def compile_cut_expression(self, expr):
|
2013-03-19 00:47:48 +01:00
|
|
|
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-07-16 14:35:57 +02:00
|
|
|
@checkargs(min=3, even=False)
|
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))
|
2013-07-16 14:35:57 +02:00
|
|
|
ret = target
|
2013-07-17 15:40:38 +02:00
|
|
|
i = iter(expr)
|
|
|
|
for (key, val) in ((self.compile(x), self.compile(y))
|
|
|
|
for (x, y) in zip(i, i)):
|
2013-03-10 17:59:16 +01:00
|
|
|
|
2013-07-16 14:35:57 +02:00
|
|
|
ret += key + val + ast.Assign(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
targets=[
|
|
|
|
ast.Subscript(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
value=target.force_expr,
|
|
|
|
slice=ast.Index(value=key.force_expr),
|
|
|
|
ctx=ast.Store())],
|
|
|
|
value=val.force_expr)
|
|
|
|
return ret
|
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))
|
2017-03-26 01:15:10 +01:00
|
|
|
if not fn.stmts or not isinstance(fn.stmts[-1], (ast.FunctionDef,
|
|
|
|
ast.ClassDef)):
|
2013-04-09 23:06:04 +02:00
|
|
|
raise HyTypeError(expr, "Decorated a non-function")
|
2017-03-26 01:15:10 +01:00
|
|
|
decorators, ret, _ = self._compile_collect(expr)
|
|
|
|
fn.stmts[-1].decorator_list = decorators + fn.stmts[-1].decorator_list
|
|
|
|
return ret + fn
|
2013-03-10 03:01:59 +01:00
|
|
|
|
2013-11-10 19:00:01 +01: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):
|
2013-11-10 19:00:01 +01:00
|
|
|
expr.pop(0) # with*
|
2013-04-08 00:35:36 +02:00
|
|
|
|
|
|
|
args = expr.pop(0)
|
2013-11-10 19:00:01 +01:00
|
|
|
if not isinstance(args, HyList):
|
|
|
|
raise HyTypeError(expr,
|
|
|
|
"with expects a list, received `{0}'".format(
|
|
|
|
type(args).__name__))
|
|
|
|
if len(args) < 1:
|
|
|
|
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 != []:
|
2015-10-14 19:38:05 +02:00
|
|
|
thing = self._storeize(args[0], 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)
|
|
|
|
|
2013-07-19 00:43:06 +02:00
|
|
|
var = self.get_anon_var()
|
|
|
|
name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
|
|
|
ctx=ast.Store(),
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
|
|
|
# Store the result of the body in a tempvar
|
|
|
|
body += ast.Assign(targets=[name],
|
|
|
|
value=body.force_expr,
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
2013-07-19 00:43:06 +02:00
|
|
|
ret = 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
|
|
|
|
expr_name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column)
|
|
|
|
|
|
|
|
ret += Result(expr=expr_name, temp_variables=[expr_name, name])
|
|
|
|
|
|
|
|
return ret
|
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)
|
2014-12-22 22:52:34 +01:00
|
|
|
elts, ret, _ = self._compile_collect(expr)
|
2013-05-04 09:16:01 +02:00
|
|
|
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
|
|
|
|
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)
|
|
|
|
|
|
|
|
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:
|
2013-05-04 09:16:01 +02:00
|
|
|
comp_target = self.compile(target)
|
2015-10-14 19:38:05 +02:00
|
|
|
target = self._storeize(target, comp_target)
|
2014-01-15 23:55:33 +01:00
|
|
|
gen_res += self.compile(iterable)
|
|
|
|
gen.append(ast.comprehension(
|
2013-05-04 09:16:01 +02:00
|
|
|
target=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
|
|
|
|
|
|
|
|
@builds("list_comp")
|
|
|
|
@checkargs(min=2, max=3)
|
|
|
|
def compile_list_comprehension(self, expr):
|
|
|
|
# (list-comp expr (target iter) cond?)
|
|
|
|
expr.pop(0)
|
|
|
|
expression = expr.pop(0)
|
2015-08-10 15:55:11 +02:00
|
|
|
gen_gen = expr[0]
|
|
|
|
|
|
|
|
if not isinstance(gen_gen, HyList):
|
|
|
|
raise HyTypeError(gen_gen, "Generator expression must be a list.")
|
2014-01-15 23:55:33 +01:00
|
|
|
|
|
|
|
gen_res, gen = self._compile_generator_iterables(expr)
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2015-08-10 15:55:11 +02:00
|
|
|
if len(gen) == 0:
|
|
|
|
raise HyTypeError(gen_gen, "Generator expression cannot be empty.")
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
compiled_expression = self.compile(expression)
|
2014-01-15 23:55:33 +01:00
|
|
|
ret = compiled_expression + gen_res
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += ast.ListComp(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
elt=compiled_expression.force_expr,
|
2014-01-15 23:55:33 +01:00
|
|
|
generators=gen)
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
@builds("set_comp")
|
|
|
|
@checkargs(min=2, max=3)
|
|
|
|
def compile_set_comprehension(self, expr):
|
2017-04-14 01:09:45 +02:00
|
|
|
ret = self.compile_list_comprehension(expr)
|
|
|
|
expr = ret.expr
|
|
|
|
ret.expr = ast.SetComp(
|
|
|
|
lineno=expr.lineno,
|
|
|
|
col_offset=expr.col_offset,
|
|
|
|
elt=expr.elt,
|
|
|
|
generators=expr.generators)
|
2014-01-15 23:55:33 +01:00
|
|
|
|
2017-04-14 01:09:45 +02:00
|
|
|
return ret
|
2014-01-15 23:55:33 +01:00
|
|
|
|
|
|
|
@builds("dict_comp")
|
|
|
|
@checkargs(min=3, max=4)
|
|
|
|
def compile_dict_comprehension(self, expr):
|
2017-04-14 01:09:45 +02:00
|
|
|
expr.pop(0) # dict-comp
|
|
|
|
key = expr.pop(0)
|
|
|
|
value = expr.pop(0)
|
2014-01-15 23:55:33 +01:00
|
|
|
|
2017-04-14 01:09:45 +02:00
|
|
|
gen_res, gen = self._compile_generator_iterables(expr)
|
2013-04-03 02:46:32 +02:00
|
|
|
|
2017-04-14 01:09:45 +02:00
|
|
|
compiled_key = self.compile(key)
|
|
|
|
compiled_value = self.compile(value)
|
|
|
|
ret = compiled_key + compiled_value + gen_res
|
|
|
|
ret += ast.DictComp(
|
|
|
|
lineno=expr.start_line,
|
|
|
|
col_offset=expr.start_column,
|
|
|
|
key=compiled_key.force_expr,
|
|
|
|
value=compiled_value.force_expr,
|
|
|
|
generators=gen)
|
2014-01-15 23:55:33 +01:00
|
|
|
|
2017-04-14 01:09:45 +02:00
|
|
|
return ret
|
2014-01-15 23:55:33 +01:00
|
|
|
|
|
|
|
@builds("genexpr")
|
|
|
|
def compile_genexpr(self, expr):
|
|
|
|
ret = self.compile_list_comprehension(expr)
|
|
|
|
expr = ret.expr
|
|
|
|
ret.expr = ast.GeneratorExp(
|
|
|
|
lineno=expr.lineno,
|
|
|
|
col_offset=expr.col_offset,
|
|
|
|
elt=expr.elt,
|
|
|
|
generators=expr.generators)
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-04-02 04:47:11 +02:00
|
|
|
|
2013-11-02 20:11:18 +01:00
|
|
|
@builds("apply")
|
|
|
|
@checkargs(min=1, max=3)
|
|
|
|
def compile_apply_expression(self, expr):
|
|
|
|
expr.pop(0) # apply
|
2013-12-23 19:38:10 +01:00
|
|
|
|
|
|
|
ret = Result()
|
|
|
|
|
|
|
|
fun = expr.pop(0)
|
|
|
|
|
|
|
|
# We actually defer the compilation of the function call to
|
|
|
|
# @builds(HyExpression), allowing us to work on method calls
|
|
|
|
call = HyExpression([fun]).replace(fun)
|
|
|
|
|
|
|
|
if isinstance(fun, HySymbol) and fun.startswith("."):
|
|
|
|
# (apply .foo lst) needs to work as lst[0].foo(*lst[1:])
|
|
|
|
if not expr:
|
|
|
|
raise HyTypeError(
|
|
|
|
expr, "apply of a method needs to have an argument"
|
|
|
|
)
|
|
|
|
|
|
|
|
# We need to grab the arguments, and split them.
|
|
|
|
|
|
|
|
# Assign them to a variable if they're not one already
|
|
|
|
if type(expr[0]) == HyList:
|
|
|
|
if len(expr[0]) == 0:
|
|
|
|
raise HyTypeError(
|
|
|
|
expr, "apply of a method needs to have an argument"
|
|
|
|
)
|
|
|
|
call.append(expr[0].pop(0))
|
|
|
|
else:
|
|
|
|
if isinstance(expr[0], HySymbol):
|
|
|
|
tempvar = expr[0]
|
|
|
|
else:
|
|
|
|
tempvar = HySymbol(self.get_anon_var()).replace(expr[0])
|
|
|
|
assignment = HyExpression(
|
|
|
|
[HySymbol("setv"), tempvar, expr[0]]
|
|
|
|
).replace(expr[0])
|
|
|
|
|
|
|
|
# and add the assignment to our result
|
|
|
|
ret += self.compile(assignment)
|
|
|
|
|
|
|
|
# The first argument is the object on which to call the method
|
|
|
|
# So we translate (apply .foo args) to (.foo (get args 0))
|
|
|
|
call.append(HyExpression(
|
|
|
|
[HySymbol("get"), tempvar, HyInteger(0)]
|
|
|
|
).replace(tempvar))
|
|
|
|
|
|
|
|
# We then pass the other arguments to the function
|
|
|
|
expr[0] = HyExpression(
|
2014-09-05 05:29:57 +02:00
|
|
|
[HySymbol("cut"), tempvar, HyInteger(1)]
|
2013-12-23 19:38:10 +01:00
|
|
|
).replace(expr[0])
|
|
|
|
|
|
|
|
ret += self.compile(call)
|
|
|
|
|
|
|
|
if not isinstance(ret.expr, ast.Call):
|
|
|
|
raise HyTypeError(
|
|
|
|
fun, "compiling the application of `{}' didn't return a "
|
|
|
|
"function call, but `{}'".format(fun, type(ret.expr).__name__)
|
|
|
|
)
|
|
|
|
if ret.expr.starargs or ret.expr.kwargs:
|
|
|
|
raise HyTypeError(
|
|
|
|
expr, "compiling the function application returned a function "
|
|
|
|
"call with arguments"
|
|
|
|
)
|
2013-03-10 03:14:30 +01:00
|
|
|
|
2013-11-02 20:11:18 +01:00
|
|
|
if expr:
|
2013-07-24 00:25:48 +02:00
|
|
|
stargs = expr.pop(0)
|
|
|
|
if stargs is not None:
|
|
|
|
stargs = self.compile(stargs)
|
2015-05-06 08:31:11 +02:00
|
|
|
if PY35:
|
|
|
|
stargs_expr = stargs.force_expr
|
|
|
|
ret.expr.args.append(
|
|
|
|
ast.Starred(stargs_expr, ast.Load(),
|
|
|
|
lineno=stargs_expr.lineno,
|
|
|
|
col_offset=stargs_expr.col_offset)
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
ret.expr.starargs = stargs.force_expr
|
2013-07-24 00:25:48 +02:00
|
|
|
ret = stargs + ret
|
2013-03-10 03:14:30 +01:00
|
|
|
|
2013-11-02 20:11:18 +01:00
|
|
|
if expr:
|
2015-08-10 13:17:40 +02:00
|
|
|
kwargs = expr.pop(0)
|
|
|
|
if isinstance(kwargs, HyDict):
|
|
|
|
new_kwargs = []
|
|
|
|
for k, v in kwargs.items():
|
|
|
|
if isinstance(k, HySymbol):
|
|
|
|
pass
|
|
|
|
elif isinstance(k, HyString):
|
|
|
|
k = HyString(hy_symbol_mangle(str_type(k))).replace(k)
|
|
|
|
elif isinstance(k, HyKeyword):
|
|
|
|
sym = hy_symbol_mangle(str_type(k)[2:])
|
|
|
|
k = HyString(sym).replace(k)
|
|
|
|
new_kwargs += [k, v]
|
|
|
|
kwargs = HyDict(new_kwargs).replace(kwargs)
|
|
|
|
|
|
|
|
kwargs = self.compile(kwargs)
|
2015-05-06 08:31:11 +02:00
|
|
|
if PY35:
|
|
|
|
kwargs_expr = kwargs.force_expr
|
|
|
|
ret.expr.keywords.append(
|
|
|
|
ast.keyword(None, kwargs_expr,
|
|
|
|
lineno=kwargs_expr.lineno,
|
|
|
|
col_offset=kwargs_expr.col_offset)
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
ret.expr.kwargs = kwargs.force_expr
|
2013-11-02 20:11:18 +01:00
|
|
|
ret = kwargs + ret
|
2013-04-11 03:44:23 +02:00
|
|
|
|
2013-11-02 20:11:18 +01:00
|
|
|
return ret
|
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-05-11 05:43:34 +02:00
|
|
|
@builds("require")
|
|
|
|
def compile_require(self, expression):
|
|
|
|
"""
|
|
|
|
TODO: keep track of what we've imported in this run and then
|
2014-05-02 17:58:42 +02:00
|
|
|
"unimport" it after we've completed `thing' so that we don't pollute
|
2013-05-11 05:43:34 +02:00
|
|
|
other envs.
|
|
|
|
"""
|
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
|
|
|
for entry in expression[1:]:
|
|
|
|
if isinstance(entry, HySymbol):
|
|
|
|
# e.g., (require foo)
|
|
|
|
__import__(entry)
|
|
|
|
require(entry, self.module_name, all_macros=True,
|
|
|
|
prefix=entry)
|
|
|
|
elif isinstance(entry, HyList) and len(entry) == 2:
|
|
|
|
# e.g., (require [foo [bar baz :as MyBaz bing]])
|
|
|
|
# or (require [foo [*]])
|
|
|
|
module, names = entry
|
|
|
|
if not isinstance(names, HyList):
|
|
|
|
raise HyTypeError(names,
|
|
|
|
"(require) name lists should be HyLists")
|
|
|
|
__import__(module)
|
|
|
|
if '*' in names:
|
|
|
|
if len(names) != 1:
|
|
|
|
raise HyTypeError(names, "* in a (require) name list "
|
|
|
|
"must be on its own")
|
|
|
|
require(module, self.module_name, all_macros=True)
|
|
|
|
else:
|
|
|
|
assignments = {}
|
|
|
|
while names:
|
|
|
|
if len(names) > 1 and names[1] == HyKeyword(":as"):
|
|
|
|
k, _, v = names[:3]
|
|
|
|
del names[:3]
|
|
|
|
assignments[k] = v
|
|
|
|
else:
|
|
|
|
symbol = names.pop(0)
|
|
|
|
assignments[symbol] = symbol
|
|
|
|
require(module, self.module_name, assignments=assignments)
|
|
|
|
elif (isinstance(entry, HyList) and len(entry) == 3
|
|
|
|
and entry[1] == HyKeyword(":as")):
|
|
|
|
# e.g., (require [foo :as bar])
|
|
|
|
module, _, prefix = entry
|
|
|
|
__import__(module)
|
|
|
|
require(module, self.module_name, all_macros=True,
|
|
|
|
prefix=prefix)
|
|
|
|
else:
|
|
|
|
raise HyTypeError(entry, "unrecognized (require) syntax")
|
2013-05-11 08:04:17 +02:00
|
|
|
return Result()
|
2013-05-11 05:43:34 +02:00
|
|
|
|
2013-04-07 23:54:56 +02:00
|
|
|
@builds("and")
|
2013-04-08 00:00:20 +02:00
|
|
|
@builds("or")
|
|
|
|
def compile_logical_or_and_and_operator(self, expression):
|
2015-07-03 00:52:34 +02:00
|
|
|
ops = {"and": (ast.And, "True"),
|
|
|
|
"or": (ast.Or, "None")}
|
2013-04-07 23:54:56 +02:00
|
|
|
operator = expression.pop(0)
|
2015-07-03 00:52:34 +02:00
|
|
|
opnode, default = ops[operator]
|
|
|
|
root_line, root_column = operator.start_line, operator.start_column
|
|
|
|
if len(expression) == 0:
|
|
|
|
return ast.Name(id=default,
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=root_line,
|
|
|
|
col_offset=root_column)
|
|
|
|
elif len(expression) == 1:
|
|
|
|
return self.compile(expression[0])
|
2015-02-18 22:40:07 +01:00
|
|
|
ret = Result()
|
|
|
|
values = list(map(self.compile, expression))
|
|
|
|
has_stmt = any(value.stmts for value in values)
|
|
|
|
if has_stmt:
|
|
|
|
# Compile it to an if...else sequence
|
|
|
|
var = self.get_anon_var()
|
|
|
|
name = ast.Name(id=var,
|
|
|
|
ctx=ast.Store(),
|
|
|
|
lineno=root_line,
|
|
|
|
col_offset=root_column)
|
|
|
|
expr_name = ast.Name(id=var,
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=root_line,
|
|
|
|
col_offset=root_column)
|
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):
|
|
|
|
if node is None:
|
|
|
|
line, column = root_line, root_column
|
|
|
|
else:
|
|
|
|
line, column = node.lineno, node.col_offset
|
2016-12-01 08:49:51 +01:00
|
|
|
positioned_name = ast.Name(id=var, ctx=ast.Store(),
|
|
|
|
lineno=line, col_offset=column)
|
|
|
|
temp_variables.append(positioned_name)
|
|
|
|
return ast.Assign(targets=[positioned_name],
|
2015-02-18 22:40:07 +01:00
|
|
|
value=value,
|
|
|
|
lineno=line,
|
|
|
|
col_offset=column)
|
|
|
|
root = []
|
|
|
|
current = root
|
|
|
|
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":
|
|
|
|
cond = ast.UnaryOp(op=ast.Not(),
|
|
|
|
operand=expr_name,
|
|
|
|
lineno=node.lineno,
|
|
|
|
col_offset=node.col_offset)
|
|
|
|
current.append(ast.If(test=cond,
|
|
|
|
body=[],
|
|
|
|
lineno=node.lineno,
|
|
|
|
col_offset=node.col_offset,
|
|
|
|
orelse=[]))
|
|
|
|
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:
|
2015-07-03 00:52:34 +02:00
|
|
|
ret += ast.BoolOp(op=opnode(),
|
2015-02-18 22:40:07 +01:00
|
|
|
lineno=root_line,
|
|
|
|
col_offset=root_column,
|
|
|
|
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
|
|
|
|
2015-10-03 11:01:48 +02: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]
|
2014-12-22 22:52:34 +01:00
|
|
|
exprs, ret, _ = self._compile_collect(expression)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
2015-10-03 11:01:48 +02:00
|
|
|
@builds("=")
|
2017-04-14 02:42:01 +02:00
|
|
|
@builds("is")
|
2015-10-03 11:01:48 +02:00
|
|
|
@builds("<")
|
|
|
|
@builds("<=")
|
|
|
|
@builds(">")
|
|
|
|
@builds(">=")
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_compare_op_expression(self, expression):
|
|
|
|
if len(expression) == 2:
|
2017-04-14 02:42:01 +02:00
|
|
|
return ast.Name(id="True",
|
2015-10-03 11:01:48 +02:00
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
return self._compile_compare_op_expression(expression)
|
|
|
|
|
2017-04-14 02:42:01 +02:00
|
|
|
@builds("!=")
|
2015-10-03 11:01:48 +02:00
|
|
|
@builds("is_not")
|
|
|
|
@checkargs(min=2)
|
|
|
|
def compile_compare_op_expression_coll(self, expression):
|
|
|
|
return self._compile_compare_op_expression(expression)
|
|
|
|
|
2017-04-14 02:42:01 +02:00
|
|
|
@builds("in")
|
|
|
|
@builds("not_in")
|
|
|
|
@checkargs(2)
|
|
|
|
def compile_compare_op_expression_binary(self, expression):
|
|
|
|
return self._compile_compare_op_expression(expression)
|
|
|
|
|
|
|
|
def _compile_maths_expression(self, expression):
|
2013-03-06 00:28:27 +01:00
|
|
|
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}
|
2015-04-13 05:35:08 +02:00
|
|
|
if PY35:
|
|
|
|
ops.update({"@": ast.MatMult})
|
2013-03-06 00:28:27 +01:00
|
|
|
|
2017-04-14 02:42:01 +02:00
|
|
|
op = ops[expression.pop(0)]
|
|
|
|
right_associative = op == ast.Pow
|
2013-03-06 00:28:27 +01:00
|
|
|
|
2017-06-27 23:09:31 +02:00
|
|
|
lineno, col_offset = expression.start_line, expression.start_column
|
2017-04-14 02:42:01 +02:00
|
|
|
if right_associative:
|
|
|
|
expression = expression[::-1]
|
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
|
2017-04-14 02:42:01 +02:00
|
|
|
if right_associative:
|
|
|
|
left_expr, right_expr = right_expr, left_expr
|
2013-05-04 09:16:01 +02:00
|
|
|
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,
|
2017-06-27 23:09:31 +02:00
|
|
|
lineno=lineno,
|
|
|
|
col_offset=col_offset)
|
2013-05-04 09:16:01 +02:00
|
|
|
return ret
|
2013-03-06 00:28:27 +01:00
|
|
|
|
2017-04-14 02:42:01 +02:00
|
|
|
@builds("**")
|
2015-06-26 23:10:22 +02:00
|
|
|
@builds("//")
|
2017-04-14 02:42:01 +02:00
|
|
|
@builds("<<")
|
|
|
|
@builds(">>")
|
|
|
|
@builds("&")
|
|
|
|
@checkargs(min=2)
|
|
|
|
def compile_maths_expression_2_or_more(self, expression):
|
|
|
|
return self._compile_maths_expression(expression)
|
|
|
|
|
|
|
|
@builds("%")
|
|
|
|
@builds("^")
|
|
|
|
@checkargs(2)
|
|
|
|
def compile_maths_expression_exactly_2(self, expression):
|
|
|
|
return self._compile_maths_expression(expression)
|
|
|
|
|
|
|
|
@builds("*")
|
|
|
|
@builds("|")
|
2013-12-15 07:12:57 +01:00
|
|
|
def compile_maths_expression_mul(self, expression):
|
2017-04-14 02:42:01 +02:00
|
|
|
id_elem = {"*": 1, "|": 0}[expression[0]]
|
|
|
|
if len(expression) == 1:
|
|
|
|
return ast.Num(n=long_type(id_elem),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
elif len(expression) == 2:
|
|
|
|
return self.compile(expression[1])
|
2013-12-15 07:12:57 +01:00
|
|
|
else:
|
2017-04-14 02:42:01 +02:00
|
|
|
return self._compile_maths_expression(expression)
|
|
|
|
|
|
|
|
@builds("/")
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_maths_expression_div(self, expression):
|
|
|
|
if len(expression) == 2:
|
|
|
|
expression = HyExpression([HySymbol("/"),
|
|
|
|
HyInteger(1),
|
|
|
|
expression[1]]).replace(expression)
|
|
|
|
return self._compile_maths_expression(expression)
|
|
|
|
|
|
|
|
def _compile_maths_expression_additive(self, expression):
|
2013-04-16 17:43:40 +02:00
|
|
|
if len(expression) > 2:
|
2017-04-14 02:42:01 +02:00
|
|
|
return self._compile_maths_expression(expression)
|
2013-04-16 17:43:40 +02:00
|
|
|
else:
|
2016-09-20 22:05:52 +02:00
|
|
|
op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]()
|
|
|
|
arg = expression.pop(0)
|
2013-05-04 09:16:01 +02:00
|
|
|
ret = self.compile(arg)
|
2016-09-20 22:05:52 +02:00
|
|
|
ret += ast.UnaryOp(op=op,
|
2013-05-04 09:16:01 +02:00
|
|
|
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
|
|
|
|
2017-04-14 02:42:01 +02:00
|
|
|
@builds("&")
|
|
|
|
@builds_if("@", PY35)
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_maths_expression_unary_idempotent(self, expression):
|
|
|
|
if len(expression) == 2:
|
|
|
|
# Used as a unary operator, this operator simply
|
|
|
|
# returns its argument.
|
|
|
|
return self.compile(expression[1])
|
|
|
|
else:
|
|
|
|
return self._compile_maths_expression(expression)
|
|
|
|
|
2016-09-20 22:05:52 +02:00
|
|
|
@builds("+")
|
|
|
|
def compile_maths_expression_add(self, expression):
|
|
|
|
if len(expression) == 1:
|
|
|
|
# Nullary +
|
|
|
|
return ast.Num(n=long_type(0),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
else:
|
2017-04-14 02:42:01 +02:00
|
|
|
return self._compile_maths_expression_additive(expression)
|
2016-09-20 22:05:52 +02:00
|
|
|
|
|
|
|
@builds("-")
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_maths_expression_sub(self, expression):
|
2017-04-14 02:42:01 +02:00
|
|
|
return self._compile_maths_expression_additive(expression)
|
2016-09-20 22:05:52 +02:00
|
|
|
|
2013-04-14 17:30:12 +02:00
|
|
|
@builds("+=")
|
|
|
|
@builds("/=")
|
|
|
|
@builds("//=")
|
|
|
|
@builds("*=")
|
|
|
|
@builds("_=")
|
|
|
|
@builds("%=")
|
|
|
|
@builds("**=")
|
|
|
|
@builds("<<=")
|
|
|
|
@builds(">>=")
|
|
|
|
@builds("|=")
|
|
|
|
@builds("^=")
|
|
|
|
@builds("&=")
|
2015-04-13 05:35:08 +02:00
|
|
|
@builds_if("@=", PY35)
|
2013-04-14 17:30:12 +02:00
|
|
|
@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}
|
2015-04-13 05:35:08 +02:00
|
|
|
if PY35:
|
|
|
|
ops.update({"@=": ast.MatMult})
|
2013-04-14 17:30:12 +02:00
|
|
|
|
|
|
|
op = ops[expression[0]]
|
|
|
|
|
2015-10-14 19:38:05 +02:00
|
|
|
target = self._storeize(expression[1], 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-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 == []:
|
|
|
|
return self.compile_list(expression)
|
|
|
|
|
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)
|
|
|
|
|
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
|
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
|
2013-05-04 09:16:01 +02:00
|
|
|
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)
|
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)
|
|
|
|
if fn in ("type", "HyKeyword", "keyword", "name", "is_keyword"):
|
|
|
|
with_kwargs = False
|
|
|
|
else:
|
|
|
|
with_kwargs = True
|
|
|
|
|
|
|
|
args, ret, kwargs = self._compile_collect(expression[1:],
|
|
|
|
with_kwargs)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
ret += ast.Call(func=func.expr,
|
|
|
|
args=args,
|
2014-12-22 22:52:34 +01:00
|
|
|
keywords=kwargs,
|
2013-03-05 02:40:23 +01:00
|
|
|
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("setv")
|
2013-03-06 00:16:04 +01:00
|
|
|
def compile_def_expression(self, expression):
|
2015-08-18 00:29:06 +02:00
|
|
|
root = expression.pop(0)
|
|
|
|
if not expression:
|
|
|
|
result = Result()
|
|
|
|
result += ast.Name(id='None', ctx=ast.Load(),
|
|
|
|
lineno=root.start_line,
|
|
|
|
col_offset=root.start_column)
|
|
|
|
return result
|
|
|
|
elif len(expression) == 2:
|
2015-07-25 01:11:13 +02:00
|
|
|
return self._compile_assign(expression[0], expression[1],
|
|
|
|
expression.start_line,
|
|
|
|
expression.start_column)
|
|
|
|
elif len(expression) % 2 != 0:
|
|
|
|
raise HyTypeError(expression,
|
2015-09-13 04:57:47 +02:00
|
|
|
"`{}' needs an even number of arguments".format(
|
|
|
|
root))
|
2015-07-25 01:11:13 +02:00
|
|
|
else:
|
|
|
|
result = Result()
|
|
|
|
for tgt, target in zip(expression[::2], expression[1::2]):
|
2017-03-23 23:11:55 +01:00
|
|
|
result += self._compile_assign(tgt, target, tgt.start_line,
|
|
|
|
tgt.start_column)
|
2015-07-25 01:11:13 +02:00
|
|
|
return result
|
2013-04-24 22:18:05 +02:00
|
|
|
|
|
|
|
def _compile_assign(self, name, result,
|
|
|
|
start_line, start_column):
|
2013-03-06 00:16:04 +01:00
|
|
|
|
2014-06-29 19:19:22 +02:00
|
|
|
str_name = "%s" % name
|
2015-08-11 14:03:09 +02:00
|
|
|
if _is_hy_builtin(str_name, self.module_name) and \
|
|
|
|
not self.allow_builtins:
|
2014-06-29 19:19:22 +02:00
|
|
|
raise HyTypeError(name,
|
|
|
|
"Can't assign to a builtin: `%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)
|
|
|
|
|
2014-04-29 16:22:55 +02:00
|
|
|
if result.temp_variables \
|
|
|
|
and isinstance(name, HyString) \
|
|
|
|
and '.' not in name:
|
|
|
|
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)
|
2014-04-29 16:22:55 +02:00
|
|
|
result += ast.Assign(
|
|
|
|
lineno=start_line,
|
|
|
|
col_offset=start_column,
|
|
|
|
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
|
|
|
|
2013-11-10 19:00:01 +01:00
|
|
|
@builds("for*")
|
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
|
2013-11-10 19:00:01 +01:00
|
|
|
|
|
|
|
args = expression.pop(0)
|
|
|
|
|
|
|
|
if not isinstance(args, HyList):
|
|
|
|
raise HyTypeError(expression,
|
|
|
|
"for expects a list, received `{0}'".format(
|
|
|
|
type(args).__name__))
|
|
|
|
|
|
|
|
try:
|
|
|
|
target_name, iterable = args
|
|
|
|
except ValueError:
|
|
|
|
raise HyTypeError(expression,
|
|
|
|
"for requires two forms in the list")
|
|
|
|
|
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()
|
2013-11-10 19:00:01 +01:00
|
|
|
# (for* [] body (else …))
|
2013-05-04 09:16:01 +02:00
|
|
|
if expression and expression[-1][0] == HySymbol("else"):
|
|
|
|
else_expr = expression.pop()
|
|
|
|
if len(else_expr) > 2:
|
|
|
|
raise HyTypeError(
|
|
|
|
else_expr,
|
2013-11-10 19:00:01 +01:00
|
|
|
"`else' statement in `for' is too long")
|
2013-05-04 09:16:01 +02:00
|
|
|
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
|
|
|
|
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
|
|
|
|
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-07-14 19:03:08 +02:00
|
|
|
ret.contains_yield = body.contains_yield
|
|
|
|
|
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):
|
2014-12-22 22:52:34 +01:00
|
|
|
elts, ret, _ = self._compile_collect(expression)
|
2013-05-04 09:16:01 +02:00
|
|
|
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
|
|
|
|
2015-06-26 23:47:35 +02:00
|
|
|
@builds(HySet)
|
|
|
|
def compile_set(self, expression):
|
|
|
|
elts, ret, _ = self._compile_collect(expression)
|
2017-04-14 01:09:45 +02:00
|
|
|
ret += ast.Set(elts=elts,
|
|
|
|
ctx=ast.Load(),
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
2015-06-26 23:47:35 +02:00
|
|
|
return ret
|
|
|
|
|
2013-04-07 02:02:08 +02:00
|
|
|
@builds("fn")
|
2017-03-26 03:07:45 +02:00
|
|
|
@builds("fn*")
|
|
|
|
# The starred version is for internal use (particularly, in the
|
|
|
|
# definition of `defn`). It ensures that a FunctionDef is
|
|
|
|
# produced rather than a Lambda.
|
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):
|
2017-03-26 03:07:45 +02:00
|
|
|
force_functiondef = expression.pop(0) == "fn*"
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
arglist = expression.pop(0)
|
2014-12-12 12:41:52 +01:00
|
|
|
if not isinstance(arglist, HyList):
|
|
|
|
raise HyTypeError(expression,
|
2017-02-23 00:36:52 +01:00
|
|
|
"First argument to `fn' must be a list")
|
2015-03-15 22:59:54 +01:00
|
|
|
|
|
|
|
(ret, args, defaults, stararg,
|
|
|
|
kwonlyargs, kwonlydefaults, kwargs) = self._parse_lambda_list(arglist)
|
2015-04-07 22:07:39 +02:00
|
|
|
for i, arg in enumerate(args):
|
|
|
|
if isinstance(arg, HyList):
|
|
|
|
# Destructuring argument
|
|
|
|
if not arg:
|
|
|
|
raise HyTypeError(arglist,
|
|
|
|
"Cannot destruct empty list")
|
|
|
|
args[i] = var = HySymbol(self.get_anon_var())
|
|
|
|
expression = HyExpression([
|
|
|
|
HyExpression([
|
|
|
|
HyString("setv"), arg, var
|
|
|
|
])]
|
|
|
|
) + expression
|
|
|
|
expression = expression.replace(arg[0])
|
2013-05-07 19:37:14 +02:00
|
|
|
|
2014-01-04 02:02:36 +01:00
|
|
|
if PY34:
|
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.
|
|
|
|
args = [ast.arg(arg=ast_str(x),
|
2014-01-02 01:12:17 +01:00
|
|
|
annotation=None, # Fix me!
|
2014-01-02 00:56:09 +01:00
|
|
|
lineno=x.start_line,
|
|
|
|
col_offset=x.start_column) for x in args]
|
|
|
|
|
2015-03-15 22:59:54 +01:00
|
|
|
kwonlyargs = [ast.arg(arg=ast_str(x), annotation=None,
|
|
|
|
lineno=x.start_line,
|
|
|
|
col_offset=x.start_column)
|
|
|
|
for x in kwonlyargs]
|
|
|
|
|
2014-01-02 00:56:09 +01:00
|
|
|
# XXX: Beware. Beware. This wasn't put into the parse lambda
|
|
|
|
# list because it's really just an internal parsing thing.
|
|
|
|
|
|
|
|
if kwargs:
|
2015-12-09 01:43:12 +01:00
|
|
|
kwargs = ast.arg(arg=ast_str(kwargs), annotation=None,
|
|
|
|
lineno=kwargs.start_line,
|
|
|
|
col_offset=kwargs.start_column)
|
2014-01-02 00:56:09 +01:00
|
|
|
|
|
|
|
if stararg:
|
2015-12-09 01:43:12 +01:00
|
|
|
stararg = ast.arg(arg=ast_str(stararg), annotation=None,
|
|
|
|
lineno=stararg.start_line,
|
|
|
|
col_offset=stararg.start_column)
|
2014-01-02 00:56:09 +01:00
|
|
|
|
|
|
|
# Let's find a better home for these guys.
|
|
|
|
else:
|
|
|
|
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]
|
|
|
|
|
2015-03-15 22:59:54 +01:00
|
|
|
if PY3:
|
|
|
|
kwonlyargs = [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 kwonlyargs]
|
|
|
|
|
2015-12-09 01:43:12 +01:00
|
|
|
if kwargs:
|
|
|
|
kwargs = ast_str(kwargs)
|
|
|
|
|
|
|
|
if stararg:
|
|
|
|
stararg = ast_str(stararg)
|
|
|
|
|
2013-05-07 19:37:14 +02:00
|
|
|
args = ast.arguments(
|
2014-01-02 00:56:09 +01:00
|
|
|
args=args,
|
2013-05-07 19:37:14 +02:00
|
|
|
vararg=stararg,
|
|
|
|
kwarg=kwargs,
|
2015-03-15 22:59:54 +01:00
|
|
|
kwonlyargs=kwonlyargs,
|
|
|
|
kw_defaults=kwonlydefaults,
|
2013-05-07 19:37:14 +02:00
|
|
|
defaults=defaults)
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
body = self._compile_branch(expression)
|
2017-03-26 03:07:45 +02:00
|
|
|
if not force_functiondef and not body.stmts:
|
2013-05-07 19:37:14 +02:00
|
|
|
ret += ast.Lambda(
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column,
|
|
|
|
args=args,
|
|
|
|
body=body.force_expr)
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
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:
|
|
|
|
body += ast.Return(value=body.expr,
|
|
|
|
lineno=body.expr.lineno,
|
|
|
|
col_offset=body.expr.col_offset)
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
if not body.stmts:
|
|
|
|
body += ast.Pass(lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column)
|
|
|
|
|
2013-05-07 19:37:14 +02:00
|
|
|
name = self.get_anon_fn()
|
|
|
|
|
2013-05-04 09:16:01 +02:00
|
|
|
ret += ast.FunctionDef(name=name,
|
|
|
|
lineno=expression.start_line,
|
|
|
|
col_offset=expression.start_column,
|
2013-05-07 19:37:14 +02:00
|
|
|
args=args,
|
2013-05-04 09:16:01 +02:00
|
|
|
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-04-24 22:18:05 +02:00
|
|
|
@builds("defclass")
|
|
|
|
@checkargs(min=1)
|
2015-08-04 16:43:07 +02:00
|
|
|
def compile_class_expression(self, expressions):
|
2015-08-10 10:58:13 +02:00
|
|
|
def rewire_init(expr):
|
|
|
|
new_args = []
|
|
|
|
if expr[0] == HySymbol("setv"):
|
|
|
|
pairs = expr[1:]
|
|
|
|
while len(pairs) > 0:
|
|
|
|
k, v = (pairs.pop(0), pairs.pop(0))
|
|
|
|
if k == HySymbol("__init__"):
|
|
|
|
v.append(HySymbol("None"))
|
|
|
|
new_args.append(k)
|
|
|
|
new_args.append(v)
|
|
|
|
expr = HyExpression([
|
|
|
|
HySymbol("setv")
|
|
|
|
] + new_args).replace(expr)
|
|
|
|
|
|
|
|
return expr
|
|
|
|
|
2015-08-04 16:43:07 +02:00
|
|
|
expressions.pop(0) # class
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2015-08-04 16:43:07 +02:00
|
|
|
class_name = expressions.pop(0)
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2015-08-04 16:43:07 +02:00
|
|
|
if expressions:
|
|
|
|
base_list = expressions.pop(0)
|
2013-04-24 22:18:05 +02:00
|
|
|
if not isinstance(base_list, HyList):
|
2015-08-04 16:43:07 +02:00
|
|
|
raise HyTypeError(expressions,
|
2013-04-24 22:18:05 +02:00
|
|
|
"Bases class must be a list")
|
2014-12-22 22:52:34 +01:00
|
|
|
bases_expr, bases, _ = self._compile_collect(base_list)
|
2013-04-24 22:18:05 +02:00
|
|
|
else:
|
|
|
|
bases_expr = []
|
|
|
|
bases = Result()
|
|
|
|
|
|
|
|
body = Result()
|
|
|
|
|
2013-07-15 01:25:57 +02:00
|
|
|
# grab the doc string, if there is one
|
2015-08-04 16:43:07 +02:00
|
|
|
if expressions and isinstance(expressions[0], HyString):
|
|
|
|
docstring = expressions.pop(0)
|
2013-07-15 01:25:57 +02:00
|
|
|
symb = HySymbol("__doc__")
|
|
|
|
symb.start_line = docstring.start_line
|
|
|
|
symb.start_column = docstring.start_column
|
|
|
|
body += self._compile_assign(symb, docstring,
|
|
|
|
docstring.start_line,
|
|
|
|
docstring.start_column)
|
|
|
|
body += body.expr_as_stmt()
|
|
|
|
|
2015-08-11 14:03:09 +02:00
|
|
|
allow_builtins = self.allow_builtins
|
|
|
|
self.allow_builtins = True
|
2015-08-04 16:43:07 +02:00
|
|
|
if expressions and isinstance(expressions[0], HyList) \
|
|
|
|
and not isinstance(expressions[0], HyExpression):
|
|
|
|
expr = expressions.pop(0)
|
2015-08-10 10:58:13 +02:00
|
|
|
expr = HyExpression([
|
|
|
|
HySymbol("setv")
|
|
|
|
] + expr).replace(expr)
|
|
|
|
body += self.compile(rewire_init(expr))
|
2015-08-04 16:43:07 +02:00
|
|
|
|
|
|
|
for expression in expressions:
|
2015-12-23 21:13:18 +01:00
|
|
|
expr = rewire_init(macroexpand(expression, self))
|
2015-08-10 10:58:13 +02:00
|
|
|
body += self.compile(expr)
|
2013-04-24 22:18:05 +02:00
|
|
|
|
2015-08-11 14:03:09 +02:00
|
|
|
self.allow_builtins = allow_builtins
|
|
|
|
|
2013-04-24 22:18:05 +02:00
|
|
|
if not body.stmts:
|
2015-08-04 16:43:07 +02:00
|
|
|
body += ast.Pass(lineno=expressions.start_line,
|
|
|
|
col_offset=expressions.start_column)
|
2013-04-24 22:18:05 +02:00
|
|
|
|
|
|
|
return bases + ast.ClassDef(
|
2015-08-04 16:43:07 +02:00
|
|
|
lineno=expressions.start_line,
|
|
|
|
col_offset=expressions.start_column,
|
2013-04-24 22:18:05 +02:00
|
|
|
decorator_list=[],
|
|
|
|
name=ast_str(class_name),
|
|
|
|
keywords=[],
|
|
|
|
starargs=None,
|
|
|
|
kwargs=None,
|
|
|
|
bases=bases_expr,
|
|
|
|
body=body.stmts)
|
|
|
|
|
2013-12-15 17:47:24 +01:00
|
|
|
def _compile_time_hack(self, expression):
|
|
|
|
"""Compile-time hack: we want to get our new macro now
|
|
|
|
We must provide __name__ in the namespace to make the Python
|
|
|
|
compiler set the __module__ attribute of the macro function."""
|
|
|
|
hy.importer.hy_eval(expression,
|
|
|
|
compile_time_ns(self.module_name),
|
|
|
|
self.module_name)
|
|
|
|
|
|
|
|
# We really want to have a `hy` import to get hy.macro in
|
|
|
|
ret = self.compile(expression)
|
|
|
|
ret.add_imports('hy', [None])
|
|
|
|
return ret
|
|
|
|
|
2013-05-11 09:10:34 +02:00
|
|
|
@builds("defmacro")
|
|
|
|
@checkargs(min=1)
|
|
|
|
def compile_macro(self, expression):
|
|
|
|
expression.pop(0)
|
|
|
|
name = expression.pop(0)
|
|
|
|
if not isinstance(name, HySymbol):
|
2013-05-11 19:59:19 +02:00
|
|
|
raise HyTypeError(name, ("received a `%s' instead of a symbol "
|
|
|
|
"for macro name" % type(name).__name__))
|
2013-05-11 09:10:34 +02:00
|
|
|
name = HyString(name).replace(name)
|
2016-07-07 18:24:04 +02:00
|
|
|
for kw in ("&kwonly", "&kwargs", "&key"):
|
2015-10-13 23:48:23 +02:00
|
|
|
if kw in expression[0]:
|
|
|
|
raise HyTypeError(name, "macros cannot use %s" % kw)
|
2013-05-11 09:10:34 +02:00
|
|
|
new_expression = HyExpression([
|
2013-05-11 20:32:35 +02:00
|
|
|
HyExpression([HySymbol("hy.macros.macro"), name]),
|
|
|
|
HyExpression([HySymbol("fn")] + expression),
|
2013-05-11 09:10:34 +02:00
|
|
|
]).replace(expression)
|
2013-05-11 20:32:35 +02:00
|
|
|
|
2013-12-15 17:47:24 +01:00
|
|
|
ret = self._compile_time_hack(new_expression)
|
2013-05-11 20:32:35 +02:00
|
|
|
|
2013-12-15 17:47:24 +01:00
|
|
|
return ret
|
|
|
|
|
2017-06-21 05:48:54 +02:00
|
|
|
@builds("deftag")
|
2014-01-14 02:38:16 +01:00
|
|
|
@checkargs(min=2)
|
2017-06-21 05:48:54 +02:00
|
|
|
def compile_tag_macro(self, expression):
|
2013-12-15 17:47:24 +01:00
|
|
|
expression.pop(0)
|
|
|
|
name = expression.pop(0)
|
2017-05-10 03:54:32 +02:00
|
|
|
if name == ":" or name == "&":
|
2017-06-21 05:48:54 +02:00
|
|
|
raise NameError("%s can't be used as a tag macro name" % name)
|
2015-12-17 12:59:33 +01:00
|
|
|
if not isinstance(name, HySymbol) and not isinstance(name, HyString):
|
2013-12-15 17:47:24 +01:00
|
|
|
raise HyTypeError(name,
|
|
|
|
("received a `%s' instead of a symbol "
|
2017-06-21 05:48:54 +02:00
|
|
|
"for tag macro name" % type(name).__name__))
|
2013-12-15 17:47:24 +01:00
|
|
|
name = HyString(name).replace(name)
|
|
|
|
new_expression = HyExpression([
|
2017-06-21 05:48:54 +02:00
|
|
|
HyExpression([HySymbol("hy.macros.tag"), name]),
|
2013-12-15 17:47:24 +01:00
|
|
|
HyExpression([HySymbol("fn")] + expression),
|
|
|
|
]).replace(expression)
|
|
|
|
|
|
|
|
ret = self._compile_time_hack(new_expression)
|
2013-05-11 20:32:35 +02:00
|
|
|
|
|
|
|
return ret
|
2013-05-11 09:10:34 +02:00
|
|
|
|
2017-06-21 05:48:54 +02:00
|
|
|
@builds("dispatch_tag_macro")
|
2014-01-14 02:38:16 +01:00
|
|
|
@checkargs(exact=2)
|
2017-06-21 05:48:54 +02:00
|
|
|
def compile_dispatch_tag_macro(self, expression):
|
|
|
|
expression.pop(0) # dispatch-tag-macro
|
2017-05-10 03:54:32 +02:00
|
|
|
tag = expression.pop(0)
|
|
|
|
if not type(tag) == HyString:
|
2014-01-14 02:38:16 +01:00
|
|
|
raise HyTypeError(
|
2017-05-10 03:54:32 +02:00
|
|
|
tag,
|
2017-06-21 05:48:54 +02:00
|
|
|
"Trying to expand a tag macro using `{0}' instead "
|
2017-05-10 03:54:32 +02:00
|
|
|
"of string".format(type(tag).__name__),
|
2014-01-14 02:38:16 +01:00
|
|
|
)
|
2017-05-10 03:54:32 +02:00
|
|
|
tag = HyString(hy_symbol_mangle(str(tag))).replace(tag)
|
2017-06-21 05:48:54 +02:00
|
|
|
expr = tag_macroexpand(tag, expression.pop(0), self)
|
2014-01-14 02:38:16 +01:00
|
|
|
return self.compile(expr)
|
|
|
|
|
2013-06-05 11:41:58 +02:00
|
|
|
@builds("eval_and_compile")
|
|
|
|
def compile_eval_and_compile(self, expression):
|
2015-08-09 09:00:51 +02:00
|
|
|
expression[0] = HySymbol("do")
|
2013-06-02 17:21:03 +02:00
|
|
|
hy.importer.hy_eval(expression,
|
|
|
|
compile_time_ns(self.module_name),
|
|
|
|
self.module_name)
|
|
|
|
expression.pop(0)
|
|
|
|
return self._compile_branch(expression)
|
|
|
|
|
2013-06-05 11:41:58 +02:00
|
|
|
@builds("eval_when_compile")
|
|
|
|
def compile_eval_when_compile(self, expression):
|
2015-08-09 09:00:51 +02:00
|
|
|
expression[0] = HySymbol("do")
|
2013-06-05 11:41:58 +02:00
|
|
|
hy.importer.hy_eval(expression,
|
|
|
|
compile_time_ns(self.module_name),
|
|
|
|
self.module_name)
|
|
|
|
return Result()
|
|
|
|
|
2013-05-16 18:59:20 +02:00
|
|
|
@builds(HyCons)
|
|
|
|
def compile_cons(self, cons):
|
|
|
|
raise HyTypeError(cons, "Can't compile a top-level cons cell")
|
|
|
|
|
2013-03-06 00:39:34 +01:00
|
|
|
@builds(HyInteger)
|
2013-04-10 14:26:16 +02:00
|
|
|
def compile_integer(self, number):
|
2013-08-21 01:09:03 +02:00
|
|
|
return ast.Num(n=long_type(number),
|
2013-04-10 14:26:16 +02:00
|
|
|
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)
|
2016-11-30 06:00:48 +01:00
|
|
|
|
|
|
|
if not glob:
|
|
|
|
raise HyTypeError(symbol, 'cannot access attribute on '
|
|
|
|
'anything other than a name '
|
|
|
|
'(in order to get attributes of'
|
|
|
|
'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
|
|
|
|
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-07-08 04:36:26 +02:00
|
|
|
if symbol in _stdlib:
|
|
|
|
self.imports[_stdlib[symbol]].add(symbol)
|
|
|
|
|
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)
|
|
|
|
|
2017-02-19 01:15:58 +01:00
|
|
|
@builds(HyBytes)
|
|
|
|
def compile_bytes(self, bytestring):
|
|
|
|
f = ast.Bytes if PY3 else ast.Str
|
|
|
|
return f(s=bytes_type(bytestring),
|
|
|
|
lineno=bytestring.start_line,
|
|
|
|
col_offset=bytestring.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):
|
2014-12-22 22:52:34 +01:00
|
|
|
keyvalues, ret, _ = self._compile_collect(m)
|
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-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
|
|
|
|
2017-06-23 01:32:29 +02:00
|
|
|
body = compiler.imports_as_stmts(tree) + result.stmts
|
2013-05-04 09:16:01 +02:00
|
|
|
|
|
|
|
ret = root(body=body)
|
|
|
|
|
|
|
|
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
|