SHOTS A HIGH­PERFORMANCE WEB TEMPLATING LANGUAGE

A thesis submitted to the Kent State University Honors College in partial fulfillment of the requirements for University Honors

by

David Steinberg

December, 2013 Thesis written by

David Steinberg

Approved by

______, Advisor

______, Chair, Department of Computer Science

Accepted by

______, Dean, Honors College

ii TABLE OF CONTENTS

LIST OF FIGURES...... iv

LIST OF TABLES...... v

ACKNOWLEDGEMENTS...... vi

CHAPTER

I. ABSTRACT...... 1

II. NOMENCLATURE...... 2

III. INTRODUCTION...... 5

IV. CURRENT STATE OF WEB DEVELOPMENT...... 6

V. THE PROBLEM...... 8

VI. THE GENESIS OF SHOTS...... 10

VII. DEVELOPING SHOTS...... 12

VIII. HOW TO USE SHOTS...... 18

IX. ADVANTAGES OF USING SHOTS...... 51

X. FUTURE DIRECTIONS...... 53

XI. CONCLUSION...... 55

REFERENCES...... 56

APPENDIX A...... 57

APPENDIX B...... 59

iii LIST OF FIGURES

Figure 1. A Web Server and Clients...... 13

iv LIST OF TABLES

Table 1. Symbol Set...... 19

Table 2. Resource File Extensions...... 44

Table 3. HTML vs. Shots...... 51

v ACKNOWLEDGEMENTS

The author would like to thank Dr. Arden Ruttan for his support as an advisor and good nature throughout the general mayhem of this project over the last year. The author would also like to thank Dr. Mikhail Nesterenko, Dr. Jonathan Secaur and Dr. Elizabeth Howard for their support as members of the thesis defense committee.

vi 1

Abstract

Python­based web development lacks a templating language that offers the same sleek succinctness that Python does. Conventional methods of producing HTML web pages are cumbersome and error­prone. Shots is a web page templating language that aims for efficiency and effectiveness through a minimal syntax and automatic, assistive functionality for “boilerplate” programming.

This paper is broken into sections detailing the life­cycle of Shots. It begins with general details about web development and a discussion of various technologies and tools used.

Observations of what is currently missing in web development and an overview of the author’s previous attempts at languages for web development follow. An outline of the process of building the Shots language is presented, and then a guide for use is provided. The paper concludes with a look at the advantages of using Shots and future directions of the project. 2

Nomenclature

Within the context of this document, the following definitions of these domain­specific terms are assumed:

“client” refers to an individual (or usually his or her computer) who accesses a web page.

“client­side” refers to anything in a web browser on an individual’s computer that is displaying/interacting with a web page.

“CSS” refers to Cascading Style Sheets, a language to describe the way a page’s content should be styled.

“delimiter” refers to a special symbol or set of symbols used to separate sections of text, usually signifying a section of text with special meaning.

“Django” refers to a popular web framework, written in Python, that offers some different inherent functionality than Flask, such as connection to a database.

“favicon” refers to the (usually 16 x 16 pixel) image to the left of URLs in a web browser’s address bar. They are used to provide visual context to a web page visitor as well as 3

bookmarks for a web page.

“Flask” refers to a web server, written in Python, that can serve Shots (as well as raw HTML) to clients as web pages.

“HTML” refers to HyperText Markup Language, a language used to describe the content (and general layout) of a web page.

“JavaScript” refers to a client­side scripting language used to provide functionality (usually interactivity) to web pages.

“Jinja” refers to a web page templating language that allows the addition of logic and programming control structures such as conditional statements and loops.

“jQuery” refers to a JavaScript library that makes development in JavaScript much easier to write and read.

“nested” refers to a section of code that is “inside” another section of code.

“open­source” refers to the non­proprietary, shared programs and code bases. 4

“Python” refers to a server­side scripting language. Flask, Jinja and Shots are all written in

Python.

“preprocessor” refers to a program that manipulates input into a different output before its use by another program.

“regular expression” refers to a succinct representation of a pattern of text. In many programming languages, regular expressions can be used to efficiently search for and replace text.

“server” refers to a computer that serves web pages.

“server­side” refers to anything on a computer that is serving web pages.

“tag” refers to words that are used to specify certain types of elements in HTML.

“template engine” refers to a tool used to preprocess an HTML document, performing logical operations and adding in data from an external program.

“URL” refers to uniform resource locators, used by web browsers as “addresses” of web pages on the Internet. 5

Introduction

This paper presents a new web page templating language called Shots. It is written in

Python and utilizes the Jinja [2] template engine. Shots is proposed as a language that will offer web developers efficiency and effectiveness through a minimal syntax and automatic functionality in areas such as file searching. The driving principle behind Shots is that web development should be straightforward, clean, and easy.

Over the last decade, drastic increases in web development have inspired rapid evolution in the uses of certain existing technologies. Ruby and Python, interpreted languages mostly used to script operating systems, have become prominent server­side scripting languages used in web development. The growth in number and power of web servers written in both languages continue to spark the creation of various new tools.

Preprocessing techniques applied to web development have broken the construction of dynamic, data­driven web pages down to a significantly quicker, cleaner process. Many libraries and languages have been built on top of existing preprocessing tools, and the layer of abstraction through which one can tackle web development is constantly being distilled. Shots sits at the top of a technological stack including Python, Flask, and Jinja. It is a clear cut, high level approach to the problem space of web page production. 6

Current State of Web Development

At the core of web development is HTML, CSS and JavaScript. These client­side languages provide content layout, styling, and functionality, respectively. Web browsers render these languages as web pages to display to individuals. Every web page is “served” by a web server, and server­side technologies are at work before a page is served. Examples of server­side languages are Python, PHP, Node.js, and Ruby. These languages not are web servers by themselves, but they allow for logic and procedure within the computer serving the web pages.

