Functions are used to create customized pages. That is, pages generated only for that client at only at that point in time.

Functions are therefore part of the HTML page (or CSS).

Example:

<!DOCTYPE html>
<html>
   <head>
      ...
   </head>
   <body>
      ...
      <p>This page has been accessed .nofPageHits() times</p>
   </body>
</html>

The character sequence “.nofPageHits()” is a function. And will be replaced when sent to a client by a number indicating the number of page hits.

<!DOCTYPE html>
<html>
   <head>
      ...
   </head>
   <body>
      ...
      <p>This page has been accessed 36649 times</p>
   </body>
</html>

A page with functions in it must contain the four magic characters: “.sf.” in its filename. The presence of these characters cause the file to be parsed by Swiftfire and the functions in it to be replaced by the values they return.

Functions have a fixed syntax and will only be recognized as a function if the function name has been registered during startup.

Any “function” that fails the syntax check or when the name is not registered will not be replaced.

Function syntax

A function starts with a leading character which is followed by a name, an optional priority and some optional arguments.

The leading character is a dot: “.”

The name consists of letters, numbers, underscores or hyphens.

Each function has a priority, default is zero, but the function can specify a priority by following the function name with a colon followed by the priority.

Each function has either an array type argument (array of strings) or a JSON type argument. While the number of arguments may be zero, the argument specification must be present.

The syntax in BNF:

(In Extended-BNF notation: {} = sequence, [] = optional, | = or, .. = any in range)

<function> ::= <leading-sign><name>[<priority-seperator>[<priority>]]<arguments>

<leading-sign>          ::= "."
<name>                  ::= <letter>|<digit>|<allowed-signs-in-name>{[<letter>|<digit>|<allowed-signs-in-name>]}
<letter>                ::= "A" .. "Z" | "a" .. "z"
<digit>                 ::= "0" .. "9"
<allowed-signs-in-name> ::= "-"|"_"
<priority-seperator>    ::= ":"
<priority>              ::= <digit>{[<digit>]}

<arguments>             ::= "("[<argument>[{<argument-separator><argument>}]]")"|<json-object>
<json-object>           ::= "{"<json-code>"}"
<argument>              ::= <string>|<quoted-string>
<arguments-separator>   ::= ","
<string>                ::= {[" "]}<name>{[" "]}
<quoted-string>         ::= {[" "]}"""{<printable>}"""{[" "]}

Examples

It is perhaps best explained by giving some examples:

.name()

A simple function, leading sign, followed by name and having no arguments and priority 0.

._name()

The function name contains a leading underscore with priority 0.

.function-name()

A function name containing a hyphen with priority 0.

.name:()

A function with a default priority of 0

.name:18()

A function with priority 18

.name(one, two, true, 12)

A function with 4 arguments (all arguments are passed as strings) and priority 0

.name("Just one line")

A function with 1 argument and priority 0

.name:2("\"contains one double quote", 133)

A function with 2 argument, the first argument starts with a double quote, and the priority is 2.

.name{}

A function with an empty JSON argument and priority 0

.name_:55{"title": "A Book Title", "Array": [12, 3, 4, true]}

A function with a more complex JSON argument and priority 55

Arguments

Function arguments are one of two: Either an array of strings (Unix-like) or A JSON object.

Array like arguments are always passed as strings. But cannot contain blanks or double quotes unless the string is enclosed in double-quotes and inner double quotes are escaped by a backslash: ". If a string is enclosed in double quotes, a backslash must also be escaped: \.

A JSON argument is passed to the function as a VJson object. See SwifterJSON on how to access the items in the JSON object.

Priorities

Each function callout can have its own priority. Swiftfire parses the entire file and finds all function invokations before activating any. Next Swiftfire sorts the function calls according to their priority. Higher numbers indicate a higher priority. Then Swiftfire starts executing the functions starting from the highest priority and progressing to the lowest. After each function has been executed, the resulting page is merged from the original page and the results of the functions.

The reason for the priority is that there is a one way communication possible from higher prioritized functions to lower prioritized functions. This is done by way of a dictionary that each function has access to.

Adding Functions

Functions are added to Swiftfire in two steps:

  1. Create a functions
  2. Register the functions

Creating a function

The signature for the functions is as follows:

public typealias Signature = (
	_ args: Function.Arguments,
	_ info: inout Function.Info,
	_ environment: Function.Environment
) -> Data?

The parameters are:

args: The arguments as specified in the HTML (or CSS) file.

info: The dictionary that is available to communicate from higher prioritized functions to lower prioritized functions.

environment: A class that holds references to the environment of the function. This is best explained by showing the class itself:

public final class Environment {
    public var header: HttpHeader
    public var body: Data?
    public var connection: Connection
    public var domain: Domain
    public var response: Service.Response
    public var chainInfo: Service.ChainInfo
}

Return value: The function should return a Data object containing UTF-8 formatted text.

Registering a function

The functions are registered in “Function.Registration.swift” by the following code:

// =================================
// Names for the available functions
// =================================


/// Returns the number of hits for the requested resource (path).
///
/// Necessary arguments: None.
///
/// Optional argument: A string with the path of a resource for which the hitcount must be returned.
///
/// Example: .nofPageHits()

let NOF_PAGE_HITS = "nofPageHits"


// ==================================================
// Add to the next function to register new functions
// ==================================================
// Notice that the sequence itself is not important

/// Register the functions

func registerFunctions() {
    functions.register(name: NOF_PAGE_HITS, function: sf_nofPageHits)
}

To add a function, create a new name for it, and add the function to the list.