HFL BASIC CONCEPTS AND BEHAVIOR
HFL Delimiters:
HFL can be embedded within HTML files because it has its own delimiters that can be distinguished from HTML tags.
-
<\ <instr>* \>Open and close delimiters for HFL script// <comment>Rest-of-line comment inside of HFL script
- Cells are the structural framework to which all actions of HFL are applied. They are the basic internal unit of content that HFL uses for layout, and have a hierarchical organization akin to HTML nodes. The document is stored as a tree structure of cells, each of which can store text, properties, HFL instructions, and sub-cells. (The name 'cell' was chosen over 'node' in order to make it possible in the future to add HTML node operations without name conflicts.)
- HFL parses a document into one or more cells to create the cell tree in two stages.
- The first stage parses the document into sets of HFL scripts and HTML tags. Each enclosed HTML tag is at least one cell, and if it contains inner HTML tags it will be a sub-tree of cells. HFL scripts are parsed into HFL binary instructions, and stored in separate cells called 'format cells', which are marked as executable.
- The second stage executes the instructions of the HFL scripts in the format cells. All other changes to the cell hierarchy are under the control of the format instructions.
- These instructions are executed by the scope engine, which is the core of the HFL compiler. It is called the scope engine because it processes scope specifications, which are specifications of ranges of effect within the cell tree (see 'Scopes' below).
- Scopes are the syntactic framework within which all actions of HFL take place.
- Almost every construct in HFL that uses braces is in fact a scope, with the specification outside the braces constraining the code block inside the braces. All scopes identify a range of the content of a document, and all context-sensitive actions within the code block have their range of effect narrowed by that range.
- The term 'scope' means 'range of effect' and the concept is not new. Many programming languages such as C and javascript support a simple type of scoping known as lexical scoping, where variables declared inside of a code block are not visible outside of the block.
- HFL supports hierarchical context scoping, where the scopes specifications constrain the range of effect of inner scopes and inner context-sensitive program statements, as well as the visibility of variables.
-
For example, in:
-
// If the first word of the first paragraph is 'The', make it blue
p:1 {
word: 1 {
if ($text == 'The') {
color: blue;
}
}
}
-
- it will only search for the first word within the bounds of the cells of the first paragraph's scope. If it finds the first word, it will only check the text within the bounds of the cells of that word's scope. And it it matches the text, it will only assign the color blue to the cells of that word's scope.
- The syntax of simple 'tag' scope specifications resembles CSS syntax, having an optional scope tag (often an HTML tag), followed by an optional scope-range qualifier, and then a code block enclosed in braces. In fact almost all CSS statements can be inserted into HFL code as-is.
- HFL scope syntax has capabilities far beyond those of CSS however, including the ability to nest scopes within scopes, and execute program instructions within their blocks, as well as set properties. In effect, HFL scope syntax can be thought of as a greatly enhanced superset of CSS.
-
SIMPLIFIED SYNTAX:
-
<scoped-block>scope ( <scope-refs> ) [{ <code-block> }]where:<scoped-block>:=<scope-selectors> { [<code-block>] }<scope-selectors>:=<scope-selector>+ [, <scope-selector>]*<scope-refs>:=<scope-ref> [, <scope-ref>]*<scope-ref>:=<scope-selectors> | <cell-num> | <cell-array><scope-selector>:=[<tag>] [<scope-qualifier>][?]<scope-qualifier>:=[<class-sel>][<id-sel>][<attr-sels>][: <scope-range>]<class-sel>:=.<class-name><id-sel>:=#<id-name><attr-sels>:=<attr-sel> [ <attr-sel> ]*<attr-sel>:=[ <attr> <attr-comp> <value> ]<scope-range>:=<scope-range-spec> | all<scope-range-spec>:=<start> [- {<end> | last} [by <incr>]]]<code-block>:=<hfl-statement>*<start> is value or variable that evaluates to a signed integer<end> is value or variable that evaluates to a signed integer<incr> is value or variable that evaluates to a signed integerThe <class-sel>, <id-sel> and <attr-sels> selectors follow the same syntaxas CSS.
-
- Scope specifications are actually a type of function call, that searches the cell hierarchy to match the criteria of the specific scope tag. If a match is found, the instructions in the scope's code block are executed. Most scope tags are HTML tags, but there are other types as well, including pattern-based scope tags (such as the 'word' scope in the example above) that scan the text content to match against a regex pattern.
- Most scope qualifiers are a combination of one or more of a class, an id, or a tag with a numeric range (e.g. 'p:1-3'), with the range being the most common. A scope specified with a range is in effect a 'for' loop. The scope's code block is executed once for each match of the scope range. (Actually in HFL a 'for' loop is just one type of scope.)
-
For example:
-
p:1-3 {color:red;}
-
-
would set the color of paragraphs 1 through 3 to red, and is essentially
the same as:
-
var $i; // $i is a variable
for ($i = 1; $i < 3; $i++) {
p:$i {color:red;}
}
-
- but far more readable, easier to type, and much clearer in its intent.
- Scopes can be nested, so an outer scope can locate a outer cell, and an inner scope can refine the focus to a particular range within the outer cell. Any scope references inside a scope's code block only search within the bounds of that scope. So the inner scope only searches within the range of the outer scope. This results in a hierarchical searching behavior.
- There are many actions that are affected by a scope's range-of-effect constraint within the scope's code block. Any HTML attributes or CSS properties are only applied to the range. Any references to text content only refer to the text that lies within that scope. And in particular, content-modification functions are constrained to the content restriction. So for example, actions such as 'cut' and 'paste' can only affect the content of the enclosing scope.
- For a more detailed description of HFL scopes, see the section on "Scope Specifications" .
-
EXAMPLES:
-
// Make the second and third paragraphs blue
p:2-3 {blue}
// Make the first five words of the second ine of paragraph 5 bold.
p: 5 { line:2 { word:1-5 {bold} }}
// Assign the class "MenuBarItemSubmenu" to the first <a> tag
a:1{class="MenuBarItemSubmenu"}
// Make every odd word red and every even word green
word:1-last by 2{red} word:2-last by 2{green}
// Call function MakeSubMenu() on any <p> tags that have prop __menu-level="parent"
p[__menu-level="parent"]:all { MakeSubMenu(); }
-
-
The basic operations of the scope engine involve editing operations, such as
create, insert, cut,
copy and paste. Many of these
operations such as cut, copy and
paste inherently are from one scope to another.
In each, one half of the pair of scopes is a clip pool.
For example:
-
cut { p:2 {id="para2"} } // Cut p:2 into main clip pool
body {
paste { #para2 } // Paste it from pool into body
}
-
- Clip pools are simply cells that can be used for temporary storage of other cells. Since the core of the scope engine does text editing, it is necessary to have a place to edit to and from. Clip pools serve a purpose similar to the Windows clipboard, in that they are a place to keep copies of content for later use. But there can be any number of clip pools.
-
The system provides one standard clip pool per page, named '#clip',
which can be accessed as as scope 'clip', e.g.
-
cut { p:3 {id="para3"} } // Cut p:3 into main clip pool
clip { #para3 { print("IN #para3");} } // Access p:3 in default clip
// pool of current page
-
-
Applications can create their own custom clip pools as well, by specifying an
id or class on the editing scopes. Custom clip pools do not need to be
declared, they are created when first referenced. For example:
-
cut#myclip{ p:4 {id="para4"} } // Cut p:4 into custom page pool #myclip
-
- If the clip qualifier is not specified or is a #<id>, the clip pool is local to the current page. That means that its contents are only accessible to scopes within the same page. This is the default.
- If the qualifier is a .<class>, the clip pool is global to the whole document, and its contents can be accessed from any page.
-
EXAMPLES:
clip#myclip { #para4{ ... } } // Access #para4 in custom clip
// pool '#myclip' in current page
cut#myclip { p:5-last } // Cut p:5-last, adding to custom clip
// pool '#myclip' in current page
paste#myclip { p:1 } // Paste p:1 in custom clip pool
// '#myclip' into current scope
copy.myglobalclip { title:1 } // Copy title:1 to custom global clip
// pool '.myglobalclip'
paste_contents { clip#body_content } // Paste contents of custom clip pool
// '#body_content' into current scope
cut { this_scope } // Cut the contents of current scope
// to page's default clip pool
- NOTE: Scope references are relative to the location of the scope. When scopes are copied or moved into clip pools, their scope indices might change. For example, when cutting p:5 from the page into clip pool #myclip, it might now only be the third <p> cell within the clip pool. So within the pool it would now be 'p:3', not 'p:5'.
-
As an added convenience, the clip pool qualifiers can also be variable references,
if the variable names begin with a '$'.
- For example:
-
$my_clip = "myclip";
clip#$my_clip { ... }
- would be the same as:
-
clip#myclip { ... }
- and:
-
$my_global = "myglobalclip";
clip.$my_global { ... }
-
would be the same as:
clip.myglobalclip { ... }
-
As a special case, if the variable's value contains a leading
'.' or '#', it can change the type of
the clip pool.
For example:
-
$my_clip2 = ".myglobalclip";
clip#$my_clip2 { ... }
- would be the same as:
-
clip.myglobalclip { ... }
-
- Modal scopes are the main mechanism that allows statements that edit the cell structures to use the same syntax as statements that reference them. Examples of modal scopes are the create, cut, copy and paste scopes.
- These scopes are called 'modal' because they set a mode that changes the behavior of scope specifications inside them. Within a mode, inner scopes are interpreted differently.
-
For example, normally:
-
p:1{red}
-
- would find the first scope of type <p> and set its color to red.
-
But for example, when inside modal scope 'create':
-
create { p:1{red} }
-
- it creates a cell of type <p> and makes it red.
- Many of the modal scopes involve operations with a clip pool as one side of the operation. In these scopes, if the clip pool is not named, they will use the default clip pool for the current page.
- Below are some of the HFL modal scopes, shown with simplified syntax. For a complete description of modal scopes see the section on "Modal Scopes" in the main documentation.
-
create { <scoped-block>+ ... }
- Creates and inserts new cells of the type of any scopes mentioned into the current cells of the enclosing parent scope.
-
insert { <scoped-block>+ ... }
- Creates new cells of the type of any scopes mentioned, and inserts them in front on any other child scopes of the current cells of the enclosing parent scope.
-
append { <scoped-block>+ ... }
- Creates new cells of the type of any scopes mentioned, and inserts them after any other child scopes of the current cells of the enclosing parent scope.
-
cut { <scoped-block>+ ... }
- Extracts cells of the type of any scopes mentioned, etc. and places them in a clip pool.
-
copy { <scoped-block>+ ... }
- Copies cells of of the type of any scopes mentioned, and places them in a clip pool.
-
paste { <scoped-block>+ ... }
- Removes cells of the type of any scopes mentioned from a clip pool, and pastes them into the current scope in the same manner as 'append'.
- HFL's programming syntax is very similar to javascript, with some minor exceptions that are necessary to support embedded CSS and HTML attribute syntaxes.
- Perhaps the most noticeable difference is that semi-colons are required at the end of all HFL expressions and CSS property assignments, to distinguish them from HTML attribute assignments. In javascript semi-colons are separators, but in HFL they are statement terminators, and are required unless the statement ends with braces.
-
Another noticeable difference is that in HFL, variables must be declared
unless their names begin with a '$'.
There are two reasons for this.
- First, when identifiers are used in the left-hand side of an assignment statement, there is an ambiguity as to whether the identifier is a cell property or a variable as they appear the same. Without pre-declaring variables it is not possible to tell which type was intended.
- Second, to be compatible with HTML, in attribute-assignment statements unquoted identifier names without leading '$'s are treated as strings (also known as 'barewords').
- HFL supports the overloading of the '+' operator to do string concatenation, but only if one of the two sub-expressions is a quoted string. It does not support the overloading of the '+=' operator to do string concatenation.
- HFL supports function definitions, flow-control features such as 'if' and 'while', variable declarations, and arithmetic expressions, among others. One minor difference is that HFL only supports constant values in 'switch' statements.
- HFL also supports many of the more complex javascript language features, including function closures, objects, the 'dot' string-concatenation and object-property notation, and many of the DOM-access functions.
-
EXAMPLE:
-
// Variables and strings
var str = "The quick"; // Variable declaration of a string
var str2 = str+" brown fox"; // String concatenation
// Modifying cells marked with tag 'bottom_links'
bottom_links:all? {
$ilink = 0; // Expression assign
tag_final="" // HTML attribute assign
width: 100px; height: 20px; // CSS property assign
item:all { // Scope specification
if ($href) {tag="a"} // 'if' stmt with HTML attribute assign
if ($ilink > 0) {
create:before { txt{" | "} } // Constructor scope with text assign
}
$ilink++; // Expression assign
}
}
-
- HFL supports both the javascript 'var' and 'let' declarations of variables, including their lexical scoping behavior. Thus variables declared with either declaration outside of functions or blocks enclosed in braces will be global in scope.
-
var <var-decl> [, <var-decl>]* ;let <var-decl> [, <var-decl>]* ;where:<var-decl>:=<var-name> [= <expr>]
- Variables declared using 'var' within functions or scope specifications will be global to the function or scope (i.e. will have function scope), regardless of where they are declared within the function or scope.
- Variables declared using 'let' will only be visible to the immediately-enclosing block inside the innermost set of braces (i.e. will have block scope).
-
Variables in HFL have some slight behavioral differences from javascript:
- Variables whose names do not begin with a '$' must be declared. Thus '$foo' would not have to be declared, but 'bar' would.
- Variables whose names begin with a '$' followed by one or more alphanumeric characters can be used within double-quoted strings and will be replaced with their value.
- Variables that are not declared are true global variables, not properties of the global object.
-
EXAMPLES:
-
// Variables without leading '$' must be declared
var count = 1; // Declared global var
count++;
// Variables with leading '$' will expand in double-quoted strings
$wld = 'World'; // Undeclared global var
print("Hello $wld!"); // Prints "Hello World!"
// Variables can take arrays as values
var arr = [0, 1+3, 2]; // Array
var $type = arr[1]; // Access array element
print("Type is $type"); // Prints "Type is 4"
test_nested_vars(); // Call test function
// Variables within a function follow javascript 'var' and 'let' scope rules
function test_nested_vars() {
var $a = 'the', $b = ' quick', $c = ' blue', $d = ' dolphin', $e = ' jumped';
print("$a$b$c$d$e$f");
if ($b) {
let $b1 = ' slow'; // Has block scope
var $c = ' brown'; // Overrides $c
var $f = ' lazy dog'; // Has function scope
print("$a$b1$c$f");
}
print("$a$b$b1$c$d and the sea");
nested();
function nested () { // Inner-function closure
var $d = ' fox', $g = ' walked'; // Has nested function scope
print ("$a$b$c$d$g")
}
print("$a$b$c$d$e over the$f$g");
}
-
-
GENERATES OUTPUT:
-
Hello World!
Type is 4
the quick blue dolphin jumped
the slow brown lazy dog
the quick brown dolphin and the sea
the quick brown fox walked
the quick brown fox jumped over the lazy dog
-
- Pseudo-variables are language components that have the appearance and syntax of a variable reference, but in fact are accessing properties or internal state, often as function calls. All pseudo-variables begin with a leading '$', and thus will be expanded if used inside of double-quoted strings.
- There are two types:
-
Built-in Pseudo-variables
-
Built-in pseudo-variables return important internal state values:$tagReturns the current cell's 'tag' value, if any$classReturns the current cell's .class value, if any$idReturns the current cell's #id value, if any$textReturns the current cell's own text content, if any$alltextReturns the current scopes's text content, includingall of the content of child cells, if any$cell_numReturns the cell number of the first cell of therange of the current scope.$cell_num_lastReturns the cell number of the last cell of therange of the current scope.$cell_rangeReturns the range of cells of the current scopeas an array of two elements.$scope_countReturns the counter value of the current scope's innermostscope. This can be useful in debugging print statements.$page_cell_numReturns the cell number of the parent page containingthe current scope$page_numReturns the scope-start value of the parent page containingthe current scope$page_filenameReturns the output file name the parent page, if a namehas been assigned
-
-
Property Pseudo-variables
- Most cell properties (e.g. 'margin', 'width') can be accessed using pseudo-variables having the same name with a '$' in front, e.g. '$margin', '$width'.
- If the property has a hyphen in the name, e.g. 'margin-left', the corresponding pseudo-variable has an underscore in place of the hyphen, e.g. '$margin_left'
-
EXAMPLES:
-
// Built-in pseudo-variables
if ($tag != 'div') { class='content' } // Set class in non <div>s
print("CURRENT CELL IS $cell_num"); // Print curr cell#
$sz = substr($text, 1, -1); text = $sz // Trim quotes off string cell
if (!$class) { class = "myclass" } // Set class if none
// Property pseudo-variables
color:red; print("THE COLOR IS $color"); // Prints 'THE COLOR IS red'
width:$width/2; // Halves the width
href="$HREF_PATH/$href" // Prepend href with path
-
- HFL supports simple arrays as well as javascript-style Array objects. Unlike in javascript, in HFL a simple array is a data structure, not an object.
- Simple arrays are far more efficient to use and manipulate than array objects because there is no object overhead.
- Object methods can be applied to simple arrays, so the distinction may appear to be moot in most cases.
- But the performance is much lower when using object methods instead of function calls because HFL has to first convert a simple array to an Array object before it can call object methods or access object field values.
-
EXAMPLES:
-
var simple_array = ['a', 'b', c']; // Create a simple array
var array_obj = new Array('a', 'b', 'c'); // Create an array object
// Pop the 'c' off of the simple array using function syntax: efficient
var el1 = pop(simple_array);
// Pop the 'b' off of the simple array using object syntax: inefficient
var el2 = simple_array.pop();
-
- HFL supports true hash tables, as well as javascript-style objects that can be used as hash tables. The difference is that a true hash table only contains the hash entries, without all of the rest of the object baggage. HFL hash tables are not objects.
- In particular, this means the the 'foreach' operator will only find the hash entries, not all of the other properties and method entries that are associated with true javascript-compatible objects.
- HFL hash table values are accessed in the same way as an object property, but can accept any valid HFL expression as a key, not just strings. This allows them to be used for any kind of associative-array lookup.
-
Hash tables are created using the 'hash' function.
-
EXAMPLE:
-
var myhash = hash('A', 1, 'B', 2, 'C', 3, 'D', 4, 'E', 5, 'F', 6);
$nCurr = myhash['B'];
myhash['G'] = 7;
-
-
EXAMPLE:
- Cell properties are properties that correspond to HTML attributes and CSS properties.
- NOTE: They are not javascript object properties. To reduce confusion, within this document cell properties will just be referred to as 'properties', whereas javascript object properties will be referred to as 'object properties'.
- Properties in HFL can be set using either HTML attribute syntax or CSS property syntax.
-
CSS-style properties must end with a semi-colon and are specified by
the syntax:
-
<prop> [<qualiier>] : <value>;
-
-
HTML-style attributes are specfied by the syntax:
-
<attr> = "<string>"<attr> = '<string>'<attr> = <expression>;
-
- Note that CSS-style property-syntax requires semi-colons at the end, and HTML attribute-syntax does not unless the value is an expression or function call. In general it is best to enclose attribute values that are strings within quotes to avoid having them possibly being mistaken for variables or properties.
- Both syntaxes allow the use of variables that have leading '$' characters, allowing a script to assign property values as variables and have them be expanded later. This is especially useful when those values are being passed as function arguments, or are generated by code.
- NOTE: The CSS-style syntax does not allow the values to be expressions or function calls, but does allow expansion of variables within double-quoted strings. If a CSS-style value needs to be calculated, that calculation must be in a separate statement that sets a variable, and the variable used for the value.
-
In addition to the normal properties and attributes, HFL adds a few
special-case properties:
-
text = <text-string>// Set the text of a celltag = <tag>// Set the HTML tag of a cellfinal_tag = <final-tag>// Set the HTML cell tag that will be output
-
-
Further, a few properties support the HFL '+=' operator extension
to perform append actions:
-
class += <text-string>// Append a class to classes of a celltext += <text-string>// Append the text of a cell
- In the case of the 'class' attribute, because when specifying multiple classes in a string the classes need to be separated by spaces, the '+=' operator can be easier to write and simpler to read. As an example these two code segments have the same effect:
-
class += "foobar"
if ($class) { // '$class' returns current class value
class = "$class foobar" // See: 'pseudo-variables'
}
else {
class = 'foobar'
}
-
- As a special case convenience, HFL allows the text of cells to be set implicitly by just specifying them in a double-quoted string. When specified this way the text is inserted as-is, without expansion of any '$' variables as would be other double-quoted strings.
-
EXAMPLES:
p:2-5 { class="titles subheadings" }
p:2 { class+="active" }
div:all {id: "main-content" width: 800px; }
$myheight = "1000px";
div:5 { height:$myheight }
item {href="../index.html" text="Photo Gallery"}
p:6 { text="Paragraph 6" } // Set text of cell explicitly
p:7 { "Paragraph 6" } // Set text of cell implicitly
Temporary Classes, Ids And Properties
- Properties can be created by the application that do not display on output. These 'temporary' properties allow HFL code to mark cells so that they can be located later, and also to attach data to them to be accessed later.
-
Temporary id names
- Temporary id values are ID values that begin with two underscores: "__<digits>".
- They allow for scope matches based upon ids, without resulting in the id value being output into the generated file. They are useful when no real id needs to be assigned.
-
EXAMPLE:
-
// Mark a paragraph as a footnote
p:4 { id ='__footnote' }
...
// Later find the paragraph using normal scope syntax
#__footnote {
...
}
-
-
Temporary class names
- Temporary class names can mark cells using classes for internal tracking, and such temporary classes are suppressed on output, in the same manner as temporary ids. Because HFL supports multiple classes, one can use a temporary class as a marker without it being output as a real class.
- Like temporary ids, temporary class names begin with '__'.
- Temporary class names allow code to be identified using the normal scope engine patterns.
-
EXAMPLE:
-
// Alert that nav_bar was modified, by adding a class
nav_bar { class+="__nav_bar_override" }
...
// Later test if nav_bar was modified by testing for the class
nav_bar {
if (hasClass('__nav_bar_override')) {
...
}
}
// It can be used as a scope-qualifier
.__nav_bar_override:all? {
...
}
-
-
Temporary property names
- Temporary property names allow the application to attach data to cells, which then can be accessed later by other code. This can be used to mark cells for internal tracking and data passing, and such temporary properties are suppressed on output, in the same manner as temporary ids.
- Like temporary ids, temporary properties begin with '__'.
- Temporary properties allow code to be identified using the normal scope engine patterns, and so can be used to mark and later locate specific cells.
-
EXAMPLE:
-
// Alert that nav_bar was modified, by adding a property
nav_bar { __nav_bar_override_style = "table" }
...
// Later test the property pseudo-variable to see if it was modified
nav_bar {
// If have a style value, insert a link to its .css file
if ($__nav_bar_override_style && $__nav_bar_override_style != 'true') {
create { css_link {href="$ROOT_VER/Styles/$__nav_bar_override_style.css"} }
}
}
// Later find the modified nav_bar using its property value as a selector
nav_bar[__nav_bar_override_style='table'] {
...
}
-