There are web servers written in each of these languages made to render and deliver web pages. Flask [1] is an example of a web server written in Python. Web servers often allow some sort of logic or procedure to be used prior to a page’s being served. One can utilize tools written in the same language as the web server (and other languages, but their use is beyond the point of this document) before a web page is served.

A variety of available tools offer different services, some only accessible in certain languages. One popular type of tool is a preprocessor of CSS. Examples of CSS preprocessors are Sass [8], LESS [3] and Compass [5]. Each providing a somewhat different syntax, these tools allow an individual to compose his or her CSS in a more aesthetically pleasing, manageable way. The Sass, LESS or Compass will be translated to regular CSS before it is served to a client.

Another popular type of tool is a template engine for HTML. While these vary across 7

languages, they allow logical operations to be performed and external data to be inserted into an

HTML page before it is served. Programming constructs such as conditional statements and loops can be used in HTML development. If, for example, a web page had the word “hello” printed 100 times, and the web page developer wanted to change “hello” to “hi there,” he or she would have to replace “hello” 100 times. If, however, the HTML were written using a templating language, and the word “hello” were printed in a looping construct that iterated 100 times, the web page developer would only have to change the word once. 8

The Problem

Web development has come a long way from its inception. Tool after tool has been created that allows for faster, cleaner, more manageable code. Flask offers an extremely simple way to map sleek looking URLs to web pages, with the ability to use Python logic on data before rendering and serving a web page. Jinja offers a way for logical operations in Python to be performed and external data to be inserted into an HTML page before it is served.

In Jinja, one can execute Python statements within special delimiters, and render variables from the Python environment in different special delimiters. While Jinja adds a hugely powerful layer of ability to dynamic web pages, its syntax is not very clean or easy. The language’s additional symbols, used en masse, begin to add a significant amount of “weight” to a document and can cause the text to look quite cluttered and unreadable.

HTML itself has a relatively heavy syntax. Each element in a web page must be encased with angled brackets, and elements that may have other elements inside of them require a

“closing” tag (which is a “/” symbol and the opening tag name enclosed by angled brackets). In a large document, these excess symbols add massive amounts of code to produce and read through, and if, for example, closing tags are not placed properly, the entire web page may display incorrectly.

Python is a “whitespace­sensitive” language, which means that its programming structures are delimited by each line’s initial whitespace and do not require special symbols to signify when the structures end. Python inherently promotes good practices for programming, as 9

it requires sections of code that are nested under others to be clearly visually “inside.” The clean, easy­to­read code that is produced in Python is in Flask as well as it is written in Python.

Templates written in HTML and Jinja do not share this quality. They are laden with extraneous symbols and the potential for web developers to make mistakes by not properly including closing tags. 10

The Genesis of Shots

Shots is the author’s third attempt at a web development language and the second of those relating to HTML. The first attempt, called HooTML, was based on the idea that an element’s content, style, and functionality (HTML, CSS, and JavaScript) could be declared all together, and an element could inherit properties from other elements. These ideas were taken directly from the object­oriented (hence the “oo”). HooTML was implemented in PHP and was not built using any best practices for parsing a grammar, such as breaking text into identifiable “tokens” and recursively building elements based on the order of the tokens.

The second attempt at a web development language, called jQuby, related to

JavaScript. It was implemented in Ruby (hence the “uby”) and was modeled after characteristics of Ruby, such as an “end” block delimiter and the word “def” instead of

“function.” A series of regular expressions was applied to the source text and properly formatted jQuery (hence the “jQ”) was produced, which would run normally as client­side code. While the use of regular expressions was a great improvement over the not­generalized approach of HooTML, the inherent limitations of regular expressions forced certain restrictions on the language, such as limited nesting abilities.

The number of templating languages in existence is too great to try to make an exhaustive list, but there are key templating languages that inspired Shots, either directly or indirectly. Jade [10] is a templating language, written for use with Node.js, that has had the 11

greatest impact on the genesis of Shots. Jade’s minimal, clean syntax largely inspired most of the syntax of Shots. Some key differences will be explained in the Developing Shots section below.

Haml [7] is templating language for use with Ruby that came before Jade. It heavily influenced the development of Jade, and therefore, the development of Shots.

Jinja, as mentioned previously, hugely inspired Shots (and is the basis for some of the more complex functionality). Jinja derived many of its ideas from templates used by the popular

Python web framework Django [3]. These templates provide a somewhat different syntax than

Jade and Haml, in that Python variables are more pointedly delimited in the text. This clear delineation allows the data sections of the document to stand out more and was carried through to Shots. 12

Developing Shots

Environment

After researching language parsing and best practices for implementing a parser, the author was recommended an extraordinarily informative book [4] for building language parsers and compilers. While the book utilized the Java programming language in its parser implementations, Python, being quick to write and prototype in, was a natural choice for implementing a language parser and became the basis of Shots.

Flask offered an extremely fast and easy web server environment for Python. The name

Shots is a play on the idea of a flask serving shots. Flask is a minimal, powerful web server used by numerous high­profile tech companies, as well as countless individuals and small businesses.

Jinja is inherently included in Flask and may be used to render HTML templates for

Flask to serve. Linking Flask application to Jinja templates has advantages, such as offering easy ways to generate URLs for links to specific functionality within an application.

The following figure illustrates where the relevant languages and tools discussed above reside. Python, Flask, Jinja and Shots are server­side technologies. HTML, CSS and

JavaScript are client­side technologies. 13

Figure 1. A Web Server and Clients 14

Directory Structure Constraints

Flask imposes a certain structure on the content contained within an application’s directory. By default, folders labeled “templates” and “static” within the top­most directory of an application house HTML/Jinja templates and resource files (such as CSS and JavaScript) respectively. Shots was originally built to translate “.shot” files, generate “.” files in the

