Skip to content

Commit

Permalink
Make fn work the same as lambda
Browse files Browse the repository at this point in the history
That is, allow it to generate a `lambda` instead of a `def` statement if the function body is just an expression.

I've removed two uses of with_decorator in hy.compiler because they'd require adding another case to HyASTCompiler.compile_decorate_expression and they have no ultimate effect, anyway.

In a few tests, I've added a meaningless statement in `fn` bodies to force generation of a `def`.

I've removed `test_fn_compiler_empty_function` rather than rewrite it because it seems like a pain to maintain and not very useful.
  • Loading branch information
Kodiologist committed Feb 19, 2017
1 parent 29bb66c commit 580552a
Show file tree
Hide file tree
Showing 4 changed files with 10 additions and 28 deletions.
9 changes: 3 additions & 6 deletions hy/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2304,13 +2304,12 @@ def compile_set(self, expression):
@builds("fn")
@checkargs(min=1)
def compile_function_def(self, expression):
called_as = expression.pop(0)
expression.pop(0)

arglist = expression.pop(0)
if not isinstance(arglist, HyList):
raise HyTypeError(expression,
"First argument to `{}' must be a list".format(
called_as))
"First argument to `fn' must be a list")

(ret, args, defaults, stararg,
kwonlyargs, kwonlydefaults, kwargs) = self._parse_lambda_list(arglist)
Expand Down Expand Up @@ -2382,7 +2381,7 @@ def compile_function_def(self, expression):
defaults=defaults)

body = self._compile_branch(expression)
if not body.stmts and called_as == "lambda":
if not body.stmts:
ret += ast.Lambda(
lineno=expression.start_line,
col_offset=expression.start_column,
Expand Down Expand Up @@ -2528,7 +2527,6 @@ def compile_macro(self, expression):
if kw in expression[0]:
raise HyTypeError(name, "macros cannot use %s" % kw)
new_expression = HyExpression([
HySymbol("with_decorator"),
HyExpression([HySymbol("hy.macros.macro"), name]),
HyExpression([HySymbol("fn")] + expression),
]).replace(expression)
Expand All @@ -2551,7 +2549,6 @@ def compile_reader(self, expression):
"for reader macro name" % type(name).__name__))
name = HyString(name).replace(name)
new_expression = HyExpression([
HySymbol("with_decorator"),
HyExpression([HySymbol("hy.macros.reader"), name]),
HyExpression([HySymbol("fn")] + expression),
]).replace(expression)
Expand Down
8 changes: 5 additions & 3 deletions tests/compilers/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,11 @@ def test_ast_expression_basics():

def test_ast_anon_fns_basics():
""" Ensure anon fns work. """
code = can_compile("(fn (x) (* x x))").body[0]
code = can_compile("(fn (x) (* x x))").body[0].value
assert type(code) == ast.Lambda
code = can_compile("(fn (x) (print \"multiform\") (* x x))").body[0]
assert type(code) == ast.FunctionDef
code = can_compile("(fn (x))").body[0]
can_compile("(fn (x))")
cant_compile("(fn)")


Expand Down Expand Up @@ -430,7 +432,7 @@ def test_lambda_list_keywords_kwargs():
def test_lambda_list_keywords_kwonly():
"""Ensure we can compile functions with &kwonly if we're on Python
3, or fail with an informative message on Python 2."""
kwonly_demo = "(fn [&kwonly a [b 2]] (print a b))"
kwonly_demo = "(fn [&kwonly a [b 2]] (print 1) (print a b))"
if PY3:
code = can_compile(kwonly_demo)
for i, kwonlyarg_name in enumerate(('a', 'b')):
Expand Down
18 changes: 0 additions & 18 deletions tests/compilers/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,6 @@ def _make_expression(*args):
def setUp(self):
self.c = compiler.HyASTCompiler('test')

def test_fn_compiler_empty_function(self):
ret = self.c.compile_function_def(
self._make_expression(HySymbol("fn"), HyList()))
self.assertEqual(ret.imports, {})

self.assertEqual(len(ret.stmts), 1)
stmt = ret.stmts[0]
self.assertIsInstance(stmt, ast.FunctionDef)
self.assertIsInstance(stmt.args, ast.arguments)
self.assertEqual(stmt.args.vararg, None)
self.assertEqual(stmt.args.kwarg, None)
self.assertEqual(stmt.args.defaults, [])
self.assertEqual(stmt.decorator_list, [])
self.assertEqual(len(stmt.body), 1)
self.assertIsInstance(stmt.body[0], ast.Pass)

self.assertIsInstance(ret.expr, ast.Name)

def test_compiler_bare_names(self):
"""
Check that the compiler doesn't drop bare names from code branches
Expand Down
3 changes: 2 additions & 1 deletion tests/importer/test_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ def test_basics():

def test_stringer():
"Make sure the basics of the importer work"
_ast = import_buffer_to_ast("(defn square [x] (* x x))", '')
_ast = import_buffer_to_ast(
"(defn square [x] (print \"hello\") (* x x))", '')
assert type(_ast.body[0]) == ast.FunctionDef


Expand Down

0 comments on commit 580552a

Please sign in to comment.