Presentation
This document presents the core constructions of the HOP programming language.
It does not present the APIs which are already presented in the
API tab.
HOP is functional expression based programming language. That is:
- All HOP expression as a value.
- Functions are first class values. They can be passed as argument,
returned as result, or stored in data structure.
HOP uses a lexical scoping discipline, that is the functions capture their
definition environment for their free variables.
HOP is safe because it checks types and array bounds. Contrary to
languages such as ML, HOP checks types at runtime.
Syntax
In this text we use the classical conventions. The sign +
denotes a non empty repetition. The sign * denotes a possibly
empty repetition. The sign ? denotes an optional occurrence.
The sign | denotes the alternative.
HOP expressions are either atoms, quotes, or lists.
<expression> := <atom>
| <text>
| <quote>
| <list>
An atom is a syntactic elements described by the following grammar:
<atom> := <number>
| <ident>
| <keyword>
| <string>
| <boolean>
| <character>
| #unspecified
<ident> := [a-zA-Z<>/?|!@%^&*-_=+][a-zA-Z0-9<>/?|!@%^&*-_=+]*
<keyword> := :<ident>
<string> := " <char>+ "
<char> := "any character but " and \"
| \ [\"tnr]
<character> := # \ "a character"
| #\Newline
| #\Space
| #\Return
| #\Tab
<boolean> := #t | #f
Hop supports
texts. A text is made of a list of strings. A text
is actually a syntactic shorthand for building list of strings.
<text> := <square-text>
| <curly-text>
<square-text> := [ ... ]
<curly-text> := { ... }
Inside <square-text> all characters are left as is with two exceptions:
- the character ] must escaped as \],
- the sequence ,( is an escape operator. It introduce an
Hop expression inside a text.
Inside <curly-text> all characters are left as is with two exceptions:
- the characters { and } that are included in the text
but must be balanced. The text is ended when the parse reads a closing
bracket matching the one that has opened the text construction.
- the character $ is an escape operator. It introduces an
Hop expression inside a text. The sequence $$ introduces a
single $ sign in the text construction.
Quotes are:
<quote> := '<ident>
| <literal-vector>
<literal-vector> := '#(<expression>*>)
Lists are:
<list> := (<expression>*)
In addition to these syntactic elements, server-side expressions may use
client-side embedding:
<server-expression> := <expression>
| ~<client-expression>
Symmetrically, client-side expressions may use
server-side embedding:
<client-expression> := <expression>
| $<server-expression>
Definitions
HOP supports definitions of variables, functions, and services:
<definition> := <define-variable>
| <define-function>
| <define-service>
<definition-variable> := (define <ident> <expression>)
<definition-function> := (define (<ident> <ident>*) <expression>)
<definition-service> := (define-service (<ident> <ident>*) <expression>)
Basic constructions
The sequence of HOP expressions is:
<sequence> := (begin <expression> ...)
The alternative is:
<alternative> := (if <expression> <expression> <expression>?)
HOP supports derivative conditional constructions:
<alternative-cond> := (cond ((<expression> <expression>)+))
<alternative-case> := (case <expression> ((<atom>+ <expression>)+))
Local variables are introduced with the
let constructions:
<local-var> := <let> | <let*> | <letrec>
<let> := (let (<bindings>*) <expression>)
<let*> := (let* (<bindings>*) <expression>)
<letrec> := (letrec (<bindings>*) <expression>)
<binding> := <symbol>
| (<symbol> <expression>)
The
let construction binds independent variable. The
let* is
equivalent to a cascade of nested
lets. The
letrec allows
recursive definitions of lexical variables.
Anonymous functions are introduced by
lambda expression:
<lambda> := (lambda (<ident>*) <expression>)
Loops are either defined with
named let or anonymous functions:
<named-let> := (let <symbol> (<binding>*) <expression>)
Function applications are defined as:
<funcall> := (<expression> <expression>*)
Anonymous services are introduced by the
service form:
<service> := (service [:ttl <integer>] [:timeout <integer>] [:url <string>]
(<ident>*) <expression>)
Conditional compilation
Conditional compilation are managed in HOP by the
cond-expand form:
<cond-expand> := (cond-expand (<cond-expand-set> <expression>)+)
<cond-expand-set> := else
| <cond-expand-feature>
| (not <cond-expand-set>)
| (or <cond-expand-set>+)
| (and <cond-expand-set>+)
<cond-expand-feature> := symbol
Hop defines the following features:
- bigloo
- hop
- hop-<version> (i.e., hop-2.0.0)
- hop-<branch> (i.e., hop-2.0.x)
In addition, client-side defines additional features that allows source code
to behave differently on the server and on the client:
See
Conditional Compilation for
additional details.
Modules
Modules structure HOP weblet. Server and client codes may use
modules. A module
export variables, functions, and classes. A
module might
import another module. In that case, it may access to
the imported variables, functions, and classes.
The syntactic structure of a module is as follows:
(module <ident> <clause>+ ...)
<expression>*
A single file cannot contain more than one module. The syntax for the clauses
is:
<clause> := <import>
| <client-import>
| <server-import>
| <library>
| <export>
| <static>
<import> := (import <import-module>+)
| <client-import>
| <server-import>
<import-module> := <ident>
| (<ident> <string>)
<client-import> := ~(import <ident>+) can only be used by server code
<server-import> := $(import <ident>+) can only be used by client code
<library> := (library <ident>+)
<export> := (export <class-or-binding>+)
<class-or-binding> := <variable>
| <function>
| <class>
<variable> := <ident>
<function> := (<ident>+)
| (generic <ident>+)
<class> := (class <ident> <class-field>)
<class-field> := <ident>
| (<ident> <class-attribute>+)
<class-attribute> := read-only
| (default <expression>)
Here is an example of a module named foo that uses the library
ssl, that imports the module named bar, and that exports the
function fun-foo, the variable var-foo, and the class class-foo.
(module foo
(library ssl)
(import bar)
(export (fun-foo x)
var-foo
(class class-foo
(name read-only)
(x (default 0))
(y (default 0)))))
Module Access File
Hop is different from languages such as C where a module is defined by a
file. For Hop, the module name is not necessarily the name of the file
where the text of the module is written and modules can even be split across
several files.
Since modules are defined independently of files, it is necessary to make a
link between a module and its files and there are two ways of doing this.
Choosing an import clause where the file-names are specified or creating a
module access file
. Such a file must contain only one list, each
element of the list being of the form:
(module-name "file-name" ... "file-name")
By default Hop checks if a file named .afile exists. If it
exists it is loaded as a module access file.
Note: The Bigloo distribution contains a tool, bglafile,
that can automatically build a module access file
. See the manpage for
bglafile for details.
Mixer server and client modules
Since Hop-2.0.x, servers and clients may mutually import
modules. That is, server modules may import client modules to access
to client functions and variables when producing a HTML
tree. Respectively, client modules may import server modules, for
instance, for accessing services from client code. The following
example illustrate this mutual imports.
The following server module defines a weblet drawing chart:
(module foo-server
(import (gchart "http://localhost:8080/hop/weblets/download?weblet=gchart-api-1.0.0.hz"))
(export img pre)
~(import foo-client))
(define types '(bhs bvs bhg bvg p p3 pc chp v s r rs))
(define tnum 0)
(define (new-url)
(let ((len (length types)))
(set! tnum (+fx tnum 1))
(if (>=fx tnum len) (set! tnum 0))
(google-chart
:colors '("a25cff" "f72f2b"
"253c81" "c62f6d"
"522e81" "f79326"
"f7d93a" "aef742"
"69f7ab" "3df7ec")
:type (list-ref types tnum)
:data (map (lambda (l) (cons (symbol->string l) (random 100)))
types))))
(define url (new-url))
(define img (<IMG> :id "img" :src url :title "An image"))
(define pre (<PRE> :id "pre" :style "font-size: 70%" url))
(define-service (foo)
(<HTML>
(<HEAD> :jscript (service-resource foo "foo.scm"))
(<BODY>
(<BUTTON> :onclick ~(with-hop ($(service () (new-url))) update)
"New barchart")
(<DIV> :style "margin-top: 2ex"
img
pre))))
The server modules imports the client module named
client defined as:
(module foo-client
$(import foo-server)
(export (update)))
(define (update src)
(innerHTML-set! $pre src)
(set! $img.src src))
These modules assume a
module access file
the
.afile defined as:
(
(server "foo.hop")
(client "foo.scm")
)
Server Side core language
This section presents the HOP constructions that are only applicable to
server-side programming.
Classes and Objects
The server side HOP object model is inspired by the
Common Lisp Object System.
Contrary to languages such as SmallTalk or Java, in
that model,
methods are not attached to classes but to
generic
functions. This enables code to add new functionalities to
pre-existing data structures (objects). The HOP declination uses
single inheritance and single dispatch. That is, a class may inherit
from exactly one class and only the first argument of method explored
to resolve the late binding.
Server side classes are defined in the module declaration. A class may either
be exported in which case all the weblet might create objects of class,
declare subclasses, generic functions, and methods.
For the sake of the example, here is a simple source code that defines
two classes:
(module example
(static (class point x y))
(export (class point3d::point z))
(export (generic to-string ::object)))
(define-generic (to-string obj)
(with-output-to-string (lambda () (display obj))))
(define-method (to-string obj::point)
(with-access::point obj (x y)
(format "point:~a-~a" x y)))
(define-method (to-string obj::point3d)
(with-access::point3d obj (z)
(format "~a-~a" (call-next-method) z)))
The form
define-generic defines a new generic function. The form
define-method adds a method to a generic function. The generic function
must be visible by the module declaring the method. The form
with-access
opens an object up. Objects are creates with the
instantiate form. Here
is an expression creating an instance of the
point3d:
(instantiate::point3d
(z 10)
(x 0)
(y 5))
Class fields that are provided with a default value are not required by
instantiate.
Character set
The character set (charset) used for denoting strings of characters varies
depending on the execution context and the configuration parameters.
On the client-side, the charset used is the one of the hosting platform.
Since HOP client-side expressions are designed to be strictly compatible
with JavaScript, these expressions adopt the JavScript charset which is
UCS-2.
The communications between the client and the server can be changed but
by default UTF-8 is used.
The charset of the server depends on two parameters: hop-locale
and hop-charset. The first one specifies the charset of the native
environment hosting the server (i.e., the charset of the operating that
is running the server). The latter specifies how HOP internally represents
character strings. Hence, if hop-locale differs from hop-charset
a translation mechanism is involved when expressions are read. Several
functions (charset-convert, iso-latin->utf8, or
ucs2-string->utf8-string) can be used for converting strings from
one charset into another.