“templates” directory and render the generated HTML file as a template through Jinja. This thin veneer on the existing structure quickly evolved into a much more intricate process.

Jinja is able to render templates simply by referring to their file name (not explicit file path), because it is intrinsically tied to Flask and is “aware” of the templates directory. Shots was originally able to connect to a Flask application and then pass the information along to Jinja.

Not having to generate a file for every Shot became a goal of the project, and eventually

Shots became a translator purely of text, without producing new files. Shots would read the specified file’s text, parse and translate it to HTML with Jinja template placeholders and then would send the raw text to Jinja for rendering. This new production style was made possible because Jinja offers multiple ways for templates to be loaded. Without modification, Jinja will use a “file loader,” which searches in the default (or altered) templates directory. One can specify that Jinja should use a “source loader,” which allowed Shots to simply render text without creating new files.

An unfortunate side­effect of this switch to text­based templates is that templates can no longer be compartmentalized and pieced back together by Jinja (a key element of what makes it such a powerful template engine). Templates must be aware of their context (the templates 15

directory) to be able to “extend” or “include” other templates. To provide a context within the file system to Jinja, Shots currently uses a “file loader.” Instead of passing along information from a Flask application, Shots loads files through Jinja using absolute paths.

The benefit of using absolute paths is that Shots can now be used completely outside of a Flask application. As long as some web serving tool can include a Python library (which Shots is), it can use Shots, which would in turn use Jinja. The downside of using absolute paths and removing Flask from the file equation is that templates can no longer easily generate URLs for links to specific functionality within an application. Shots now allows URLs to be found and included through the use of the “@” symbol.

Language Design

The philosophy throughout the design of Shots was to keep things straightforward, clean, and easy. Wherever this principle did not stay true, there had to be very good reason.

HTML, while offering a wide range of element types for different purposes, still has a distinct taste of yesteryear. Many tag names are awkward shorthand for (not very well named) sections of web pages. Though HTML5, the newest standard of HTML, has added many more semantically sound tags, many remain that do not correlate with the mental model a web developer has when writing HTML.

An example of a difference between a Shots tag and an HTML tag is the “link” tag. In

Shots, a “link,” as one would expect, refers to a page element that, when clicked, will take the user to a web page that has its hyperlink specified in the element’s “to” attribute. In HTML, this 16

is accomplished with an “a” (anchor) tag and a hyperlink in the element’s “href” attribute. This difference makes Shots much more readable and understandable. Upon reading a Shot, one may recognize a line of code that contains a “link to” a different web page.

This semantic enhancement has side effects. The “link” tag already has a different meaning in HTML. Its use varies, but it is mainly used to include favicons or CSS files. Its existence is not a problem, because Shots already has replacements for those tags: “favicon” and “” respectively. Though redefining tags or introducing new tags moves the keywords in

Shots further from raw HTML, the benefits of more semantically­relevant tags far outweighed any negative ramifications.

Shots is built to allow a web developer to clearly and succinctly state what each piece of a web page does, instead of spending his or her time worrying about balancing opening and closing tags. For this reason, angled brackets are not used to delimit elements, as they are in

HTML. A side effect of allowing an identifier (defined below) to begin a line in Shots, instead of a “less than sign,” is that raw text cannot be freely mixed within elements. Currently, one may use a colon ( : ) or a “pipe” ( | ) to delimit text in Shots. The use of a “pipe” or “vertical bar” is adopted from Jade.

Whitespace sensitivity is inspired by Python. Jade and Haml are also whitespace sensitive, but the idea for a templating language that looks and feels like Python comes directly from the language itself. While requiring each line to be carefully initialized with specific amounts of spaces and tabs does not feel “easy,” as the guiding philosophy suggests, the clear, visual nature of nesting using whitespace is clean and modeled after Python. This style promotes clean 17

code structuring and offers relief from elements’ closing tags.

HTML Generation

The guiding principle of clean and straightforward code carried through to the HTML that Shots generates. The whitespace that each line begins with is mirrored in its HTML equivalent. Each closing tag for an element is placed exactly as far from the left edge of the document as the opening tag. Self­closing tags do not have a closing tag or a final “/” symbol, compliant with the HTML5 specification. Jinja template directives that do not have nested elements inside are printed on only one line. 18

How to Use Shots

Language Details

Whitespace

As in Python, whitespace is significant in Shots. Whitespace is defined as a space or a tab.

Single spaces count as one space, and tabs count as four spaces by default. One can change the default width of a tab through the “tab_width” setting. Each line’s initial whitespace is its

“depth.” Elements are nested (explained shortly) within elements above them if their “depth” is greater.

Identifier Definition

It is important to define what Shots considers an “identifier.” Anything of the form:

[A­Za­z_][A­Za­z0­9_­]* is a valid identifier and can be used as element tags, IDs and classes. In human terms, this means the following: identifiers may begin with a letter (either case) or an underscore or a dash, and they may contain any number of letters (either case) or numbers or underscores or dashes after that.

Special Characters

Shots has a very basic symbol set. 19

Table 1. Symbol Set

: or | Text follows (until the end of the line)

:: or > A nested element follows

+ Used to chain sibling elements together on one line.

# An ID follows

. A class follows

= Used to set a variable or an attribute of an element.

( ) Used when defining or invoking a function.

=> Used when listing the returned variable(s) from a macro call

[[ ]] or {{ }} A template variable is inside

! An HTML comment follows (until the end of the line)

!! A Shot­side comment follows (until the end of the line)

Elements

The simplest example of an element is equivalent to an HTML tag.

h1 will render as this HTML:

Of course, in real use, this element would be filled with content. Here is an example with some content to demonstrate usage. It will be explained later on.

h1: This is some content 20

will render as this HTML:

