grammar for where/letting/with and suite expressions (thunks etc)

B

Bengt Richter

We have where syntax in combination with suite expression syntax (bear with me,
I think a good synergy will emerge ;-)

http://groups.google.co.uk/[email protected]
http://groups.google.co.uk/[email protected]

are the key referneces for background (I'm just repeating from Oren's post for convenience).

The first suggests a grammar mod for its proposed "with" (rather than the name "where"),
but it doesn't cover the variations we are discussing now.

If "where" is not a good word, and "with" is destined for use that may clash, how about "letting"
or if that's too long, "per" ? Anyway, the new stuff adds the new compound statements

Here's a cut at Grammar modifications: (though I guess the grammar in

http://www.python.org/doc/current/ref/grammar.txt

is really closer to actual parsing. BTW, how is the Grammar file that comes with the sources maintained along with the former?
E.g., I didn't see how the latter parses a non-assignment expression as a statement, e.g.
NAME '(' ')'
Maybe I have a glitched version of Grammar?

But anyway, since I did this already, it will serve as a sketch:

compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef | letting_stmt | named_suite_stmt
small_stmt: tiny_stmt | flow_stmt | import_stmt | global_stmt | exec_stmt
tiny_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | import_stmt | assert_stmt

letting_stmt: tiny_stmt 'letting' ( let_stmt | ':' suite )
let_stmt: named_suite_stmt | assignments_stmt
assignments_stmt: expr_stmt??

named_suite_stmt: NAME suite_expr
suite_expr: '::' suite | parameters ':' suite | 'def' parameters ':' suite


I split out a tiny_stmt subset of small_stmt so we won't accept "continue letting x=5" and such.

Basically we have either

<tiny_stmt> letting:
<suite> # meaning any possible suite, but obviously should concern itself
# with binding names for tiny_stmt to use

or
<tiny_stmt> letting <let_stmt>

where <let_stmt> binds names by assignment, or by a named_suite_stmt, e.g, (assuming a bare expression is parsed as an expr_stmt)

foo(x) letting x=1 # simple let_stmt as assignmerns_stmt
foo(x, y) letting x,y = 1,2 # assignments_stmt should exclude augassign operators so is not ==
safe_open(tk, 'data.txt') letting tk(f):
# this suite is thunk tk's body, called back by foo, passing safely opened f as arg
longest = max(len(line) for line in f) # save result longest in local namespace

If you put the ':' after letting, you introduce a general suite whose bindings will override any name bindings
otherwise used by the preceding tiny_stmt, e.g, the above in letting: form

foo(x) letting:
x=1 # simple let_stmt as assignmerns_stmt
foo(x, y) letting:
x,y = 1,2 # assignments_stmt should exclude augassign operators so is not ==
safe_open(tk, 'data.txt') letting:
tk(f):
# this suite is thunk tk's body, called back by foo, passing safely opened f as arg
longest = max(len(line) for line in f) # save result longest in local namespace

Note that tk(f): and its suite work just like a def, except for binding a thunk instead. You can also
bind tk to the thunk expression, analogous to binding a lambda to what a def name would bind, e.g.,
This is will do the same as above, though the isolated (f): may look unfamiliar, ;-)
safe_open(tk, 'data.txt') letting:
tk = (f):
# this suite is thunk tk's body, called back by foo, passing safely opened f as arg
longest = max(len(line) for line in f) # save result longest in local namespace

Since letting: introduces a full-fledged suite, nested lettings of both kinds become possible, e.g.

foo(tk, func, *args, **kw) letting:
kw = dict(tuplist) letting:
tuplist = [(1,'one'), (2,'two')] # silly example
args = d.values() letting d:: # d becomes dict of suite bindings
a = 123
def foo(x):
tk(logitem): # tk becomes thunk with body defined in following suite
print logitem
def func(x, y): # func is ordinary but transient function
return 'Hi from fun with', (x,y)

What I haven't covered is the same-indentation aligned 'with' following a statement, as seems permitted
in Nick reference post where he says
"""
Grammar Change
--------------
Current::
statement ::= stmt_list NEWLINE | compound_stmt

New::
statement ::= (stmt_list NEWLINE | compound_stmt) [local_namespace]
local_namespace ::= "with" ":" suite
"""

I'll have to leave that for now. Anyway, hope this all evolves into something neat ;-)

Actually, I don't like "letting" that much as the word. I keep having to type where^H^H^H^H^Hletting ;-)
"per" would be short, but maybe not mnemonic enough. "with" might be good, but I am allowing
with not to have a colon, e.g. with a single thunk definition for a function call:

final_status = safe_open(tk, 'data.txt') with tk(f):
longest = max(len(line) for line in f)

Since my 'letting' is effectively a tiny_stmt trailer and anticipated with usage is more header like,
maybe that is enough to disambiguate. I'll have to see the future with grammar.


Hm, Let's see if Nick Coghlan's summary of examples can be handled with letting:

Examples
--------

# Statement local functions (from Andrey Tatarinov)
# aka How to cope if lambda goes away :)
res = [ f(i) for i in objects ] with:
def f(x):
#do something
OK as is.

# Declaring properties (from Nick Coghlan)
class C(object):
x = property(get, set) with:
def get(self):
pass
def set(self, value):
pass
OK as is.

# Design by contract (from Nick Coghlan)
@dbc(pre, post)
def foo():
pass
with:
def pre():
pass
def post():
pass

Problem, but if decorator expressions were allowed to have letting,

@dbc(pre, post) letting:
def pre(): pass
def post(): pass
def foo():
pass

# Singleton classes (from Paul Rubin)
C = C() with:
class C:
pass
OK as is.

# Complex default values (from Carlos Ribeiro)
def f(x=default()):
pass
with:
def default():
pass

Since this is not decorated, I can do it with an anonymous def suite expression
which, being an expression (making the whole a simple expr assignment) allows letting:

f = def(x=default()):
pass
letting: # dedent to this line ends anonymous def suite and goes back to expression context
def default():
pass

To show the expression nature better, (not that it's a preferred spelling ;-),
the expression is parenthesized, and closes with the closing ')'

f = (def(x=default()):
pass
) letting:
def default():
pass
or uglier:
f = (def(x=default()):
pass) letting:
def default():
pass

You could even write a one-liner for this, since the suites are one-liner passes ;-)

f = (def(x=default()):pass) letting default=(def():pass)

So there was really only a problem with the decorator.
But I gotta go for now.

Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top