Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
Conflicts:
	macropy/__init__.py
	readme.md
  • Loading branch information
lihaoyi committed Jul 5, 2013
2 parents 1fe1e2f + bd13b06 commit ae3c528
Show file tree
Hide file tree
Showing 18 changed files with 459 additions and 175 deletions.
14 changes: 14 additions & 0 deletions changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Changelog
=========

1.0.2
-----
Removed unit test from PyPI distribution

1.0.1
-----
- Fixed a bug in `ast_ctx_fixer`
- `gen_sym()` is now `gen_sym(name="sym")`, allowing you to override the base name
- Implemented `macropy.case_classes.enum` macro
- Implemented `macropy.quick_lambda.lazy` and `macropy.quick_lambda.interned` macros

2 changes: 1 addition & 1 deletion macropy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ def console():

import core.exporters

__version__ = "1.0.0"
__version__ = "1.0.2"
exporter = core.exporters.NullExporter()
194 changes: 144 additions & 50 deletions macropy/case_classes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Macro providing an extremely concise way of declaring classes"""
from macropy.core.macros import *
from macropy.core.hquotes import macros, hq, name, unhygienic
from macropy.core.hquotes import macros, hq, name, unhygienic, u

macros = Macros()

Expand Down Expand Up @@ -37,6 +37,47 @@ def __iter__(self):
yield getattr(self, x)


class Enum(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, "all"):
cls.all = []
thing = object.__new__(cls, *args, **kw)
cls.all.append(thing)
return thing

@property
def next(self):
return self.__class__.all[(self.id + 1) % len(self.__class__.all)]
@property
def prev(self):
return self.__class__.all[(self.id - 1) % len(self.__class__.all)]

def __str__(self):
return self.__class__.__name__ + "." + self.name

def __repr__(self):
return self.__str__()


def __iter__(self):
for x in self.__class__._fields:
yield getattr(self, x)

def enum_new(cls, **kw):
if len(kw) != 1:
raise TypeError("Enum selection can only take exactly 1 named argument: " + len(kw) + " found.")

[(k, v)] = kw.items()

for value in cls.all:
if getattr(value, k) == v:
return value

raise ValueError("No Enum found for %s=%s" % (k, v))

def noop_init(*args, **kw):
pass

def extract_args(init_fun, bases):
args = []
vararg = None
Expand All @@ -62,6 +103,7 @@ def extract_args(init_fun, bases):
all_args.append(kwarg)
return args, vararg, kwarg, defaults, all_args


@Walker
def find_member_assignments(tree, collect, stop, **kw):
if type(tree) in [GeneratorExp, Lambda, ListComp, DictComp, SetComp, FunctionDef, ClassDef]:
Expand All @@ -75,16 +117,14 @@ def find_member_assignments(tree, collect, stop, **kw):
]
map(collect, self_assigns)

@macros.decorator
def case(tree, gen_sym, **kw):
"""Macro providing an extremely concise way of declaring classes"""
def split_body(tree):

def split_body(tree, gen_sym):
new_body = []
outer = []
init_body = []
for statement in tree.body:
if type(statement) is ClassDef:
outer.append(case_transform(statement, [Name(id=tree.name, ctx=Load())]))
outer.append(case_transform(statement, gen_sym, [Name(id=tree.name)]))
with hq as a:
name[tree.name].b = name[statement.name]
a_old = a[0]
Expand All @@ -98,67 +138,121 @@ def split_body(tree):
init_body.append(statement)
return new_body, outer, init_body

def prep_initialization(init_fun, args, vararg, kwarg, defaults, all_args):

init_fun.args = arguments(
args = [Name(id="self")] + [Name(id = id) for id in args],
vararg = vararg,
kwarg = kwarg,
defaults = defaults
)
def prep_initialization(init_fun, args, vararg, kwarg, defaults, all_args):

init_fun.args = arguments(
args = [Name(id="self")] + [Name(id = id) for id in args],
vararg = vararg,
kwarg = kwarg,
defaults = defaults
)

for x in all_args:
with hq as a:
unhygienic[self.x] = name[x]
for x in all_args:
with hq as a:
unhygienic[self.x] = name[x]

a[0].targets[0].attr = x
a[0].targets[0].attr = x

init_fun.body.append(a[0])
init_fun.body.append(a[0])

def case_transform(tree, parents):

with hq as methods:
def __init__(self, *args, **kwargs):
pass
def shared_transform(tree, gen_sym, additional_args=[]):
with hq as methods:
def __init__(self, *args, **kwargs):
pass

_fields = []
_varargs = None
_kwargs = None
__slots__ = []
_fields = []
_varargs = None
_kwargs = None
__slots__ = []

init_fun, set_fields, set_varargs, set_kwargs, set_slots, = methods
init_fun, set_fields, set_varargs, set_kwargs, set_slots, = methods

args, vararg, kwarg, defaults, all_args = extract_args(init_fun, tree.bases)
args, vararg, kwarg, defaults, all_args = extract_args(init_fun, [Name(id=x) for x in additional_args] + tree.bases)
if vararg:
set_varargs.value = Str(vararg)
if kwarg:
set_kwargs.value = Str(kwarg)
additional_members = find_member_assignments.collect(tree.body)
prep_initialization(init_fun, args, vararg, kwarg, defaults, all_args)
set_fields.value.elts = map(Str, args)
set_slots.value.elts = map(Str, all_args + additional_members)
new_body, outer, init_body = split_body(tree, gen_sym)
init_fun.body.extend(init_body)
tree.body = new_body
tree.body = methods + tree.body
return outer


def case_transform(tree, gen_sym, parents):

outer = shared_transform(tree, gen_sym)

tree.bases = parents
assign = FunctionDef(
gen_sym("prepare_"+tree.name),
arguments([], None, None, []),
outer,
[hq[apply]]
)
return [tree] + ([assign] if len(outer) > 0 else [])

if vararg:
set_varargs.value = Str(vararg)
if kwarg:
set_kwargs.value = Str(kwarg)
@macros.decorator
def case(tree, gen_sym, **kw):
"""Macro providing an extremely concise way of declaring classes"""
x = case_transform(tree, gen_sym, [hq[CaseClass]])

additional_members = find_member_assignments.collect(tree.body)
return x

prep_initialization(init_fun, args, vararg, kwarg, defaults, all_args)
set_fields.value.elts = map(Str, args)
set_slots.value.elts = map(Str, all_args + additional_members)

new_body, outer, init_body = split_body(tree)
init_fun.body.extend(init_body)
@macros.decorator
def enum(tree, gen_sym, **kw):

count = [0]
new_assigns = []
new_body = []
def handle(expr):
assert type(expr) in (Name, Call), stmt.value
if type(expr) is Name:
expr.ctx = Store()
self_ref = Attribute(value=Name(id=tree.name), attr=expr.id)
with hq as code:
ast[self_ref] = name[tree.name](u[count[0]], u[expr.id])
new_assigns.extend(code)
count[0] += 1

elif type(expr) is Call:
assert type(expr.func) is Name
self_ref = Attribute(value=Name(id=tree.name), attr=expr.func.id)
id = expr.func.id
expr.func = Name(id=tree.name)

expr.args = [Num(count[0]), Str(id)] + expr.args
new_assigns.append(Assign([self_ref], expr))
count[0] += 1

assert all(type(x) in (Expr, FunctionDef) for x in tree.body)

for stmt in tree.body:
if type(stmt) is Expr:
assert type(stmt.value) in (Tuple, Name, Call)
if type(stmt.value) is Tuple:
map(handle, stmt.value.elts)
else:
handle(stmt.value)
else:
new_body.append(stmt)

assign = FunctionDef(
gen_sym(),
arguments([], None, None, []),
outer,
[hq[apply]]
)
tree.body = new_body + [Pass()]

tree.body = new_body
tree.bases = parents
shared_transform(tree, gen_sym, additional_args=["id", "name"])

tree.body = methods + tree.body
with hq as code:
name[tree.name].__new__ = staticmethod(enum_new)
name[tree.name].__init__ = noop_init

return [tree] + ([assign] if len(outer) > 0 else [])

x = case_transform(tree, [hq[CaseClass]])
tree.bases = [hq[Enum]]

return x
return [tree] + new_assigns + code
6 changes: 5 additions & 1 deletion macropy/core/cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def fix_ctx(tree, **kw):


@Walker
def ast_ctx_fixer(tree, ctx, stop, **kw):
def ast_ctx_fixer(tree, ctx, stop, set_ctx, **kw):
"""Fix any missing `ctx` attributes within an AST; allows you to build
your ASTs without caring about that stuff and just filling it in later."""
if "ctx" in type(tree)._fields and (not hasattr(tree, "ctx") or tree.ctx is None):
Expand All @@ -35,6 +35,10 @@ def ast_ctx_fixer(tree, ctx, stop, **kw):
stop()
return tree

if type(tree) is Attribute:
set_ctx(Load())
return tree

if type(tree) is Assign:
for target in tree.targets:
ast_ctx_fixer.recurse(target, Store())
Expand Down
20 changes: 16 additions & 4 deletions macropy/core/gen_sym.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,21 @@ def name_finder(tree, collect, **kw):
if type(tree) is ImportFrom:
names = [x.asname or x.name for x in tree.names]
map(collect, names)
if type(tree) in (FunctionDef, ClassDef):
collect(tree.name)

found_names = name_finder.collect(tree)
names = ("sym" + str(i) for i in itertools.count())
x = itertools.ifilter(lambda x: x not in found_names, names)
return lambda: x.next()
found_names = set(name_finder.collect(tree))

def name_for(name="sym"):

if name not in found_names:
found_names.add(name)
return name
offset = 1
while name + str(offset) in found_names:

offset += 1
found_names.add(name + str(offset))
return name + str(offset)
return name_for

4 changes: 2 additions & 2 deletions macropy/core/hquotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def post_proc(tree, captured_registry, gen_sym, **kw):
if captured_registry == []:
return tree

unpickle_name = gen_sym()
unpickle_name = gen_sym("unpickled")
pickle_import = [
ImportFrom(
module='pickle',
Expand Down Expand Up @@ -53,7 +53,7 @@ def hygienator(tree, stop, **kw):
if type(tree) is Captured:
new_sym = [sym for val, sym in captured_registry if val is tree.val]
if not new_sym:
new_sym = gen_sym()
new_sym = gen_sym(tree.name)

captured_registry.append((tree.val, new_sym))
else:
Expand Down
10 changes: 5 additions & 5 deletions macropy/core/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@ def macro_searcher(tree, **kw):
return tree


file_vars = {
v.func_name: v(tree=tree, src=src, expand_macros=expand_macros)
for v in injected_vars
}
file_vars = {}


for v in injected_vars:
file_vars[v.func_name] = v(tree=tree, src=src, expand_macros=expand_macros, **file_vars)

# you don't pay for what you don't use

allnames = [
(m, name, asname)
Expand Down
2 changes: 1 addition & 1 deletion macropy/core/test/exporters/pyc_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

exporters.pyc_cache_count += 1
f[1]

6 changes: 5 additions & 1 deletion macropy/core/test/gen_sym_macro.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@
@macros.expr
def f(tree, gen_sym, **kw):
symbols = [gen_sym(), gen_sym(), gen_sym(), gen_sym(), gen_sym()]
assert symbols == ["sym0", "sym2", "sym5", "sym6", "sym7"], symbols
assert symbols == ["sym2", "sym5", "sym6", "sym7", "sym8"], symbols
renamed = [gen_sym("max"), gen_sym("max"), gen_sym("run"), gen_sym("run")]
assert renamed == ["max1", "max2", "run1", "run2"], renamed
unchanged = [gen_sym("grar"), gen_sym("grar"), gen_sym("omg"), gen_sym("omg")]
assert unchanged == ["grar", "grar1", "omg", "omg1"], unchanged
return Num(n = 10)
3 changes: 1 addition & 2 deletions macropy/experimental/test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from macropy.test import test_suite, peg
from macropy.test import test_suite
#import js_snippets
#import pattern
import pinq
Expand All @@ -8,7 +8,6 @@
Tests = test_suite(cases = [
#js_snippets,
#pattern,
peg,
#pinq,
pyxl_snippets,
#tco_test
Expand Down
2 changes: 1 addition & 1 deletion macropy/peg.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def PegWalker(tree, stop, collect, **kw):
names = distinct(flatten(b_left))
tree.right.args.args = map(f[Name(id = _)], names)
tree.right.args.defaults = [hq[[]]] * len(names)
tree.right.args.kwarg = gen_sym()
tree.right.args.kwarg = gen_sym("kw")
stop()

return tree
Expand Down
Loading

0 comments on commit ae3c528

Please sign in to comment.