SUPL

The Smoothly Unfolding Project Language
Greg Bryant

[Update February 2019: there's code for a working version of SUPL at github.]

Dec 25, 2012

How does one create an example program in a new language, or an example sequence on a new platform, or a sequence intended to demonstrate best practices, of the smoothest unfolding of a solution to a particular problem? Is there sufficient tooling for "working over" that "unfolding example", rewriting it, over and over again, until it is smooth?

SUPL was an early attempt to do this. I created a working version in mid-2009 (original example) but it inspired me to do something quite different (www.grogix.org) so this work remained unpublished.

SUPL lets you create a documented, unfolding description, of any type, and compile, run and edit all the steps, continually, while improving the sequence. It ensures the development of differentiating, structure-preserving code from step-to-step.

As an example of a SUPL description, here's ListItem.supl, which produces the working Google App Engine webapp described in detail here.

The goals for SUPL 2.0 include a web-based IDE which enables the editing, smoothing, and running of a sequence, online, in the sort of format clear below.

Key to SUPL:

.P is a grammar production, either 'start' or a previously declared non-terminal

.nt is a non-terminal

.t is a terminal

.Sequence : 'ListItem'

A trivial Google App Engine web application

enabling the user to create lists with items

(This automatically puts everything in a directory gen__)

--------------------------------------------------------------------------

.step 1 : 'form'

Starting morphology: overall shape

.P : 'start'

.nt : 'form main file'

.nt : 'form other files'

--------------------------------------------------------------------------

.step 2 : 'form: the GAE target morphology'

Starting Morphology (position)

POSITIONS / PRODUCTIONS / MORPHOLOGY

.P : 'form main file'

.file : 'ListItem.py'

.t : 'name'

<<<

# ListItem.py

>>>

.t : 'import'

<<<

import cgi

import os

from google.appengine.ext import db

from google.appengine.ext import webapp

from google.appengine.ext.webapp import template

from google.appengine.ext.webapp.util import run_wsgi_app

>>>

.nt : 'db'

.nt : 'pages'

.nt : 'handler map'

.t : 'main'

<<<

def main():

run_wsgi_app(application)

if __name__ == "__main__":

main()

>>>

.P : 'form other files'

.nt : 'homepage template'

.nt : 'launch file'

--------------------------------------------------------------------------

.step 3 : 'data and purpose'

There is a simple data model that reflects the purpose of the application

In our case, two types of data, one forming a List of the others. A List key is then required for the Item members.

PRINCIPLES

.template : 'db_model' : 'NAME'

<<<

class NAME(db.Model):

name = db.StringProperty(multiline=True)

>>>

.template : 'db_model_ref' : 'NAME', 'REF', 'KEY_LABEL'

<<<

class NAME(db.Model):

KEY_LABEL = db.ReferenceProperty(REF)

name = db.StringProperty(multiline=True)

>>>

(POSITIONS / PRODUCTIONS / MORPHOLOGY) + (PARAMETERS)

.P : 'db'

.t : db_model('List')

.t : db_model_ref ('Item', 'List', 'list_key')

--------------------------------------------------------------------------

.step 4 : 'Functional Centers and the First Bridge'

There are functional centers, which reflect the primary actions of the application, and the technology in which the application runs.

POSITIONS / PRODUCTIONS / MORPHOLOGY

.P : 'pages'

.nt : 'home page'

.nt : 'creating lists'

.nt : 'creating items'

--------------------------------------------------------------------------

.step 5 : 'A page and its renderer'

A page consists of handlers and a renderer

POSITIONS / PRODUCTIONS / MORPHOLOGY

.P : 'home page'

.nt : 'home page handler'

.nt : 'home page render'

--------------------------------------------------------------------------

.step 6 : 'Class and Method Morphology: Pages'

One page, the home page, in this case

.P : 'home page handler'

.t : 'code'

<<<

class HomePage(webapp.RequestHandler):

def get(self):

template_values = {

'list_form': 0,

'item_form': 0

}

self.response.out.write(render(template_values))

>>>

.P : 'home page render'

.nt : 'home page render title'

.nt : 'home page render parameters'

.nt : 'render body'

--------------------------------------------------------------------------

.step 7 : 'Class and Method Morphology: Feature handlers'

Non-page handlers: for the web, has a GET handler, which sets up the feature and a POST handler, which does it.

.P : 'creating lists'

.nt : 'creating lists title'

.nt : 'creating lists parameters'

.nt : 'creating lists get handler'

.nt : 'creating lists post handler'

.P : 'creating items'

.nt : 'creating items title'

.nt : 'creating items parameters'

.nt : 'creating items get handler'

.nt : 'creating items post handler'

--------------------------------------------------------------------------

.step 8 : 'Titles: The Class and Method Bridge'

The functional centers are expressed in the form of methods and classes. Among these, the Handlers form a distinct group.

.template : 'class_title' : 'NAME'

<<<

class NAME

>>>

.template : 'method_title' : 'NAME'

<<<

def NAME

>>>

.P : 'home page render title'

.t : method_title ('render')

.P : 'home page handler title'

.t : class_title ('HomePage')

.P : 'creating lists title'

.t : class_title ('CreateList')

.P : 'creating items title'

.t : class_title ('CreateItem')

--------------------------------------------------------------------------

.step 9 : 'Parameters: for Render and Handler Shells'

The handler classes, the calls to render, have explicit arguments.

This is for all the 'parameter' non-teminals above.

.P : 'home page render parameters'

.t : 'for populating templates'

<<<

(template_values):

>>>

.P : 'home page handler parameters'

.t : 'handler class'

<<<

(webapp.RequestHandler):

>>>

.P : 'creating lists parameters'

.t : 'handler class'

<<<

(webapp.RequestHandler):

>>>

.P : 'creating items parameters'

.t : 'handler class'

<<<

(webapp.RequestHandler):

>>>

--------------------------------------------------------------------------

.step 10 : 'Shape of a handler map'

.P : 'handler map'

.t : 'app instantiation'

<<<

application = webapp.WSGIApplication([

>>>

.nt : 'URL handler list'

.t : 'debug flag'

<<<

],debug=True)

>>>

--------------------------------------------------------------------------

.step 11 : 'handler list'

.P : 'URL handler list'

.t : 'pages'

<<<

('/', HomePage),

>>>

.t : 'forms with keys'

<<<

('/item_form/(.*)/', CreateItem),

>>>

.t : 'forms without keys'

<<<

('/list_form/', CreateList),

>>>

.t : 'posts'

<<<

('/create_list/', CreateList),

('/create_item/', CreateItem)

>>>

--------------------------------------------------------------------------

.step 12 : 'template valve'

.P : 'render body'

.t : 'name and parameters'

<<<

def render (template_values):

>>>

.nt : 'template payload'

.t : 'get template file'

<<<

path = os.path.join(os.path.dirname(__file__), 'index.html')

>>>

.t : 'return popluated template'

<<<

return(template.render(path, template_values))

>>>

--------------------------------------------------------------------------

.step 13 : 'Form Get Methods'

.P : 'creating lists get handler'

.t : 'call with self'

<<<

def get (self):

>>>

.t : 'template dictionary'

<<<

template_values = {

'list_form': 1

}

>>>

.t : 'http response with render'

<<<

self.response.out.write (render(template_values))

>>>

.P : 'creating items get handler'

.t : 'call with self and key'

<<<

def get (self, list_key):

>>>

.t : 'template dictionary'

<<<

template_values = {

'item_form': 1,

'list_key': list_key

}

>>>

.t : 'http response with render'

<<<

self.response.out.write (render(template_values))

>>>

--------------------------------------------------------------------------

.step 14 : 'Template outline'

.P : 'homepage template'

.file : 'index.html'

.t : 'html top'

<<<

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Passing Keys</title>

<meta http-equiv="Content-Type" content="text/html;

charset=UTF-8" >

</head>

<body>

<a href="/"><font size="+3"><b>Passing Keys</b></font></a><br>

<br><br>

>>>

.nt: 'homepage template list form'

.t : 'list iteration'

<<<

{% for list in lists %}

<font size="+3"><b>{{ list.name }}</b></font><br>

{% for item in list.items %}

&nbsp;&nbsp;&nbsp;<b>*</b> {{ item.name }} <br>

{% endfor %}

>>>

.nt : 'homepage template item form'

.t : 'html tail'

<<<

{% endfor %}

</body>

</html>

>>>

--------------------------------------------------------------------------

.step 15 : 'List Form Pathway'

.P : 'homepage template list form'

.t : 'condition and html'

<<<

{% if list_form %}

<form action="/create_list/" method="post">

<textarea style="background:#eeee00" name="name" rows=1 cols=33></textarea><br>

<span align="left"><input type="Submit" name="button" value="Create list"></span>

<span style="padding-left:138px"><a href="/">cancel</a></span><br>

</form>

{% else %}

<br>&nbsp;<a href="/list_form/">Create a List</a><br><br>

{% endif %}

>>>

--------------------------------------------------------------------------

.step 16 : 'Item Form Pathway'

.P : 'homepage template item form'

.t : 'if form and this item'

<<<

{% if item_form %}

{% ifequal list.key list_key %}

<a name="form"></a>

<form action="/create_item/" method="post">

<span style="padding-left:15px">

<textarea style="background:#eeee00" name="name"

rows=1 cols=33></textarea><br>

</span>

<span style="padding-left:15px">

<input type="Submit" name="button" value="Create an item">

</span>

<span style="padding-left:130px">

<a href="/">cancel</a>

</span>

<br>

<input type="hidden" name="list_key"

value="{{ list_key }}">

</form>

{% else %}

<br>

<span style="padding-left:15px">

<a href="/item_form/{{ list.key }}/#form">

Create an item

</a>

</span>

<br><br>

{% endifequal %}

{% else %}

<br>

<span style="padding-left:15px">

<a href="/item_form/{{ list.key }}/#form">Create an item</a>

</span>

<br><br>

{% endif %}

>>>

--------------------------------------------------------------------------

.step 17 : 'Pathway posts'

.P : 'creating lists post handler'

.t : 'cl code'

<<<

def post(self):

list = List()

list.name = self.request.get('name')

list.put()

self.redirect("/")

>>>

.P : 'creating items post handler'

.t : 'ci code'

<<<

def post(self):

item = Item()

list_key = self.request.get('list_key')

item.list_key = List.get(list_key)

item.name = self.request.get('name')

item.put()

self.redirect("/")

>>>

--------------------------------------------------------------------------

.step 18 : 'Template Payload'

.P : 'template payload'

.t : 'code'

<<<

lists = db.GqlQuery("SELECT * FROM List")

new_lists = []

for list in lists:

items = db.GqlQuery("SELECT * FROM Item Where list_key = :1",

list.key())

new_list = {

'name': list.name,

'key': str(list.key()),

'items': items

}

new_lists.append(new_list)

template_values['lists'] = new_lists

>>>

--------------------------------------------------------------------------

.step 19 : 'Launch file'

.P : 'launch file'

.file : 'app.yaml'

.t : 'app.yaml'

<<<

application: listitem

version: 1

runtime: python

api_version: 1

handlers:

- url: /(.*).txt

static_files: \1.txt

upload: (.*).txt

- url: /.*

script: ListItem.py

>>>