This is some content

Elements that are self­closing will not have a closing tag in the rendered HTML, nor will they be rendered with a final “/,” compliant with the newest HTML standard, HTML5. Such tags are

“area,” “base,” “br,” “col,” “command,” “doctype,” “embed,” “hr,” “img,” “input,”

“keygen,” “meta,” “param,” “source,” “track,” and “wbr.”

Note: As in HTML, self­closing elements may not have nested elements (explained shortly) within them.

img input will render as this HTML:

There are two special cases of element tags. One is the redefined “link” tag. It is used to represent hyperlinks in Shots. The “to” attribute (attributes will be discussed in detail shortly) indicates what to link to, as one would expect.

link to="http://shots­lang.com" will render as this HTML:

The other special case is the “favicon” tag, which is followed by the name of a resource 21

(following the guidelines of referencing resources).

favicon "/static/img/favicon.ico" will render as this HTML:

Breaks

Breaks can be written as single breaks or multiple breaks in one statement.

br br 3 will render as this HTML:





Nesting

Elements can be nested under each other by using more white space on child elements’ lines.

div span a span a img will render as this HTML:

22

Elements may also be nested with the special “::” or “>” symbol.

div :: span :: a li > link to="#" will render as this HTML:

  • Chaining

    Elements may be chained together on a single line with the “+” symbol.

    div + div span + span + a: hi will render as this HTML:

    hi

    Attributes

    Just like raw HTML, attributes are key=“value” pairs or standalone attributes.

    div name="personal" input autofocus type="text" will render as this HTML: 23

    There is a special case for attributes when defining “audio” and “video” elements. Sources for these elements may be specified as either a single resource or an array of resources. As usual with file referencing, file extensions are optional.

    Note: Unlike for image sources, Shots will not stop searching for files once a matching file has been found. Each possible type of audio or video source will be search for.

    If an application’s static directory looks like this:

    static/ audio song.mp3 song.ogg song.wav video movie.mp4 movie2.ogg movie2.webm and a Shot looks like this:

    audio src="song" video src=["movie", "movie2"] it will render as this HTML:

    24

    IDs

    Elements may be assigned an ID by placing the “#” symbol followed by an ID name anywhere on the line that an element is declared. As stated above, elements that begin with an ID will automatically be made into div elements. IDs may also be declared as an attribute like normal.

    div #header #content div id="footer" will render as this HTML:

    Classes

    Classes can be added to an element by placing a dot followed by the class name anywhere on the line that an element is declared. As stated above, elements that begin with a class will automatically be made into div elements. “class” can be declared as an attribute like normal.

    div.bigText.alert .medText a class="smallText" will render as this HTML: 25

    Text

    Text comes in two forms: 1) in a single line after a colon and one single space (either on the line of an element declaration or nested inside an element) and 2) in a block inside an element that ended its declaration with a colon.

    Note: Text that comes after a colon must have a space before it. If it does not, the first character of the text will not show up in the HTML.

    While colons may have any amount of whitespace before them on element declaration lines, it is recommended that colons come directly after a tag, ID, or class. If, however, an element has attributes, it is recommended that a single space be placed between the last attribute and the colon.

    : This is raw text a: This is other raw text div: Text blocks don't require colons in front. They will preserve their spacing. will render as this HTML:

    This is raw text This is other raw text

    Text blocks don't require colons in front. 26

    They will preserve their spacing.

    URLs

    When specifying a URL for an “href” attribute, the “@” symbol can be used to generate the

    URL for a route. Whitespace is allowed between the “@” symbol and the function name.

    If an application’s main file looks like this:

    from flask import Flask from shots import Shot

    app = Flask(__name__)

    @app.route("/") def index(): return Shot("index").render()

    @app.route("/login") def user_login(): ...

    if __name__ == "__main__": app.run() and “index.shot” looks like this:

    link to="@user_login" it will render as this HTML:

    CSS 27

    CSS can be declared either with an external source (following the guidelines of referencing resources), or as raw CSS in a single­line or multiline element. CSS blocks do not need a colon at their beginnings. Both “css” and “style” tags may be used.

    css "/static/css/style.css" css: body { font­family: sans­serif } css h1 { color: blue; } will render as this HTML:

    JavaScript

    JavaScript may be declared either with an external source (following the guidelines of referencing resources), or raw JavaScript as a single­line or multiline element. JavaScript blocks do not need a colon at their beginnings. “js,” “javascript” and “script” may all be used as tags.

    js "/static/js/script.js" js: alert("hi") js for (i = 0; i < 10; i++) { alert("hi"); } 28

    will render as this HTML:

    Commenting

    Commenting in Shots can be accomplished in a variety of ways. Comments can either be displayed as regular HTML comments or kept as secret “Shot­side” comments.

    The first style of commenting is a single­line comment meant to be displayed in the HTML. It begins with an exclamation point and goes until the end of the line. Those familiar with HTML programming will find the exclamation point a natural way to comment.

    ! This is a comment will render as this HTML:

    If a comment relates to templating directives, it might be best to have it only in the Shots code but not the HTML. Comments that being with two exclamation points will not be rendered in the HTML file that is created.

    !! This comment will not appear in the HTML will not render as anything. 29

    The third style of commenting is a multi­line comment meant to be displayed in the HTML. It begins with the word “comment” and goes until there are no nested lines. The text after the word “comment” on the first line will be included in the comment.

    comment This a block comment. It can span many lines. Larger remarks go in block comments. will render as this HTML:

    Block comments can be very useful for debugging. If, perhaps, a particular element is not rendering properly, to remove the element from the page, just place the word “comment” before the first word of the element.

    comment .row .col­lg­6 a: This is a delightful little Bootstrap example. .col­lg­6 a It is unfortunately broken. will render as this HTML:

    30

    Generally, it is nice to keep broken code from even showing up in the HTML. Secret block comments may be used to to hide broken code and larger block comments that relate to template directives. Secret block comments do not render in the HTML. They begin with the word “secret” and go until there are no nested lines. The text after the word “secret” on the first line will be included in the comment.

    secret .row .col­lg­6 a: This is a delightful little Bootstrap example. .col­lg­6 a It is unfortunately broken. will not render as anything.

    Escaping

    Sometimes it is necessary to write Shots code without rendering it. Writing the Shots code as raw text, either on one line or in a text block, will cause the text to remain unchanged. If, for example, the following is used to demonstrate how to write Shots:

    div: a: This is some text. img src="bicycle" it will render as this HTML:

    a: This is some text. img src="bicycle"

    Currently, raw text is the only way to include raw HTML in Shots code. 31

    body:

    This is a header

    This is some content
    will render as this HTML:

    This is a header

    This is some content

    Variables

    Jinja provides a way to pass variables to templates, so Shots does too! Any variables that have been passed to the template in the Shot’s “render” method are available throughout the template. To access variables in a template, either [[ and ]] or {{ and }} may be used. If, for example, the template had been rendered like this:

    return Shot("index").render(greeting="Hello, world") and the template contained the following code:

    div: [[ greeting ]] it will render as this HTML:

    Hello, world

    Variables can be set using a standard equals sign.

    name = "Ella" h3: [[ name ]] will render as this HTML: 32

    Ella

    Template variables can also be rendered based on conditional statements.

    id = "middle" div [[ "#" + id if id else "" ]] : This is content. will render as this HTML:

    This is content.

    As Jinja handles the code after it has been translated from Shots, dynamic file references cannot be searched for and must be absolute paths (with file extensions specified).

    page = "home" css "/static/css/[[ page ]].css" will render as this HTML:

    while this:

    page = "home" css "[[ page ]]" will not invoke a search for a file named “home.css” and will render as this HTML:

    Sometimes it is necessary to use the [[ and ]] symbols in raw text. To avoid causing Shots to think there is a variable to render, [[[ and ]]] may be used.

    a: In Shots, use [[[ and ]]] to render variables. will render as this HTML: 33

    In Shots, use [[ and ]] to render variables.

    Inheritance

    Because Shots is built on top of Jinja, templates may inherit from each other. Templates use

    “block” directives to indicate sections of templates that will be replaced by those that inherit from them. Templates use “extends” directives to indicate which templates they inherit from.

    Note: An important distinction between Shots templates and Jinja templates is that template names in the “extends” directive do not need to include the “.shot” extension, and the names may be either absolute paths or the template name without any preceding directory names.

    Shots will find these file paths as it does other files.

    This example is adapted from the Jinja documentation and translated to Shots.

    In “base.shot”:

    block head css "style" title block title : ­ My Webpage

    #content block content

    #footer : ©; Copyright 2013 by link to="http://domain.invalid/" : you : . 34

    In “child.shot”:

    extends "base"

    block title : Index

    block head [[ super() ]] css: .important { color: #336699; }

    block content h1: Index p.important: Welcome to my homepage.

    “child.shot” will render as this HTML:

    Index ­ My Webpage

    Index

    Welcome to my homepage.

    Control Structures 35

    Template control structures are ways to add logic to the HTML of a web page. They may freely use template variables without the [[ and ]] symbols. Those familiar will Jinja will notice the same control structures available in Jinja.

    Note: An important distinction between Shots templates and Jinja templates is that template control structures do not need to be closed with an “end___” in Shots. Shots will include these automatically.

    The “set” directive in Jinja has been reduced to only requiring an equals sign. Variables can be used anywhere below their initialization in the template.

    x = "Hello, world" div: [[ x ]] will render as this HTML:

    Hello, world

    The “for” directive is used to provide a looping mechanism to a template.

    for i in range(1,7) h[[ i ]]: This is a header. will render as this HTML:

    This is a header.

    This is a header.

    This is a header.

    This is a header.

    This is a header.
    36

    This is a header.

    A “for” directive may be followed by an “else” directive that will be executed if the “for” loop does not occur.

    my_list = [] for item in my_list li: item else li: There don't seem to be any items. will render as this HTML:

  • There don't seem to be any items>/li>

    The “if” directive provides a mechanism to present HTML based on certain conditions. It can be used in conjunction with “elif” directives and/or an “else” directive.

    name = "Chuck" if name : Hello, [[ name ]]. elif hour > 22 : Hello, moon. else : Hello, world. will render as this HTML:

    Hello, Chuck.

    Because Shots is built on top of Jinja, colons may optionally be used at the end of statements that start blocks (such as loops and conditionals).

    This code is completely valid (and looks almost exactly like Python!):

    for n in names: 37

    if n == "Mary": div: Hi, Mary else: div: Hi, not Mary

    Function­like, “macro” directives are pieces of code that can be used over and over. Either

    “def” or “macro” may be used to declare them. They are called either like normal function invocations, by rendering them in [[ and ]] symbols, or by “call” directives. The preferred approach is invoking a macro just like a normal function.

    The following examples are adapted from the Jinja documentation and have been translated to

    Shots.

    def input(id, type="text") input #[[ id ]] type="[[ type ]]" value=""

    .login input("username") input("password", type="password") will render as this HTML:

    This example, using a “call” directive:

    def render_dialog(title, class="dialog") .[[ class ]] h2: [[ title ]] .contents 38

    caller()

    call render_dialog("Hello, world") : This is a dialog rendered by a call directive. will render as this HTML:

    Hello, world

    This is a dialog rendered by a call directive

    Macros can return values, and those values can be used within the body of the macro call.

    def foo(): return "bar", 5

    foo() => str, num for i in range(num): div: [[ str ]] will render as this HTML:

    bar
    bar
    bar
    bar
    bar

    As in Jinja, “filter” directives can be used to affect sections of a template. Again, this example is adapted from the Jinja documentation and translated to Shots.

    filter upper : This text becomes uppercase 39

    will render as this HTML:

    THIS TEXT BECOMES UPPERCASE

    The “include” directive allows a template to include other templates’ contents. The inclusion of other pages is quite helpful for modularized pieces of content, especially when only a handful of a sites’ many pages reuse certain content.

    In “index.shot”:

    include "included" div: I hate being second.

    In “included.shot”:

    a: I love being first.

    “index.shot” will render as this HTML:

    I love being first

    I hate being second

    The “import” directive allows Shots with “macro” directives in them to be imported to the current template and then used. Importing Shots is very much like importing modules in Python.

    In “imported.shot”:

    def input(id, type="text") input #[[ id ]] type="[[ type ]]" value=""

    def render_dialog(title, class="dialog") .[[ class ]] h2: [[ title ]] .contents 40

    caller()

    In “index.shot”:

    import "imported" as imps

    .login imps.input("username") imps.input("password", type="password")

    call imps.render_dialog("Hello, world") : This is a dialog rendered by a call block. it will render as this HTML:

    Hello, world

    This is a dialog rendered by a call block.

    The “from” directive can be used, as in Python, to import certain “macro” directives from a

    Shot.

    With “imported.shot” as above, if “index.shot” looks like this:

    from "imported" import render_dialog

    call render_dialog("Hello, world") : This is a dialog rendered by a call block. 41

    it will render as this HTML:

    Hello, world

    This is a dialog rendered by a call block.

    Sometimes it is desirable to include symbols that have certain meaning to the underlying Jinja engine without template variables or template directives. The “raw” directive allows sections of templates to not be rendered.

    Note: the Shots code will still be rendered. The “raw’ directive only affects “{%” and “%}” and

    “{{“ and “}}.”

    raw : "{%," "%}," "{{" and "}}" all have meaning in Jinja. will render as this HTML:

    "{%," "%}," "{{" and "}}" all have meaning in Jinja.

    File Handling

    When static resources or templates are referenced, Shots will search for the file. Static resources are searched for anywhere inside the “static_dir” directory, and templates are searched for anywhere inside the “template_dir” directory. By default, these are the “static” and “templates” directories (respectively) within an application. 42

    Shots will stop looking after finding the first matching file (except for audio and video sources).

    First, the relevant top level directory is searched, and then each subdirectory is recursively searched in order of the current directory’s file listing.

    Note: File extensions are optional when referencing Shots or resources. The exception to this rule is when using “link” elements (when linking to a “.pdf”, for example).

    Shot Files

    If a Flask application’s main file looks like this:

    from flask import Flask from shots import Shot

    app = Flask(__name__)

    @app.route("/") def index(): return Shot("index").render()

    if __name__ == "__main__": app.run() and the application directory looks like this:

    app.py shots/ templates/ index.shot

    The “index” Shot will be found. If, however, the application’s directory looks like this: 43

    app.py shots/ pages/ index.shot the “index” Shot will NOT be found, unless it is referenced with an absolute (or relative) path. If the “index” function in the application’s main file looks like this:

    @app.route("/") def index(): return Shot("/pages/index.shot").render() it will be able to find the “index” Shot.

    The “static_dir” and “template_dir” properties can be set by accessing the global “settings”.

    The following application will also be able to find the “index” Shot.

    from flask import Flask from shots import Shot, settings

    settings.template_dir = "pages"

    app = Flask(__name__)

    @app.route("/") def index(): return Shot("index").render()

    if __name__ == "__main__": app.run()

    Shots can also be set to work with a specific Flask application. If an application’s main file looks like this:

    from flask import Flask from shots import Shot, settings 44

    app = Flask(__name__,template_folder="pages")

    settings.app = app

    @app.route("/") def index(): return Shot("index").render()

    if __name__ == "__main__": app.run() it will be able to find the “index” Shot.

    Resource Files

    When a tag is found in a Shot that matches one of those in column 1 of the following table and the element references a file, the list of types in column 2 is used to match file paths (in alphabetical order).

    Table 2. Resource File Extensions

    css, style .css

    js, javascript, script .js

    audio .mp3, .ogg, .wav

    video .mp4, .ogg, .webm

    img .apng, .bmp, .gif, .jpeg, .jpg, .png, .svg

    favicon .apng, .gif, .ico, .jpeg, .jpg, .png, .svg

    If an application’s directory looks like this:

    app.py 45

    shots/ static/ fun_times.gif templates/ index.shot and “index.shot” looks like this:

    img src="fun_times" it will render as this HTML:

    If the “static” folder looks like this:

    static/ fun_times.png img/ dolphins.png fun_times.gif and “index.shot” looks like this:

    img src="fun_times" img src="dolphins" it will render as this HTML:

    If “index.shot” looks like this:

    img src="fun_times.gif" it will render as this HTML: 46

    Files must be specified throughout Shots either as unique resource names (or different file types) or as absolute (or relative) paths.

    If “index.shot” looks like this:

    img src="/static/img/fun_times.gif" it will render as this HTML:

    Generated HTML

    When a Shot is rendered, the file is parsed, and an HTML file is generated. The generated file gets placed inside the “html_dir” directory (which is inside the “template_dir” directory) with a matching relative path.

    If an application’s main file looks like this:

    from flask import Flask from shots import Shot

    app = Flask(__name__)

    @app.route("/") def index(): return Shot("index").render()

    if __name__ == "__main__": app.run() 47

    and the “templates” directory looks like this:

    templates/ index.shot after rendering “index.shot,” the templates directory will look like this:

    templates/ html/ index.html index.shot

    The default value for “html_dir” is “html.” The value can be altered through the global settings.

    If an application’s main file looks like this:

    from flask import Flask from shots import Shot, settings

    settings.hmtl_dir = "translated"

    app = Flask(__name__)

    @app.route("/") def index(): return Shot("index").render()

    if __name__ == "__main__": app.run() and the “templates” directory looks like this:

    templates/ index.shot after rendering “index.shot,” the templates directory will look like this:

    templates/ translated/ 48

    index.html index.shot

    Error Detection and Reporting

    Shots will report errors encountered during the parsing of a file, and the program execution may be stopped depending on the severity of the error.

    If, for example, a Shot contains the following code:

    img src="picture" and no file exists that 1) is named “picture” and 2) has an “image­relevant” file ending (e.g.

    “png” or “gif”), Shots will report that no file could be found, but it will continue to translate the file and will generate HTML.

    If Shots expects a specific piece of syntax and does not find it, it will report the error and halt execution. For example, if the following code is in a Shot:

    css dolphin the following error will be logged (with the corresponding line number):

    expected "media" or "scoped" as css attribute and the program will stop running. Similarly, a “js” element with an attribute that is not “async” or “defer” will stop the program and produce an error. Attributes followed by equals signs but no string or number and “use” statements that do not include a string for a library’s name will 49

    also cause errors and halted execution.

    Errors relating to Jinja’s functionality are reported directly by Jinja. If, for example, a variable is referenced but is neither set previously in the template nor passed in as a parameter when the template is rendered, Jinja will stop executing and will report the error.

    Development vs. Production

    Throughout development, pages and resources must sometimes be moved around between directories (sometimes very deep within directories). Any file movement inside the “static_dir” or “template_dir” will be dynamically reflected in Shots.

    Once an application is ready for deployment, make sure to set the “developing” flag in the global settings to False.

    If an application’s main file looks like this:

    from flask import Flask from shots import Shot

    app = Flask(__name__)

    @app.route("/") def index(): return Shot("index").render()

    if __name__ == "__main__": app.run() 50

    “index.html” will be generated (and replace any existing file) at its dynamic “html_dir” path. If an application’s main file looks like this:

    from flask import Flask from shots import Shot, settings

    settings.developing = False

    app = Flask(__name__)

    @app.route("/") def index(): return Shot("index").render()

    if __name__ == "__main__": app.run()

    “index.html” will NOT be generated (unless it doesn’t exist). Only producing a new HTML file when it is needed means that there is almost no overhead to including Shots in the application at this point. No new parsers will be created or used, and the existing HTML files will be passed to Jinja without intervention. 51

    Advantages of Shots

    There are many advantages to using Shots in web development. Because Shots has its root in standard HTML and is translated to HTML, it is compatible with contemporary technologies such as CSS and JavaScript. Shots is inherently “pluggable” into a Flask application, and may be utilized in other Python­based web server environments.

    Shots can also be used as a standalone tool to process “.shot” files into “.html” files that can then be used by any web server (not just Python­based ones). While a Shot can not be automatically rendered by Ruby, for example, an application built in Ruby can utilize the generated HTML as a standard web page it is serving.

    Shots is a much terser language than HTML. An example of its brevity is found in a translation of an example on Twitter Bootstrap’s site [9]. As Bootstrap is a hugely popular tool used throughout web development, a web page based in it is a reasonable measure of a standard web page. The following table demonstrates the differences.

    Table 3. HTML vs. Shots

    Language Lines of Code Character Count

    HTML 205 9,941

    Shots 186 6,700

    While Shots is generally geared toward those who are familiar with HTML, it offers a far smaller learning curve for those who know neither Shots nor HTML. The shifting of tag 52

    names to more semantically­relevant alternatives provides less of an abstraction to conquer for an individual new to web development.

    Those familiar with Python but not HTML will find Shots quite natural to begin using.

    With the use of logic and procedure (available due to Jinja) and no need to use Jinja’s special delimiter symbols, Shots can look almost exactly like Python code. The main difference is that

    “print” statements in Python are replaced by some Shots code to contain or display output.

    Here is an example program in Python:

    def print_one_per_line(items): for item in items: print item

    words = ["horse", "honey", "halibut"] print_one_per_line(words)

    The following is an almost identical program in Shots:

    def print_one_per_line(items): for item in items: div: [[ item ]]

    words = ["horse", "honey", "halibut"] print_one_per_line(words)

    The only difference between these examples is the third line, in which printing (or displaying) occurs. Executing the Python program from the command line and rendering the Shot in a web browser will result in the same visual output. 53

    Future Directions

    Shots has many exciting directions in which it can be taken. The first future goal of this project is to allow raw HTML to be written throughout a Shot. The ability to freely mix HTML and Shots will offer an easier entrance to Shots programming as well as safety for those interested in incorporating pieces of Shots and HTML to complete a web page. Being able to distinguish HTML content from other content is trivial and will simply require the use of one of the countless Python libraries for parsing HTML. The challenge will be in intelligently reading nested Shots code inside HTML and nested HTML inside Shots code.

    Another future goal of Shots is to generalize Shots in such a way that it could be seamlessly integrated with other Python­based web servers, such as Django. Currently, a

    Django web server could use Shots, but the file structure that a Django application requires does not match that of Flask (or Shots for the time being), and significant effort would be required to alter the logic in file detection and creation.

    A “reverse compiler” that accepts HTML as input and produces Shots has been a goal of the project since its beginning. Various tools exist in Python to traverse an HTML file and return a tree structure representing its elements’ hierarchical relationships. It would not be difficult to apply the reverse of the existing translation logic and print the corresponding Shots code.

    Shots has not been measured for performance, as it has largely been an academic exploration of constructing and parsing a language. Shots can be set to not generate new 54

    HTML files (when, for example, a web page has been finalized and does not need to be generated again), offering minimal­to­no overhead in a production environment. The amount of time needed to generate a file if it does not yet exist, however, has not been tested. Testing speed and efficiency is important because it offers metrics for potential future adopters to decide whether Shots would be desirable for them. It could also be the impetus for more efficient algorithms behind the logic of the translator.

    A final goal of the project is to more heavily modularize the components of the parser.

    While the parser is successfully built as a “recursive descent” parser, there are numerous subroutines that should become standalone methods instead of part of a method for conceptually unrelated functionality. A key example of heavily coupled functionality is the process through which attributes are parsed. The entire process is contained within a method responsible for parsing an whole element. Shifting attribute consumption to a new method (and other similar changes) will more closely align the parser’s infrastructure with the corresponding grammar (found in Appendix A). 55

    Conclusion

    Shots is web page templating language that aims for efficiency and effectiveness by providing web developers a straightforward, clean, and easy language with which to express their ideas and content. With a minimal syntax, Shots is easy to learn, and its automatic

    “assistance” functionality relieves developers from the “boilerplate” aspects of web development.

    This paper details Shots, from its inception to implementation and beyond. Web development, while progressing quite fully and regularly, is missing key elements. Shots fills those voids and more. The author’s third attempt at a language for web development, Shots, has various advantages over conventional HTML and a number of exciting, challenging directions it can go. 56

    References

    [1] A. Ronacher. “Welcome | Flask (A Python Microframework).” Flask. 2010. http://flask.pocoo.org

    [2] A. Ronacher. “Welcome | Jinja2 (The Python Template Engine).” Jinja. 2011. http://jinja.pocoo.org

    [3] A. Sellier. “LESS « The Dynamic Stylesheet language.” LESS. 2010. http://lesscss.org

    [4] A. V. Abo, M. S. Lam, R. Sethi and J. D. Ullman. Compilers Principles, Techniques, and Tools, 2nd ed. Boston: Pearson Education, 2007.

    [5] C. M. Eppstein. “Compass Home | Compass Documentation.” Compass. 2011. http://compass­style.org

    [6] Django Software Foundation. “The Web framework for perfectionists with deadlines | Django.” Django Project. 2005. https://docs.djangoproject.com/

    [7] H. Catlin. “Haml.” Haml. 2006. http://haml.info

    [8] H. Catlin. “Sass: Syntactically Awesome Style Sheets.” Sass. 2006. http://sass­lang.com

    [9] M. Otto and J. Thornton. “Bootstrap.” Bootstrap. 2010. http://getbootstrap.com/

    [10] TJ. Holowaychuk. “Jade ­ Template Engine.” Jade. 2010. http://jade­lang.com 57

    Appendix A

    This is a basic description of Shot’s syntax. Due to the scope of this document, the author asks readers to see Jinja's documentation for questions relating to its exact syntax.

    = | | | | |

    = ": " ? ?

    = *

    = (* )*

    = ( )*

    = ( | ? )

    = ( ? * * | * * | * ? * )

    =

    = "#" *

    = "." *

    = ( * "=" * ( | ) )?

    = [A­Za­z_][A­Za­z0­9_­]*

    = '"' '"' | "'" "'"

    = [0­9]* 58

    = ( )*

    = ">" (": " ? | )

    = ("+" (": " ? | ))+

    = ... (see Jinja docs) | "="

    = "block" | "break" | "call" | "continue" | "def" | "do" | "elif" | "else" | "extends" | "filter" | "for" | "from" | "if" | "import" | "include" | "macro" | "raw" | "return"

    = | | |

    = "! " ?

    = "comment " ? ?

    = "!! " ?

    = "secret " ? ?

    = "[[" * * "]]" | "{{" * * "}}"

    = | | | |

    = if else

    = 59

    Appendix B

    The following examples can be served by a Flask app using the Shots library.

    Example 1. Tech Company Links sites = ["amazon", "apple", "google", "yahoo"] for s in sites: link to="http://[[ s ]].com" | [[ s | upper ]]

    Example 2. Numbers and Logging css: h1, h2, h3, h4, h5, h6 { float: left } for i in range(48): x = (i % 6) + 1 h[[ x ]]: [[ i ]]

    js: console.log([[ x ]])

    Example 3. Math def add(x=0, y=0): return x + y add(3, 4) => z div: [[ z ]]

    Example 4. Programming Language Table comment This Shot requires the "bootstrap.lib" file that can be found in the templates/libs/ directory of the source on GitHub. use "bootstrap" css body { padding: 20px } #submit { margin­left: 20px } 60

    td { padding: 30px } def rating_buttons(language): tr td: [[ language ]] for i in range(1,6): td input type="radio" name="[[language]]" value="[[i]]" : [[ i ]] languages = ["Python", "Node.js", "Ruby", "PHP", "Perl", "ASP"] form table for language in languages: rating_buttons(language)

    br

    input #submit type="submit"

    Example 5. Color Boxes comment Mouse over a box to change its color. Mouse out to make it white. Click to keep a color until you mouse over again! css .box { height: 20px; width: 20px; float: left; border: 1px solid black; } for i in range(500): if i % 17 == 0: x = i % 256 .box css="background­color: rgb([[x]], [[x]], [[x]])" else: .box js "http://code.jquery.com/jquery" 61

    js $(document).ready(function(){ function randomNumber(){ return Math.floor(Math.random()*256); } function randomColor(){ result = "rgb(" + randomNumber() + "," + randomNumber() + "," + randomNumber() + ")"; return result; } $(".box").mouseenter(function(){ $(this).css("background­color", randomColor()); }).mouseleave(function(){ if ($(this).attr("clicked") == "yes") { $(this).attr("clicked","no"); } else { $(this).css("background­color", "white"); } }).click(function(){ $(this).attr("clicked","yes"); }); });