Skip to content

Instantly share code, notes, and snippets.

@igauravsehrawat
Forked from siddhi/pyconindia_dsl.py
Last active August 29, 2015 14:22
Show Gist options
  • Save igauravsehrawat/3762d48b9c95e9810146 to your computer and use it in GitHub Desktop.
Save igauravsehrawat/3762d48b9c95e9810146 to your computer and use it in GitHub Desktop.
from pyparsing import *
# By default, PyParsing treats \n as whitespace and ignores it
# In our grammer, \n is significant, so tell PyParsing not to ignore it
ParserElement.setDefaultWhitespaceChars(" \t")
def parse(input_string):
def convert_prop_to_dict(tokens):
"""Convert a list of field property tokens to a dict"""
prop_dict = {}
for token in tokens:
prop_dict[token.property_key] = token.property_value
return prop_dict
# The form DSL grammar expressed in PyParsing notation
# PyParsing has a brilliant DSL syntax
word = Word(alphas)
newline = Suppress("\n")
colon = Suppress(":")
arrow = Suppress("->")
key = word.setResultsName("property_key")
value = Word(alphanums).setResultsName("property_value")
field_property = Group(key + colon + value).setResultsName("field_property")
field_type = oneOf("CharField EmailField PasswordField").setResultsName("field_type")
field_name = word.setResultsName("field_name")
field = Group(field_name + colon + field_type + Optional(arrow + OneOrMore(field_property)).setParseAction(convert_prop_to_dict) + newline).setResultsName("form_field")
form_name = word.setResultsName("form_name")
form = form_name + newline + OneOrMore(field).setResultsName("fields")
return form.parseString(input_string)
# Code to create an Internal DSL to make outputting HTML easy
class HtmlElement(object):
"""The base element used by all the other classes"""
default_attributes = {} # If the element has any default attributes
tag = "unknown_tag" # Name of the tag
def __init__(self, *args, **kwargs):
"""Children are passed in as args, attributes as kwargs"""
self.attributes = kwargs
self.attributes.update(self.default_attributes)
self.children = args
def __str__(self):
"""Render the tag contents as HTML"""
# Convert the attributes into HTML representation
attribute_html = " ".join(["{}='{}'".format(name, value) for name,value in self.attributes.items()])
if not self.children:
# If there are no children, render in empty tag format
return "<{} {}/>".format(self.tag, attribute_html)
else:
# Otherwise render an open tag, followed by the children, followed by close tag
children_html = "".join([str(child) for child in self.children])
return "<{} {}>{}</{}>".format(self.tag, attribute_html, children_html, self.tag)
class Form(HtmlElement):
"""Form tag"""
tag = "form"
class Input(HtmlElement):
"""Input tag"""
tag = "input"
def __init__(self, *args, **kwargs):
"""The name parameter is compulary for input tags"""
HtmlElement.__init__(self, *args, **kwargs)
# If there is a label attribute, then store that and delete it
# Otherwise use the name attribute for the label
self.label = self.attributes["label"] if "label" in self.attributes else self.attributes["name"]
if "label" in self.attributes:
del self.attributes["label"]
def __str__(self):
"""The string representation also renders the label tag"""
label_html = "<label>{}</label>".format(self.label)
return label_html + HtmlElement.__str__(self) + "<br/>"
class CharField(Input):
"""Char field"""
default_attributes = {"type":"text"}
class EmailField(CharField):
"""Email field (same as CharField for now)"""
pass
class PasswordField(Input):
"""Password field, set type='password' for this field"""
default_attributes = {"type":"password"}
def render(form):
"""Render the parsed form into HTML"""
# Map the field name string to a field class
field_dict = {"CharField": CharField, "EmailField": EmailField, "PasswordField": PasswordField}
# Create the field classes
fields = [field_dict[field.field_type](name=field.field_name, **field[2]) for field in form.fields]
# Create the form
return Form(*fields, id=form.form_name.lower())
input_form = """UserForm
name:CharField -> label:Username size:25
email:EmailField -> size:32
password:PasswordField
"""
print render(parse(input_form))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment