Table of Contents
The Hecl Programming Language is a high-level scripting language implemented in Java. It is intended to be small, extensible, extremely flexible, and easy to learn and use.
Why Hecl? Hecl is intended as a complement to the Java
programming language, not a replacement. It tries to do well
what Java doesn't, and leaves those tasks to Java for which it
is best suited, by providing an API that makes it easy to tie
the two together. Hecl aims to be a very immediate language -
you can pick it up and start doing useful things with it quickly
- even people without formal training. Hecl is easy to learn.
Where Java is verbose and rigid, Hecl is forgiving and quick to
write. For instance,
System.out.println("Hello World");
vs puts "Hello World"
- 41
keystrokes (shifted letters count double) versus 22. Hecl is
built to "scale
down" - especially in terms of its users, meaning that
it is very quick to learn, and can be quickly put to productive
use even by those who are not programmers by trade.
This makes Hecl ideal for large applications written in Java that would like to provide a user friendly scripting interface, rather than, say, a clunky XML based system. Examples include: scripted web pages, command/control logic in long running applications, and, I'm sure, many environments I've never considered. Instead of a simple, static configuration file, you can give your users the power to program portions of the system to do things that you hadn't thought of when you wrote the software originally.
Hecl is a small language with a minimal core. The idea is to provide only what's necessary in the language itself, and as needed, add in extensions for specific tasks. Core Hecl is small enough to run on my Nokia 3100 cell phone as a J2ME application, presenting the interesting possibility of writing scripts, or at some point, maybe even scripting entire applications, for devices running embedded Java.
Contributions in the form of code, ideas, suggestions, or even donations are welcome. Hecl is still growing, so your thoughts are important, and you can help shape the language's future. You can download the code via CVS from the SourceForge project page: .
Hecl is available under the liberal Apache 2.0 open source license. Which says, more or less, that you may use Hecl in your own applications, even if they are not open source. You have to give me credit, though. Read the license itself to clear up any doubts. Oh, and incidentally, I don't see the license as being incompatible with the GPL, so feel free to utilize Hecl in your GPL product (I have added a note to this effect in the NOTICE file that must accompany products using the Hecl code).
I owe thanks to a lot of people for Hecl. First and foremost the creator of the Tcl programming language, Dr. John Ousterhout. While I have attempted to improve some things that I did not care for in Tcl, it is obvious that the simple, extremely flexible command-based approach that Hecl takes is derived from Tcl. I also borrowed some ideas from the (mostly defunct) Jacl implementation of Tcl in Java. Many thanks are also due my friend Salvatore Sanfilippo, with whom I have spent many hours discussing Hecl, Tcl, and the philosophy of programming languages in general. And of course, I owe a huge debt of gratitude to my fianceè, Ilenia, who puts up with all the hours I spend in front of "that damn computer".
Hecl is easy to compile and install as a standard J2SE application.
ant packageCommandline
java -jar Hecl.jar Hecl hecl> puts "hello world" hello world
gcj -o hecl --main=Hecl Hecl.jar davidw@medford:~/workshop/hecl$ ./hecl hecl> puts "hello world" hello world
java -classpath Hecl.jar Hecl tests/suite.hclA performance test is also supplied so that you can compare numbers if you're curious, or want to hack on Hecl to improve its speed:
java -classpath Hecl.jar Hecl tests/performance.hcl
Like many people, I enjoy taking something and experimenting with it before going and reading the instructions! With this in mind, I have written a brief tutorial that you can use to start exploring Hecl on your own.
Of course, we would be out of place not to begin with the famous "Hello, World". Behold:
puts "Hello, World"
Hecl is based on the notion of commands, which take any number of arguments. The puts command takes one argument, a string, and prints it out.
Like all programming languages, Hecl provides variables that may
be used to store information. Here, we set a variable,
rating
, and then print it out in the midst of
a string. This is called "interpolation", and is a convenient
way of creating new strings.
set rating 10 puts "Hecl, from 1 to 10: $rating"
Something else we notice in the above examples is that we use
double quotes "" to group a series of things. In Hecl, commands
and their arguments are separated by spaces. Since
puts only takes one argument, a string, we
use the quotes to group several words together in order to pass
them as one string to the command. Many languages require
quotes to delineate a string, but in Hecl that is not necessary
if the string has no spaces in it. For instance,
puts helloworld
is legitimate.
Another way of grouping multiple words in Hecl is with braces: {}. Hecl does not automatically perform any substitution on the variables or commands grouped within braces, as it does with quotes.
puts {The $dollar $signs $are printed literally$$ - no substitution}
Aside from the dollar sign, which returns a copy of the value of a variable, it is also possible to utilize the results of one command as the input of a second command. For example:
set rating 10 puts "Rating:" puts [set rating]
In this case, we pass the results of the set
command to the puts command. In reality, set
rating
is just a long way of writing
$rating
but it's a good example.
Like everything else in Hecl, we perform math operations as commands:
puts "2 + 2 = [+ 2 2]"
In the example, the + takes two arguments, adds them together and return the result, which is then printed out by the puts command.
In order to choose between one or more
set temp 10 if { < $temp 0 } { puts "It's freezing" } else { puts "Not freezing" }
References:
set a 1 set b &a puts $b # Returns '1' set a 2 puts $b # Returns '2'
"while" loop command:
set i 0 while { < &i 10 } { puts "i is now $i" incr &i }
Lists:
set foo [list a b c] set bar {a b c} lappend &foo d lappend &bar d set foo # Returns 'a b c d' set bar # Returns 'a b c d'
Hash tables:
set foo [hash {a b c d}] puts [hget &foo a] # prints 'b' puts [hget &foo c] # prints 'd' hset &foo c 2 puts [hget &foo c] # prints '2' puts $foo # prints 'a b c 2' (although not necessarily in that order)
"foreach" loop command:
set lst {a b c d e f} foreach {m n} $lst { puts "It is possible to grab two variables at a time: $m $n" } foreach {x} $lst { puts "Or one at a time: $x" }
Create new commands with the "proc" command. In this example we create a command that prints out a numbered list.
set list {red blue green} proc printvals {vals} { set num 1 foreach v $vals { puts "$num - $v" incr &num } } printvals &list
Hecl is very flexible - in this example, we create a "do...while" loop command that works as if it were a native loop construct.
proc do {code while condition} { upeval $code while { upeval &condition } { upeval $code } } set x 100 set foo "" do { append &foo $x incr &x } while { < &x 10 } set foo # Returns 100 - because the loop is run once and only once.
+ - * / — Basic math commands.
+
number
number
-
number
number
*
number
number
/
number
number
append — Append text to a variable.
append
varreference
string
break — Break out of a loop.
break
catch — Evaluates a script, catching any errors.
catch
script
?varname
?
The catch command evaluates a script, and returns 0 if the script evaluated successfully. If there were errors, catch returns 1. Optionally, a variable name may be passed to the command, where the results of the script evaluation will be placed. In the case of errors, the stack trace will be placed in the variable argument. If the script executes without problems, the variable will contain the result of the script execution.
continue — Skip to next cycle of a loop
continue
eval — Evaluate Hecl code.
eval
code
filter — Filter a list.
filter
list
varname
script
for — For loop.
for
initialization
test
step
body
The for command is like in many other
languages like C and Java. As arguments, it takes an
option, which is often used to set a variable to some
initial value, a
initialization
to
determine whether to continue running, a
test
script
option which is run at each iteration of the body (to
increment a variable, for example), and the body itself.
step
foreach — Iterate over elements in a list.
foreach
varname
list
body
foreach
varlist
list
body
global — Use global variable from within a proc.
global
varname
hash — Create and manipulate hash tables.
hash
list
hget
hash
key
hset
hash
key
value
The hash command takes an even-numbered list and creates a hash table from it, using the even elements as keys, and odd elements as values. A new hash table is returned. The hget and hset commands operate on hash tables. Both take a hash table as their first argument. hget also takes a key, and returns the corresponding value, or an error if no key by that name exists.
if — Conditionally execute code.
if
test
code
?elseif
test
code
...? ?else
code
?
The if command executes Hecl code conditionally. In its
most basic form, it executes a
. If the
results are not 0, then it executes
test
. If not,
no further actions take place. if may
take any number of code
elseif
clauses, which
have their own
and
test
. Finally,
if none of the conditions has matched, it is also possible
to supply an code
else
clause that will be
executed if the results of the if and elseif tests were all
false.
intro — Introspection command.
intro
?commands?
join — Join elements of a list to create a string.
join
list
?string
?
lindex — Return the Nth element of a list
lindex
list
index
lset — Set list elements.
lset
listref
index
?replacement
?
proc — Create a new procedure.
proc
name
arglist
body
The proc command creates new procedures,
which are virtually indistinguishable from built-in Hecl
commands.
is the name of the new command,
name
is a
list of arguments that the new command will take and make
available as local variables within the
arglist
, which is
the code executed every time the command is called.
body
search — Find the first instance of something in a list.
search
list
varname
script
set — Set a variable.
set
varname
?value
?
split — Split a string into a list.
split
string
?splitstring
?
time — Time the execution of a script.
time
script
?repetitions
?
Hecl is not a replacement for Java, and is indeed meant to work hand in hand with Java. We attempt to make it as easy as possible to call Java from Hecl, via the creation of new Hecl commands that can call Java code, in addition to calling Hecl from Java, which is a matter of a few lines of code. For example, to evaluate a Hecl file from Java:
try { /* First, create a new interpreter, and pass it a * mechanism to load resources with - in this case, * files. */ Interp interp = new Interp(new LoadFile()); /* Initialize the standard Hecl environment. */ com.dedasys.hecl.Standard.init(interp); /* Evaluate the file at args[0] */ Eval.eval(interp, interp.getscript(args[0])); } catch (Exception e) { System.err.println(e); }
The above code creates a new interpreter. Currently,
interpreters must know about how they are to load files, so the
interpreter is instantiated with a new
LoadFile
, which, as the name suggests,
contains code to load files from the file system. Since Hecl is
also meant for embedded environments, it is also possible to
create loader classes that load code from a compiled-in string,
from the network, or whatever other form of storage you may have
in mind.
After creating the interpreter, we call
Standard
's init
method. Standard
is a class that adds
several commands that we want to be present in standard J2SE
Hecl, but not in J2ME Hecl.
The real work is done by Eval.init
,
which takes an interpreter and a Hecl
Thing
as arguments. In this case, the
Thing
is the Hecl code read from the disk
by LoadFile
.
Creating new Hecl commands is relatively simple. The first
step is to create a new class for your command in a file, say
HelloCmd.java
. The code would look
something like this:
class HelloCmd implements Command { public void cmdCode(Interp interp, Thing[] argv) throws HeclException { System.out.println("Hello world"); } }
The command takes an interpreter and an array of
Thing
s as arguments, where the first
Thing
is the name of the command
itself, and the others are the arguments to it.
The "glue" that connects the name of your Hecl command with the Java code is also relatively simple:
interp.commands.put("hello", new HelloCmd());
Easy, no? There are a few other useful methods that you should be aware of, to share variables between Hecl and Java, and to return results from your Hecl commands:
interp.setVar(Thing varname,
Thing value);
This sets the value of varname
to
some value.
Thing interp.getVar(Thing varname);
Thing interp.getVar(String varname);
These methods take a variable name, either in string form or
as a Thing
, and return the
Thing
associated with that variable.
interp.result
is used to set the
result of a command. This oft-used variable is accessed
directly for simplicity, speed and smaller code size.
int IntThing.get(Thing thing);
Get an int from a Thing.
String StringThing.get(Thing thing);
Get a String from a Thing.
Thing IntThing.create(int i);
Creates a new thing from an int.
For the complete Hecl javadoc documentation, see the Hecl Javadocs. And, of course, look at the Hecl source code to see how it's done!