Functionally Equivalent Language Translation → FELT the buzz yet?
(#f:fn "fixed-string") (#f:fn var-name) (#f:fn expression)
Generates a function name using the syntax rules of the currently active back-end coder. You can have it rendered within quotes or raw. The starred variant causes the value to be output "naked".
FELT tries to be as helpful as it can in allowing you to try to produce language neutral source texts but there are times when you just have to do something that is specific to a particular back-end coder language. For these examples we are of course going to be talking about PHP as that was the first language that FELT was made for.
Once you see what the #F:FN
instruction actually does then you will hopefully understand what the others do and may be useful for as well!
There is one very important point to make first before we explain how and when using these pre-processor instructions is useful. You will see from the generated code in the coming examples that there is one use of these functions that causes some code to be inserted into the PHP output which executes at run-time and currently only for PHP as FELT is written in PHP. This means that for now at least, using any of these instructions with a variable name as the argument only works for the PHPCoder back-end.
That was quite a mouthful but it is important that this current limitation be pointed out lest you end up head-scratching and wasting your valuable time trying to do something that just isn't possible. Yet!
The technical reason for that is that the following PHP code is emitted to make a run-time call to translate the contents of the variable, here is a simple FELT test case to show what we are talking about:
(defvar varname "foo-bar") (#f:fn varname) (#f:var varname) (#f:sym varname)
and the corresponding PHP output is:
$varname = "foo-bar"; FELTCoder::Me()->asName($varname) FELTCoder::Me()->asVarName($varname) FELTCoder::Me()->asSymbolName($varname);
And of course, for that to be viable, the code has to be running in a PHP environment AND within the context of the FELT system; currently that means this mode of operation is available to server-based applications only..
This is the main way that you probably will use all of these instructions. In short, they translate the FELT syntax within the string into a name that is syntactically correct for the currently active back-end coder class.
(#f:sym "fixed-string") (#f:sym var-name) (#f:sym expression)
Generates a symbol name using the syntax rules of the currently active back-end coder. You can have it rendered within quotes or raw. The starred variant causes the value to be output "naked".
There are very important things to know about using this function at run-time. Please go to the #F:FN instruction page for full information about this and the other related pre-processor instructions that generate coder related output texts.
(#f:var "fixed-string") (#f:var var-name) (#f:var expression)
Generates a variable name using the syntax rules of the currently active back-end coder. You can have it rendered within quotes or raw. The starred variant causes the value to be output "naked".
There are very important things to know about using this function at run-time. Please go to the #F:FN instruction page for full information about this and the other related pre-processor instructions that generate coder related output texts.
(#f:when coder-class expressions) (#f:when PHPCoder ...body...) (#f:when JSCoder ...body...) (#f:when CSSCoder ...body...) (#f:when NodejsCoder ...body...)
This pre-processor instruction checks to see if the current back-end output coder is FOOCoder
and if so it will add the contents of the body expression to the output source text, translated of course into language FOO. Coder names are case sensitive.
If you have a small script that you'd like to keep all in one file but there are small differences that you need to take into account, for example, functions in language A not being present in language B then this instruction is what you will need to use to manage those differences. For larger projects or for cleaner FELT code, it is easier just to put all language specifics into a single file and then ensure that whatever tool-chain you use is able to pull in what it needs when it needs it.
For example, we've already seen during many of the instruction reference pages places where the PHP implode
function was used to concatenate an array of strings and produce a final string... javaScript of course does not have that instruction in its natural set of reserved words. So how do we do it? Simple, by adding this to your FELT source. In the examples, for simplicity and clarity, we added the extra function explicitly in the example but here can will use the "clever" way to do it:
(#f:when JSCoder (defun implode (x) (return (x.join))))
Now when we run the FELT source it will generate a function called implode
that works on the javaScript language target and produce no output at all for any other target language.
Here is the current (case-sensitive) list of coder names as a short example.
(#f:when PHPCoder (emit "PHP")) (#f:when JSCoder (emit "javaScript")) (#f:when NodejsCoder (emit "Node.js")) (#f:when CSSCoder (emit "CSS, commented out!"))
One of the places were this is used on the actual FELT site is in the tag library, it will help if we display and explain the code and how and why it works. That should be explanation enough on how to use the TYPEOF
instruction.
One of the coolest files in the entire project! This file contains a really simple HTML generation library that is used by both the PHP server side and the javaScript client side code because simple rules were followed and allowed FELT to generate functionally equivalent code for both. Every page from the server uses it and the client-side code using it is the automatic table-of-contents generation.
One of the core functions in the tag library is called (->string x)
, what this does is take any argument and attempt to return a st
Essentially it looks at the type of its argument and then, after determing the type, will return a string representation of that argument by following these rules:
So, how can it know what to do for PHP and Javscript? Answer: It can't! That's where the #F:WHEN
instruction comes to the rescue by allowing us to provide two different implementations of the same function at render time.
Here is the actual code that powers this site in PHP mode:
(#f:when PHPCoder (defun ->string (thing (glue "")) (if (is-string thing) (return thing)) (if (is-array thing) (return (implode glue thing))) (if (is-object thing) (return (-> thing (__toString)))) (return thing)))
(#f:when JSCoder (defun ->string (thing) (defvar glue "") (if (== 2 arguments.length) (= glue (@ arguments 1))) (defvar isa Object.prototype.toString) (defvar type (-> isa (call thing))) (switch type "[object Array]" (return (thing.join glue)) "[object String]" (return thing)) (return (concat "" thing))) ;; ;; Simulates PHP func_get_args() but requires that the locally ;; scoped "arguments" value is passed in to be able to work. We use ;; (@<) to ensure the reutrned array is in the same order. ;; (defun func-get-args (the-args) (defvar args []) (foreach (the-args a-key a-val) (@< args a-val)) (return args)))
(= var-name expression) (= total 0) ; sets the total to zero (= (@ foo :bar) 42) ; writes 42 into slot "bar" in array "foo"
See also SETQ :=/DEFVAR ==(EQUALS)
This instruction is named after the Lisp 'set quote' command. It performs the same task and so please refer to the =(ASSIGN) instruction page for complete instructions and examples.
For writing FELT code that will be converted into conventionally imperative style language, you will be using this instruction pretty much all of the time... it's what you use to assign the value of one variable or expression into a destination slot.
You should also consult DEFVAR for the notes on writing portable code regarding the use of =
and DEFVAR / :=
.
It is worth stating again that the original instruction set and operators and such like were all inspired, if that is the right word to use, by PHP and as a consequence FELT supports all of the operators that PHP does (well, I've not found one it doesn't do yet but if you do, let me know as sometimes it's easy to forget the simple things!).
As FELT is modeled on LISP, it is possible in FELT to take advantage of the fact that you don't need to explicitly place an operator between every pair of arguments, like PHP makes you do. For instance, here is how you would add a bunch of numbers in PHP:
$total = 1 + 2 + 3 + 4 + 5;
That's a lot of plus signs when all you want to do is add them! Here is the same program in FELT:
(defvar total (+ 1 2 3 4 5))
This concept applies throughout, the same concept as stated in the FELT Syntax Page page where is says that there is one form for all instructions, which is:
(instruction arg1 arg2 ... arg3)
Hopefully you can see that using +
, or *
etc. is just another instruction, albeit a numerical operation.
The following is a simple list of the operators that FELT has been told to look out for.
These are the standard operators that you would expect to find. These can have any number of arguments applied and the operator will be placed in between each as desribed above to generate the equivalent expression in the chosen back-end target language:
Operator | Description |
---|---|
+ | (+ a b ... N) addition |
- | (- a b ... N) subtraction |
* | (* a b ... N) multiplication |
/ | (/ a b ... N) division |
The remainder of the operators follow the same pattern i.e. as many arguments as you want to put in:
Operator | Description |
---|---|
. | string concatenation |
% | arithmetic modulo |
& | bit-wise AND |
| | bit-wise OR |
^ | bit-wise XOR |
|| | logical OR |
and | bit-wise AND |
or | bit-wise OR |
xor | bit-wise XOR |
concat | string concatenation |
The following are "unary" operators; they expect a single argument when called:
Operator | Description |
---|---|
! | logical-NOT brief |
not | logical-NOT longform |
The following are "binary" operators in that by their very nature they expect to have only two parameters passed with them:
Operator | Description |
---|---|
> | (> a b) -- a greater than b |
< | (< a b) -- a less than b |
>= | (>= a b) -- a greater than or equal to b |
<= | (<= a b) -- a less than or equal to b |
== | (== a b) -- a equal to b |
!= | (!= a b) -- a not equal to b |
=== | (=== a b) -- a same object/instance etc as b |
!== | (!== a b) -- a not same object/instance as b |
(alen var-name) ; return the array length (alen array-var e1 .. eN) ; a nested array reference (@# var-name) ; return the array length short-form (@# foo :names 0) ; short-form, nested array reference
This requests that the back-end coder do whatever the target language permits to return the length of the variable, assumed to contain an 'array' of some type or other.
Most languages return an integer value if the variable that is passed is an Array type and if not, the behaviour is wide, varied and usually going to result in some kind of run-time error or warning being displayed somewhere in the output stream.
(alist) ; an empty association list {} ; an empty association list (defvar foo {:n 1]} ; contains one key, "n" with value, number 1 (defvar foo (alist "X" "unknown") ; contains one key, "X" with value "unknown" (defvar foo {:sum (+ 1 2) :when (time)}) ; contains (1+2) and the current time
The ALIST
instruction has the short-form {}
. Please refer to ARRAY for guidelines on what you can put into an ALIST. The key point to remember is that the number of entries must be divisible by two, that is, the keys and values must be equally paired up and they are read as k1 v1 k2 v2 ... kN vN
until the end of the instruction. If you do not provide an even number of entries then an error will be generated at translation time.
As with creating an array, creating an association list is equally simple. You can use the ALIST
instruction or the short-form, curly braces. The choice of curly braces is very similar to the JSON "object" notation and was purposely chosen because of that. An association list is essentially an object from that perspective.
FELT places no restrictions on what you can put inside an association list, as the key or as the value, but it must make syntactically correct code via the chosen back-end or you will of course experience execution / compile errors.
Here are some examples in PHP and javaScript so that you can see what you get. As stated, FELT itself places absolutely no restrictions on what you can place inside an association list; the onus is on you as the developer to know what you are doing and how you are going to be rendering out the code in the future.
There are no portability notes to take into consideration when using this instruction. The declarations can be as simple or as complex as you want to make the. Let's convert the following into PHP and then javaScript:
(defvar foo (alist :one "Hello" :two "World")) (defvar bar {:name "Emacs" :job "Viking"}) (defvar baz {"alpha" [1 2] "beta" [3 :four] :gamma 42})
The above declarations produce this when translated to PHP:
$foo = array('one' => "Hello", 'two' => "World"); $bar = array('name' => "Emacs", 'job' => "Viking"); $baz = array("alpha" => array(1, 2), "beta" => array(3, 'four'), 'gamma' => 42);
And this when rendered as javaScript:
var foo = {'one' : "Hello", 'two' : "World"}; var bar = {'name' : "Emacs", 'job' : "Viking"}; var baz = {"alpha" : [1, 2], "beta" : [3, 'four'], 'gamma' : 42};
(and expr-1 expr-2 ... exprN)
This is the usual "logical AND" instruction which will evaluate to "true" if all of the terms within it also evaluate to "true". Some back-end languages use short-circuiting and some do not so you will have to research how the rendered code operates on each chosen backend.
(apop aref-expression) (apop foo) ; pop from the end of Array 'foo' (@> foo) ; same as above, short form
The APOP
instruction has the short-form @>
. The greater-than sign facing away from the @
symbol is meant to visually indicate that something is leaving and that it is leaving from the end of the array. The value that is removed can be assigned to a variable or used in any other way that generates valid output syntax in the targeted language. Any valid array expression (see AREF/@ can be used after the APOP instruction.
Here is the FELT source we are going to convert to PHP and javaScript:
(defvar items [ 10 20 "Thirty" [:a :b :c]]) (defvar a1 (apop items)) ; the array (defvar v30 (apop items)) (defvar v20 (@> items)) (defvar v10 (@> items)) (emit "Item count, zero == " (count items) "\n") (emit "'a' == " (@> a1) "\n") (apop a1) ; remove 'b' (emit "'c' == " (@> a1) "\n")
Now let's render the code as PHP and execute it to show it worked:
$items = array(10, 20, "Thirty", array('a', 'b', 'c')); $a1 = array_pop($items); $v30 = array_pop($items); $v20 = array_pop($items); $v10 = array_pop($items); echo "Item count, zero == ", count($items), "\n"; echo "'c' == ", array_pop($a1), "\n"; array_pop($a1); echo "'a' == ", array_pop($a1), "\n";
and when run through the PHP interpreter we get this output:
$ felt docex.felt -cw | php felt docex.felt -cw | php Item count0 'c' == c 'a' == a
And finally for Node/javaScript, this being the rendered code:
var items = [10, 20, "Thirty", ['a', 'b', 'c']]; var a1 = items.pop(); var v30 = items.pop(); var v20 = items.pop(); var v10 = items.pop(); console.log("Item count, zero == "+ count(items)+ "\n"); console.log("'a' == "+ a1.pop()+ "\n"); a1.pop(); console.log("'c' == "+ a1.pop()+ "\n");
Note that we had to provide an implementation of count
for this code to work at run-time. When piped into "node" we get this output:
felt docex.felt -as nodejscoder | node Item count0 'c' == c 'a' == a
We could have put the count
function in the original code example but I wanted to keep it concise. Please see the #F:WHEN instruction for complete details on how to provide different versions of functions for different target languages.
(apply func-name arg1 arg2 ... argN) ; arguments passed through (apply* func-name [arg1 arg2 ... argN) ; arguments in an array
See also CALL CALLSTATIC
This instruction indicates to the back-end coder that you want to call a function indirectly either through a string literal or a variable.
If you are defining a class method / function then this instruction, DEFUN
is the same as defining a method that has public access. FELT supports the notion of methods that can be "public", "private" or "protected" and in order to express those intentions you can choose from the following:
Instruction | Visibility |
---|---|
DEFUN |
public |
DEFUN+ |
public |
DEFUN- |
private |
DEFUN# |
protected |
This pattern of characters at the end is also the same for when you want to declare a class method or "static method", for which you should refer to the DEFUNC page for more information.
There is a variation on the DEFUN
form that allows you to declare a function that has no name, just a signature and a body. This is used a lot for the javaScript (JSCoder) back-end and also in the PHP back-end. FELT is clever enough to see if your version of PHP supports anonymous functions (PHP5.3+) or not and acts accordingly; if you have them it uses them, if you don't it will generate a unique name for that function and at the call site the name of that function is emitted and then in the "post-render" phase of translation the actual functions will be rendered out. For most of the time this works just fine and causes no troubles, and is completely transparent to you as a developer.
You can create closures using the DEFUN*
form but FELT itself has absolutely no bearing at all on whether or not it will work as success ultimately depends completely and utterly on the chosen output language.
All I can tell you is that for PHP, for example, if you follow "the rules" relating to closures and anonymous functions with respect to scope (see PHP -- Anonymous functions then you should be fine, the code that comprises this site makes use of them in plenty of places and it works as you would expect it to.
FELT allows most types of function signatures to be created, including default parameters and reference parameters. Not all languages support these features so again, be careful about what you are doing. If you try to use a feature that is not supported in a particular language then the core classes (those that come with FELT out of the box) will generate a warning message in the rendered code. Anybody else that writes a back-end coder should also follow the same principles.
On the surface of it FELT would appear to fall into the category of "loosely typed" as defining parameters doesn't include specifying a type. FELT was initially written to allow PHP code to be created using a LISP like syntax but it kind of grew from there when I realised that, given the amount of effort that I was putting into it, I might as well tackle all loosely-typed languages at the same time and even then, in the back of my mind I was hatching a plan to cope with typed languages as well. That will come later, for now let's continue in the loosely typed world of languages...
Let's define a function that takes two parameters and then returns the result of adding them together. Remember that this doesn't actually mean the function will work at run-time because if the parameters are of different types and the language doesn't allow the addition of such things then get the mop and bucket ready. PHP however does allow such things, as do some other languages. Here's the function definition and the PHP:
(defun add-em-up (p1 p2) (return (+ p1 p2)))
which produces PHP code like this:
function add_em_up($p1, $p2) { return ($p1 + $p2); }
Nothing too exciting there which is what we want; good clean normal run-of-the-mill parameters. And remember that FELT allows you to name parameters using almost any printable characters as it converts them into a valid form for the chosen back-end language, for example, lets re-do the above with some funkier names all round:
(defun vals->total (val#1 val#2) (return (+ val#1 val#2)))
and the corresponding javaScript code:
function vals__gt_total($val_hash_1, $val_hash_2) { return ($val_hash_1 + $val_hash_2); }
Looks kind of interesting but it is still valid PHP nonetheless and will run just fine. The point is that FELT allows you greater expressivity of variable and function naming which hopefully means more readable intentions. I have said before that FELT doesn't care for good looking output code as I don't want you to look at anything except FELT code. If it works, it works. Your code repositories will be full of FELT code which is of course beautiful to look at being essentially LISP to look at. ;)
FELT allows you to define parameters that can have what is commonly referred to as a "default" value, sometimes these are referred to as "optional" parameters. Again not all languages support this and the back-end coders will adapt accordingly.
Here is the initial example function but this time the second parameter has been modified so that "by default" it will add one to the first parameter, the output this time is again PHP for reference with the original but javaScript is also shown as a comparison because javaScript does not have this facility. If you think you will be needing this feature then the best way is to design your functions to take a single array that contains keys and values and go from there.
(defun vals->total (val#1 (val#2 1)) (return (+ val#1 val#2)))
which yields the following PHP output:
function vals__gt_total($val_hash_1, $val_hash_2 = 1) { return ($val_hash_1 + $val_hash_2); }
And for a quick reference you can see that the javaScript output just doesn't do anything with the default and assumes normal operation. This is one of the things to keep in mind when writing code that you think you may want to re-use on a different target!
function vals__gt_total(val_hash_1, val_hash_2) { return (val_hash_1 + val_hash_2); }
FELT supports the notion of mutable parameters, often referred to as "reference" parameters by many languages and sometimes as "out" parameters in other systems. Essentially they are alterable by the function being executed.
To declare a reference parameter in felt, all you do is place the keyword :ref
after the name of the variable and enclose them in parentheses. Let's re-write the example so that this time we return nothing but write the answer back into the reference parameter passed in that will the total:
(defun add-em-up (p1 p2 (total :ref)) (= total (+ p1 p2)))
and the PHP output produced is:
function add_em_up($p1, $p2, &$total) { $total = ($p1 + $p2); }
This has been one of the more interesting and challenging parts of creating FELT and whilst it may look a little ugly in places, it does work and currently the Java back-end is functional but not yet ready for general release until I am happy that it is good enough to do Android development.
Simply, by taking advantage of the fact that FELT allows most printable characters to be used in a variable name and a function name, those back-end coders that produce typed languages will look for the "reverse solidus" or, more commonly "/" (forward slash) and infer from the characters after it a type for the the function or variable.
The Java coder has a built in set of types that it will recognise. They are listed in full in the Java Coder Documentation pages but for clarity here is a short example of a class in FELT that produces a Java class that when run produces a familiar greeting:
(class Greeting (defunc+ main/v (args/as) (emit "Hello World")))
And the following console session shows how that translates into Java code and then compiles it and runs it:
$ felt greeting.felt -as javacoder class Greeting { public static void main(String[] args) {System.out.println("Hello, World!");} } $ felt docex.felt -as javacoder -o Greeting.java $ javac Greeting.java $ java Greeting Hello, World!
So, FELT can and does support typed languages but it is a little early for me to say exactly how this will pan out. I am working on this and I think that what I have in mind will be good enough, which is usually all that is required to make things work. It involves each "typed" coder having a built in set of types plus the ability for you as the FELT developer to "plug-in" your own set of abbreviations and by following some simples rules, that should be all you need to manage typed output. For now, using the Java coder is definitely classed as experimental as it is not officially ready for release but I have made it available nonetheless.
(apush aref-expression value) (apush foo 100) ; push 100 into the end of Array 'foo' (@< foo 100) ; same as above, short form
The APUSH
instruction has the short-form @<
. The greater-than sign facing towards the @
symbol is meant to visually indicate that something is being added and that it is being added to the end of the array. The value that is added must generate a valid term in the target language. Any valid array expression (see AREF/@ can be used after the APUSH instruction.
Here is the FELT source we are going to convert to PHP and javaScript:
(defvar items []) (@< items []) ; items now contains a nested array (apush (items 0) "Hello") (@< (items 0) "World") (defvar message (implode (@ items 0) " ")) (emit "That message says: " message "\n")
Now let's render the code as PHP and execute it to show it worked:
$items = array(); array_push($items, array()); array_push($items[0], "Hello"); array_push($items[0], "World"); $message = implode($items[0], " "); echo "That message says: ", $message, "\n";
and when run through the PHP interpreter we get this output:
$ felt docex.felt -cw | php That message says: Hello World
And finally for Node/javaScript, this being the modified code as javaScript has no "implode" function so we have to fake it:
(defvar items []) (@< items []) ; items now contains two arrays (apush (items 0) "Hello") (@< (items 0) "World") (defvar message (implode (@ items 0) " ")) (emit "That message says: " message "\n") (defun implode (x) (return (x.join)))
The generated code from the above FELT source is:
felt docex.felt -as nodejscoder var items = []; items.push([]); items[0].push("Hello"); items[0].push("World"); var message = implode(items[0], " "); console.log("That message says: " + message + "\\n"); function implode(x) {return x.join();}
And the generated output is:
$ felt docex.felt -as nodejscoder | node That message says: Hello,World
(aref var-name 0) ; read 0th element of var-name (aref bar :names i) ; read 'i'th element of bar / names (aref baz i j k) ; read the i-th / j-th / k-th element (@ var-name 0) ; read 0th element of var-name (@ bar :names i) ; read 'i'th element of bar / names (@ baz i j k) ; read the i-th / j-th / k-th element
The AREF
instruction lets you generate an offset into an array for reading or writing if used as the left-hand side of the =(ASSIGN) instruction. The first token after the @
is the array variable-name. The second and subsequent tokens are constructed into an access path according to the syntax rules of the back-end language.
The AREF
instruction or its short-form @
is how you access an array type variable in a FELT source text. It would be good to remember now that @
can be read as 'array' and that will help you to remember the other short-forms for the various push, pop, shift and unshift instructions as listed under the see also section.
It's worth stating them here so that you can familiarise yourself with them as they save a lot of typing and make for more concise code, and the code is certainly no more or less readable then 'perl' code. I am not saying if that's good or bad.
As stated, if you read the word 'array' when you see the @
symbol, then the position of the >
and <
symbols should make sense.
@<
, can then be read as 'push to the end of the array'. It is the same as the APUSH
instruction. I call this one 'quack' as well. ;)@>
, can then be read as 'pop from the end of the array'. It is the same as the APOP
instruction.<@
, can then be read as 'pop from the front of the array'. It is the same as the ASHIFT
instruction.>@
, can then be read as 'push to the front of the array'. It is the same as the AUNSHIFT
instruction.Some examples of using the AREF
instruction follow, first as read operations and then as write operations, first the FELT source:
(defvar data [ [1 2 3] {:five "Five!"}]) (defvar x (+ (aref data 0 1) (aref data 0 2))) (defvar y (concat "The Famous " (@ data 1 :five)))
The above declarations produce this when translated to PHP:
$data = array(array(1, 2, 3), array('five' => "Five!")); $x = ($data[0][1] + $data[0][2]); $y = ("The Famous " . $data[1]['five']);
And this when rendered as javaScript:
var data = [[1, 2, 3], {'five' : "Five!"}]; var x = (data[0][1] + data[0][2]); var y = ("The Famous " + data[1]['five']);
Adding some console output code and then running the javaScript through 'node' gives the following output:
[ [ 1, 2, 3 ], { five: 'Five!' } ] 5 The Famous Five!
The actual code that produce the above output was:
(defvar data [ [1 2 3] {:five "Five!"}]) (defvar x (+ (aref data 0 1) (aref data 0 2))) (defvar y (concat "The Famous " (@ data 1 :five))) (emit data) (emit x) (emit y)
The way to modify the contents of an array, be it a normal or association type, are exactly the same in FELT. The back-end coder will generate the appropriate code at translation time.
It helps if you have understood the =(ASSIGN) instruction as that is how you modify an array slot.
Here are some examples and the equivalent PHP code:
(defvar data [ [1 2 3] {:five "Five!"}]) (defvar x (+ (aref data 0 1) (aref data 0 2))) (defvar y (concat "The Famous " (@ data 1 :five))) (setq (aref data 0 0) 100) (setq (aref data 0 1) 200) (setq (aref data 0 2) 300) (= (@ data 1 :five) "Cinq!")
When run through the PHP coder we get this output:
$data = array(array(1, 2, 3), array('five' => "Five!")); $x = ($data[0][1] + $data[0][2]); $y = ("The Famous " . $data[1]['five']); $data[0][0] = 100; $data[0][1] = 200; $data[0][2] = 300; $data[1]['five'] = "Cinq!";
And the javaScript code:
var data = [[1, 2, 3], {'five' : "Five!"}]; var x = (data[0][1] + data[0][2]); var y = ("The Famous " + data[1]['five']); data[0][0] = 100; data[0][1] = 200; data[0][2] = 300; data[1]['five'] = "Cinq!";
(array) ; an empty array [] ; an empty array (defvar foo [1]) ; contains number 1, length 1 (defvar foo (array 1 :x "3")) ; contains number 1, 'x' and "3" (defvar foo [ (+ 1 2) (time)] ; contains (1+2) and the current time
The ARRAY
instruction has the short-form []
. You can place anything inside the square brackets that evaluates to something that will be acceptable to the selected back-end coder. Do not use commas. Repeat, do not use commas to separate the values. The items are white-space delimited. If the target language only allows homogeneous arrays then you will have to take that into consideration!
Creating an array is very simple in FELT, you just use the ARRAY
instruction or you can use the short-form, square brackets. The choice of square brackets means that if you know JSON, you already know how to code array declarations in FELT.
FELT places no restrictions on what you can put inside an array as it has no idea on what the back-end coder will allow until the process reaches that stage.
Here are some examples in PHP and javaScript so that you can see what you get. As stated, FELT itself places absolutely no restrictions on what you can place inside an array; the onus is on you as the developer to know what you are doing and how you are going to be rendering out the code in the future.
There are no portability notes to take into consideration when using this instruction. The declarations can be as simple or as complex as you want to make the. Let's convert the following into PHP and then javaScript:
(defvar foo (array (array 1 2) (array 3 4))) (defvar bar [ [1 2][3 4]]) (defvar baz [ (array 10 20) [ 42 (array "Hello") ] "World"])
The above declarations produce this when translated to PHP:
$foo = array(array(1, 2), array(3, 4)); $bar = array(array(1, 2), array(3, 4)); $baz = array(array(10, 20), array(42, array("Hello")), "World");
And this when rendered as javaScript:
var foo = [[1, 2], [3, 4]]; var bar = [[1, 2], [3, 4]]; var baz = [[10, 20], [42, ["Hello"]], "World"];
(as int var-name) ; convert var-name to an integer (as string expression) ; convert expression to a String (defvar x 3.14) (as int x) (as string x)
See also CAST
This performs type coercion for your chosen output language. This instruction is a short-form / synonym for the CAST instruction. Please refer to that instruction for full details.
(ashift aref-expression) (ashift foo) ; pop from the front of Array foo (<@ foo) ; same as above, short form
The ASHIFT
instruction has the short-form <@
. The greater-than sign facing away from the @
symbol is meant to visually indicate that something is being removed from the front of the array. The value that is removed can be assigned into a variable, used as a function argumen etc. `foo' can be any valid AREF/@ expression.
Here is the FELT source we are going to convert to PHP and javaScript:
(defvar items [ 10 20 "Thirty" [:a :b :c]]) (defvar v10 (ashift items)) (defvar v20 (<@ items)) (defvar v30 (<@ items)) (defvar a1 (ashift items)) ; the array (emit "Item count, zero == " (count items) "\n") (emit "'a' == " (<@ a1) "\n") (ashift a1) ; remove 'b' (emit "'c' == " (<@ a1) "\n")
Now let's render the code as PHP and execute it to show it worked:
$items = array(10, 20, "Thirty", array('a', 'b', 'c')); $a1 = array_shift($items); $v10 = array_shift($items); $v20 = array_shift($items); $v30 = array_shift($items); echo "Item count, zero == ", count($items), "\n"; echo "'a' == ", array_shift($a1), "\n"; array_shift($a1); echo "'c' == ", array_shift($a1), "\n";
and when run through the PHP interpreter we get this output:
felt docex.felt -cw | php Item count, zero == 0 'a' == a 'c' == c
And finally for Node/javaScript, this being the rendered code:
var items = [10, 20, "Thirty", ['a', 'b', 'c']]; var v10 = items.shift(); var v20 = items.shift(); var v30 = items.shift(); var a1 = items.shift(); console.log("Item count, zero == "+ count(items)+ "\n"); console.log("'a' == "+ a1.shift()+ "\n"); a1.shift(); console.log("'c' == "+ a1.shift()+ "\n"); function count(z) {return z.length;}
Note that we had to provide an implementation of count
for this code to work at run-time. When piped into "node" we get this output:
felt docex.felt -as nodejscoder | node Item count, zero == 0 'a' == a 'c' == c
We could have put the count
function in the original code example but I wanted to keep it concise. Please see the #F:WHEN instruction for complete details on how to provide different versions of functions for different target languages.
(aunshift aref-expression expr1 expr2 ... exprN) (aunshift foo expr) ; push to the front of foo (>@ foo expr) ; same as above, short form
The AUNSHIFT
instruction has the short-form >@
. The greater-than sign facing towards the @
symbol is meant to visually indicate that something is being added to the front of the array. The value(s) can be any valid back-end expressions.
Here is the FELT source we are going to convert to PHP and javaScript:
(defvar items []) (>@ items []) ; items now contains a nested array (aunshift (items 0) "World") (>@ (items 0) "Hello") (defvar message (implode (@ items 0) " ")) (emit "That message says: " message "\n")
Now let's render the code as PHP and execute it to show it worked:
$items = array(); array_unshift($items, array()); array_unshift($items[0], "World"); array_unshift($items[0], "Hello"); $message = implode($items[0], " "); echo "That message says: ", $message, "\n";
and when run through the PHP interpreter we get this output:
$ felt docex.felt -cw | php That message says: Hello World felt docex.felt -cw | php That message says: Hello World
And finally for Node/javaScript, this being the modified code as javaScript has no "implode" function so we have to fake it:
(defvar items []) (>@ items []) ; items now contains a nested array (aunshift (items 0) "World") (>@ (items 0) "Hello") (defvar message (implode (@ items 0) " ")) (emit "That message says: " message "\n") (defun implode (x) (return (x.join)))
The generated code from the above FELT source is:
felt docex.felt -as nodejscoder var items = []; items.unshift([]); items[0].unshift("World"); items[0].unshift("Hello"); var message = implode(items[0], " "); console.log("That message says: " + message + "\\n"); function implode(x) {return x.join();}
$ felt docex.felt -as nodejscoder | node That message says: Hello, World
(break) ; unconditional break, just do it. (break (< x 0)) ; break only if x is less than zero
The BREAK
instruction is used to terminate a loop construct early. That is its only purpose. It can also take an optional expression which will cause the break to only happen if that expression is 'true'. The exact meaning of 'true' is of course dependant upon the chosen back-end coder.
The concept of 'breaking' out of a loop is a very common one in many languages and so FELT supports that notion too. It also has CONTINUE in its repertoire which forces the next iteration rather than stops it completely.
The BREAK
instruction is very simple, you would normally use it within the context of a loop. The following example shows such a case:
(defvar stuff ["FELT" "is" "cool" "aid.. not!"]) (foreach (stuff item) (if (== "cool" item) (break)) // loop body... )
Looking at PHP rendering of the above code we get this:
$stuff = array("FELT", "is", "cool", "aid.. not!"); foreach($stuff as $item) { {if (("cool") == ($item)) {break;}} }
And javaScript produces this code:
var stuff = ["FELT", "is", "cool", "aid.. not!"]; for(item in stuff) { if (("cool") == (item)) { break; } }
Pretty much most of the time the BREAK
instruction is only required to be executed when some condition is met within the loop and so FELT makes it a little easier by allowing you to combine the conditional test and the break as one instruction.
Here is the above example, this time re-coded to show how to place a conditional test within it. FELT allows any valid expression here, but again the actual target language will be the final arbiter if what compiles / runs or doesn't!
Here it is again:
(defvar stuff ["FELT" "is" "cool" "aid.. not!"]) (foreach (stuff item) (break (== "cool" item)) ;; loop body... )
Looking at PHP rendering of the above code we get this:
$stuff = array("FELT", "is", "cool", "aid.. not!"); foreach($stuff as $item) { {if (("cool") == ($item)) break;} }
It looks a little different but that's because this time the back-end PHPCoder class took the liberty of writing a little bit of PHP code for itself to get the same effect.
And again, the back-end JSCoder produces this code:
var stuff = ["FELT", "is", "cool", "aid.. not!"]; for(item in stuff) { if (("cool") == (item)) break; }
Again, a slightly different output but functionally equivalent nonetheless.
(cast int var-name) ; convert var-name to an integer (cast string expression) ; convert expression to a String (defvar x 3.14) (cast int x) (cast string x)
See also AS
This performs type coercion for your chosen output language. This instruction represents the act of "casting" if you come from C/C++/C# backgrounds, otherwise described as "type coercion" in other languages. It can result in loss of accuracy or a total wipeout according to your target language.
The instruction is composed of the word at
or cast
followed a string that makes sense for the target platform. Yes, this is shaky ground because apart from a few very common types like int
or String for example, you stand a very good chance of not being able to generate language neutral code when type casting.
Type casting can be fairly dangerous unless you know exactly what context your code is operating withing anyway so FELT is not too worried. If you absolutely must do these sorts of things in your program, and sometimes you just have to, then FELT would prefer it if you wrapped it in a specific type-wrapping function and then wrapped that with an #F:WHEN instruction, thus keeping everything cleanly separated.
With that in mind, the following examples are shown in PHP only just so that you can see how it works.
(defvar f 3.14) (emit "Started with: " f "\n") (emit " as in int: " (as int f) "\n") (emit " as an array: " (print-r (cast Array f) #t) "\n")
And the rendered output is:
$f = 3.14; echo "Started with: ", $f, "\n"; echo " as in int: ", (int)$f, "\n"; echo " as an array: ", print_r((Array)$f, TRUE), "\n";
And the output from piping through PHP command line is:
Started with: 3.14 as in int: 3 as an array: Array ( [0] => 3.14 )
Use with extreme caution!
(class class-name | (class-name superclass-name) (:implements IName-1 IName-2 ...IName-N) (mvar ...) ; define member data (cvar ...) ; define class data (const ...) ; define constants (defun ...) ; define member / class functions )
See also DEFCLASS MVAR CVAR CONST THIS MY MCALL / -> CLASS-CALL / ::
This is a synonym for DEFCLASS. Please refer to that page for full documentation on classes.
(class-call Classname MethodName arg1 arg2 ...argN) (defvar db (class-call DBMS Open username password dbname)) (:: DBMS Close db) ;; non-portable short-forms of the above (defvar db (DBMS::Open username password dbname)) (DBMS::Close db)
See also MCALL / -> CLASS
This is how FELT lets you call static methods on a class instance. It is pretty much the same as the MCALL / -> instruction except that it assumes that the receiver is a class not an instance of a class. The MethodName can be any valid expression that generates valid code.
If you only ever intend to be generating a specific back-end then there is a short-form avilable as shown above, which can feel more natural for PHP applications. This short form allows you to put the class name and the method name together, concatenated by the ::
operator but do not put any whitespace either side of ::
or it will fail to parse.
The above sample code when rendered as PHP looks like this:
$db = DBMS::Open($username, $password, $dbname); DBMS::Close($db); $db = DBMS::Open($username, $password, $dbname); DBMS::Close($db);
JavaScript is a prototype based language and as such doesn't really have what Java or C++ might call a 'class' in the common sense of the word. FELT uses a particular way of creating 'classes' when the JSCoder or Nodejscoder back-ends are being used. For full details please refer to the CLASS documentation.
(concat expr1 expr2 ... exprN) ; string concatenation of terms (concat "Hello, " "World!") (. "Use the force," "or FELT!") ; shortform
See also EMIT
The string concatention instruction, CONCAT
has the short-form .
, which is the dor, or period, character. FELT does not impose any restrictions on the eventually types of the various expressions that you ask to be strung together. They must make syntactical sense to the back-end coder, that is the only requirement.
Always remember that FELT began as an alternative way for writing PHP code which explains why the concatentation instruction short-form is a .
character. It will be translated into whatever is required by the chosen back-end coder. So for example, in javaScript it will become the +
symbol.
(emit (concat "Hello, " "World!\n")) (emit (+ 1 2 3) " Four " :five "\n")
The above produces this PHP code,
echo ("Hello, " . "World!\n"); echo (1 + 2 + 3), " Four ", 'five', "\n";
Running this through the PHP interpreter we get this output:
$ felt docex.felt -cw | php Hello, World! 6 Four five
For javaScript code we get the following rendered code:
console.log(("Hello, " + "World!\n")); console.log((1 + 2 + 3)+ " Four "+ 'five'+ "\n");
and the output from running it thorugh Node.js is:
$ felt docex.felt -as nodejscoder | node Hello, World! 6 Four five
(const (const-name (value-expression)) (const (PI 3.14159) (E 2.718)) (const (SIGNON "Hello World"))
This allows you to instruct the coder to declare a constant. Can be used inside or outside of a CLASS declaration. The (const-name value-expression)
can be any valid FELT expression but must translate to valid code for the chosen target language. 'CONSTC' allows back-end coders to allow for instance and class constants should such a thing be available. Convention dictates names are in uppercase but you do not have to follow convention. Ever ;)
When used outside of a class-definition the CONST
instruction instructs the back-end to generate an "immutable" value in the generated source code. For PHP this means using the const
keyword as opposed to DEFINE
which uses the PHP keyword of the same name.
The examples given in the head render to PHP like so:
const PI = 3.14159; const E = 2.718; const SIGNON = "Hello World";
JavaScript doesn't yet have the same concept and so FELT makes a best guess and generates a bunch of var
-s like so:
var PI = 3.14159; var E = 2.718; var SIGNON = "Hello World";
When the CONST
instruction is used within the context of a currently active CLASS
declaration the behaviour adapts itself as required by the selected back-end coder.
The modified example source now has a class:
(class ConstantDemo
(const (PI 3.14159) (E 2.718))
(const (SIGNON "Hello World")))
For PHP that means pretty much nothing as the syntax is the same, just within the scope of a class:
class ConstantDef { const PI = 3.14159; const E = 2.718; const SIGNON = "Hello World"; }
JavaScript doesn't really have "classes", being a prototype based language, but despite that FELT makes the "most effective" decision and the back-end coder will generate this code.
function ConstantDef () { this.PI = 3.14159; this.E = 2.718; this.SIGNON = "Hello World"; }
Note that this time the constants have been prefixed with this
to make them available to the instance.
(continue) (continue 2) ; escape level, coder specific!
The CONTINUE
instruction is used to terminate the 'current' iteration of a loop and then immediately start the next one. Exactly how is an implementation detail of the chosen back-end coder. It is similar to the BREAK
instruction but does not terminate the iteration but instead forces the next iteration to begin.
The concept of 'continuing' out of a loop is a very common one in many languages and so FELT supports that notion too. It also has BREAK in its repertoire which terminates the iteration instead of making it jump to the next one in sequence.
Unless you are writing deeply nested loops (never good but sometimes you just have to!) then there is an optional second paramter to supply to the CONTINUE
instruction that lets you specify the 'escape level'. The example shown on the PHP/continue page is as good as any so let's show the FELT equivalent example first:
(while (= (list key value) (each arr)) (if (not (% key 2)) (continue) ;; skip odd members ;; do something here (do-something-odd value)))
When rendered as PHP code we get the following code:
while(list($key, $value) = each($arr)) { if ((!(($key % 2)))) { continue; } else { do_something_odd($value); } }
And here is the code from the PHP site for comparison.
while(list($key, $value) = each($arr)) { if (!($key % 2)) { // skip odd members continue; } do_something_odd($value); }
(crash expression (catch (Etype-1 handler) (Etype-2 handler) (Etype-N handler)) (finally expression))
This is a synonym for TRY for those days when it just feels better to crash than to try! Please read that page for complete information.
(cvar var-name) ; public class variable is the default (cvar+ var-name) ; public class variable (cvar# var-name) ; protected class variable (cvar- (var-name default-value)) ; private class variable (cvar var-name-1 ; multiple CVAR-s will all have the var-name-2 ; same level of access control (var-name-3 "default"))
You should only use CVAR
within a current CLASS
declaration as it makes no sense to use it anywhere else. You can define a single variable with or without a default value or you can define many variables at once, again with or without a default value.
FELT supports the notion of class variables that can be "public", "private" or "protected" and in order to express those intentions you can choose from the following:
Instruction | Visibility |
---|---|
CVAR |
public |
CVAR+ |
public |
CVAR- |
private |
CVAR# |
protected |
For full details on how to compose a "class", read the CLASS page as that goes into some detail on how to declares classes in general. This page is more concerned with the actual means of declaring class variables.
Sometimes referred to as "static" variables as well in some quarters, these variables allow for a single instance of something to be maintained regardless of how many instances of the class are minted at run-time... why is that useful, in a word, a single word in fact: Singletons!
Despite the current trend of "singleton pattern considered harmful" it will be a long long time before this very useful pattern goes away and FELT therefore supports it and any other uses you may want for class / static variables.
To define a class variable you just name the variable within the CVAR
instruction like so:
(class Foo (cvar bar))
If you want to supply a default value then you use the same syntax as for when you write a function signature with DEFUN/FUNCTION, by placing the variable name and the default value inside parentheses like so:
(class Foo (cvar (bar 100)))
Instead of having to laboriously type CVAR
for every single class variable, you can in fact supply multiple of them at once like this:
and the FELT process will
(decf var-name [ delta ] ) (decf x) ; destructivley decrements x by 1 (decf x 2) ; by 2 (-- x (get-mode)) ; by whatever (get-mode) returned
This will destructivley modify the contents of the target variable by the amount specified, with the default being one. The optional delta value can be any valid expression for the target language and it will be applied at run-time.
Many languages support the notion of "post-decrement", "pre-decrement", "post-increment" and "pre-increment". I have taken the view, at least for now and the foreseable future that these are, whilst "useful", nothing more than syntactic sugar, and as Alan Perlis has been quoted as saying:
Syntactic sugar causes cancer of the semicolon.
Who is Alan Perlis, and why do I quote him? Go find out...
This means that whilst post-increment and post-decrement are supported, there is currently no way to "inline" the pre-increment and pre-decrement operators found in other languages. That may grate with some but for now FELT stands fast on that mainly because Lisp doesn't offer it either and nobody found a reason to complain loud enough to get it changed!
(decr var-name [ delta ] ) (decr x) ; non-destructivley decrements x by 1 for evaluation (decr x 2) ; by 2 (decr x (get-mode)) ; by whatever (get-mode) returned
This will destructivley modify the contents of the target variable by the amount specified, with the default being one. The optional delta value can be any valid expression for the target language and it will be applied at run-time.
What this means is that the targeted variable has the supplied delta
expression applied to it but only for the duration of the expression. Some examples will make this clear; the idiom is very common and you will reocgnise it immediately!
(defvar x 42) (emit "x minus ten: " (decr x 10) "\n") (emit "x is still: " x "\n")
This generate the following PHP:
$x = 42; echo "x minus ten: ", ($x-10), "\n"; echo "x is still: ", $x, "\n";
When executed the above produces:
$ felt docex.felt -cw | php x minus ten: 32 x is still: 42
And the javaScript as well (piped through Nodejscoder):
var x = 42; console.log("x minus ten: "+ (x-10)+ "\n"); console.log("x is still: "+ x+ "\n");
Which when piped through the Node.js coder gives us this output:
$ felt docex.felt -as nodejscoder | node x minus ten: 32 x is still: 42
(defclass class-name | (class-name superclass-name) (:implements IName-1 IName-2 ...IName-N) (mvar ...) ; define member data (cvar ...) ; define class data (const ...) ; define constants (defun ...) ; define member / class functions )
See also CLASS MVAR CVAR CONST THIS MY MCALL / -> CLASS-CALL / ::
This is the FELT instruction that allows you to define a 'class' in the object-oriented paradigm. The CLASS
instruction is a synonym for this one and can be used interchangeably and is done for easier migration towards Lisp in the future.
All of the various parts of defining classes for PHP have been detailed in this document, Coding for PHP. Please refer to that page for instructions on how to code classes in PHP.
JavaScript doesn't really have classes although it is possible to produce something that walks like a class, talks like a class and smells like a class. Please refer to Coding for JavaScript / Node.js
(define const-name const-value) (define PI 3.14159) (define* MyThing "Cool")
This is used to define a constant into the output code. The var-name and the expression must be valid to the back-end coder. Using the define*
version means that the back-end coder is to make the definition case-sensitive.
Running the above examples through the PHP back-end:
define('PI', 3.14159); define('MyThing', "Cool", TRUE);
You can see that the define*
for the last one has made the PHP coder use the TRUE value in the define command to cause the name to be case-sensitive at run-time.
And the javaScript back-end:
var PI = 3.14159; var MyThing = "Cool";
javaScript doesn't really have the notion of a formal constant and so the usual practice is to use all uppercase for the names and define that as file level scoped variables so that they can be used everywhere.
(defstatic var-name [initial-value-expression]) (defstatic cache (array)) ; create a cached array (defstatic count 0) ; initialise a function counter
See also DEFVAR = / ASSIGN
This instruction asks that the named variable be of a type that maintains its state across invocations of a file unit or function call. **An optional initial value* may be supplied. If not initial value is given then the default behaviour is unspecified by FELT; it is the behaviour of the chosen target back-end language that will dictate what happens at execution time.
Some but not all languages allow the concept of "static" variables such that within a particular scope, be it file or functional, that variable maintains whatever state state it has across invocations of a function.
This allows functions to be able to implement simple cache or memoization behaviours as the "static" variable does not get re-set each time the function is called.
The following FELT code shows how you might use DEFSTATIC
variable within the scope of a function to maintain a list of results: this is a very simple memoization scheme and, at the expense of memory it will provide faster response times:
(defun do-query(query-string) (defstatic cache []) (defvar hash (md5 query-string)) (if (isset (@ cache hash)) (return (@ cache hash))) ;; first time here, do it... (= (@ cache hash) (execute-query query-string)) (return (@ cache hash)))
The rendered PHP code for the above example is:
function do_query($query_string) { static $cache = array(); $hash = md5($query_string); if (isset($cache[$hash])) { return $cache[$hash]; } $cache[$hash] = execute_query($query_string); return $cache[$hash]; }
JavaScript does not have this feature and just defines a normal locally scoped variable instead.
function do_query($query_string) { static $cache = array(); $hash = md5($query_string); if (isset($cache[$hash])) { return $cache[$hash]; } $cache[$hash] = execute_query($query_string); return $cache[$hash]; }
(defun func-name ( [ p1 p2 .. pN ] ) body) (function func-name ( [ p1 p2 .. pN ] ) body) (defun+ func-name ( [ (p1 "Hello") ] )) ; acl: public, p1 defaults to a string value (defun# func-name ( [ (p1 :ref) ] )) ; acl: protected, p1 is a reference variable (defun- func-name ( [ (p1 ()) ] )) ; acl: private p1 defaults to 'NULL' (defun* ( [ p1 p2 ] ) body ) ; anonymous function with two parameters
See also DEFUNC CLASS MVAR CVAR Coding for PHP Coding for javaScript
This instruction is one of the most widely used in FELT. A DEFUN
instruction must be followed by the name of the function (unless DEFUN*
is used) and then the parameters enclosed in ()
. If there are no parameters then the parentheses remain empty but they must be present. A parameter can be a simple name, thing
, a name and a default expression within parentheses, (thing "Hello")
and finally it can be a mutable reference parameter, again within parentheses: (thing :ref)
. The form ()
will be translated into the value for NULL/nil/void etc. for the selected output language. Understand DEFUN
and you also understand DEFUNC
, FUNCTION
and FUNCTIONC
.
If you are defining a class method / function then this instruction, DEFUN
is the same as defining a method that has public access. FELT supports the notion of methods that can be "public", "private" or "protected" and in order to express those intentions you can choose from the following:
Instruction | Visibility |
---|---|
DEFUN |
public |
DEFUN+ |
public |
DEFUN- |
private |
DEFUN# |
protected |
This pattern of characters at the end is also the same for when you want to declare a class method or "static method", for which you should refer to the DEFUNC page for more information.
There is a variation on the DEFUN
form that allows you to declare a function that has no name, just a signature and a body. This is used a lot for the javaScript (JSCoder) back-end and also in the PHP back-end. FELT is clever enough to see if your version of PHP supports anonymous functions (PHP5.3+) or not and acts accordingly; if you have them it uses them, if you don't it will generate a unique name for that function and at the call site the name of that function is emitted and then in the "post-render" phase of translation the actual functions will be rendered out. For most of the time this works just fine and causes no troubles, and is completely transparent to you as a developer.
You can create closures using the DEFUN*
form but FELT itself has absolutely no bearing at all on whether or not it will work as success ultimately depends completely and utterly on the chosen output language.
All I can tell you is that for PHP, for example, if you follow "the rules" relating to closures and anonymous functions with respect to scope (see PHP -- Anonymous functions then you should be fine, the code that comprises this site makes use of them in plenty of places and it works as you would expect it to.
FELT allows most types of function signatures to be created, including default parameters and reference parameters. Not all languages support these features so again, be careful about what you are doing. If you try to use a feature that is not supported in a particular language then the core classes (those that come with FELT out of the box) will generate a warning message in the rendered code. Anybody else that writes a back-end coder should also follow the same principles.
On the surface of it FELT would appear to fall into the category of "loosely typed" as defining parameters doesn't include specifying a type. FELT was initially written to allow PHP code to be created using a LISP like syntax but it kind of grew from there when I realised that, given the amount of effort that I was putting into it, I might as well tackle all loosely-typed languages at the same time and even then, in the back of my mind I was hatching a plan to cope with typed languages as well. That will come later, for now let's continue in the loosely typed world of languages...
Let's define a function that takes two parameters and then returns the result of adding them together. Remember that this doesn't actually mean the function will work at run-time because if the parameters are of different types and the language doesn't allow the addition of such things then get the mop and bucket ready. PHP however does allow such things, as do some other languages. Here's the function definition and the PHP:
(defun add-em-up (p1 p2) (return (+ p1 p2)))
which gives this as PHP output:
function add_em_up($p1, $p2) { return ($p1 + $p2); }
Nothing too exciting there which is what we want; good clean normal run-of-the-mill parameters. And remember that FELT allows you to name parameters using almost any printable characters as it converts them into a valid form for the chosen back-end language, for example, lets re-do the above with some funkier names all round:
(defun vals->total (val#1 val#2) (return (+ val#1 val#2)))
and the javaScript code produced is:
function vals__gt_total($val_hash_1, $val_hash_2) { return ($val_hash_1 + $val_hash_2); }
Looks kind of interesting but it is still valid PHP nonetheless and will run just fine. The point is that FELT allows you greater expressivity of variable and function naming which hopefully means more readable intentions. I have said before that FELT doesn't care for good looking output code as I don't want you to look at anything except FELT code. If it works, it works. Your code repositories will be full of FELT code which is of course beautiful to look at being essentially LISP to look at. ;)
FELT allows you to define parameters that can have what is commonly referred to as a "default" value, sometimes these are referred to as "optional" parameters. Again not all languages support this and the back-end coders will adapt accordingly.
Here is the initial example function but this time the second parameter has been modified so that "by default" it will add one to the first parameter, the output this time is again PHP for reference with the original but javaScript is also shown as a comparison because javaScript does not have this facility. If you think you will be needing this feature then the best way is to design your functions to take a single array that contains keys and values and go from there.
(defun vals->total (val#1 (val#2 1)) (return (+ val#1 val#2)))
and the PHP code from the above is:
function vals__gt_total($val_hash_1, $val_hash_2 = 1) { return ($val_hash_1 + $val_hash_2); }
And for a quick reference you can see that the javaScript output just doesn't do anything with the default and assumes normal operation. This is one of the things to keep in mind when writing code that you think you may want to re-use on a different target!
function vals__gt_total(val_hash_1, val_hash_2) { return (val_hash_1 + val_hash_2); }
which translates into this javaScript code:
function vals__gt_total(val_hash_1, val_hash_2) { return (val_hash_1 + val_hash_2); }
FELT supports the notion of mutable parameters, often referred to as "reference" parameters by many languages and sometimes as "out" parameters in other systems. Essentially they are alterable by the function being executed.
To declare a reference parameter in felt, all you do is place the keyword :ref
after the name of the variable and enclose them in parentheses. Let's re-write the example so that this time we return nothing but write the answer back into the reference parameter passed in that will the total:
(defun add-em-up (p1 p2 (total :ref)) (= total (+ p1 p2)))
which renders into this PHP code;
function add_em_up($p1, $p2, &$total) { $total = ($p1 + $p2); }
This has been one of the more interesting and challenging parts of creating FELT and whilst it may look a little ungly in places, it does work and currently the Java back-end is functional but not yet ready for general release until I am happy that it is good enough to do Android development.
Simply, by taking advantage of the fact that FELT allows most printable characters to be used in a variable name and a function name, those back-end coders that produce typed languages will look for the "reverse solidus" or, more commonly "/" (foward slash) and infer from the characters after it a type for the the function or variable.
The Java coder has a built in set of types that it will recognise. They are listed in full in the Java Coder Documentation pages but for clarity here is a short example of a class in FELT that produces a Java class that when run produces a familiar greeting:
(class Greeting (defunc+ main/v (args/as) (emit "Hello World")))
And the following console session shows how that translates into Java code and then compiles it and runs it:
$ felt greeting.felt -as javacoder class Greeting { public static void main(String[] args) {System.out.println("Hello, World!");} } $ felt docex.felt -as javacoder -o Greeting.java $ javac Greeting.java $ java Greeting Hello, World!
So, FELT can and does support typed languages but it is a little early for me to say exactly how this will pan out. I am working on this and I think that what I have in mind will be good enough, which is usually all that is required to make things work. It involves each "typed" coder having a built in set of types plus the ability for you as the FELT developer to "plug-in" your own set of abbreviations and by following some simples rules, that should be all you need to manage typed output. For now, using the Java coder is definitely classed as experimental as it is not offically ready for release but I have made it available nonetheless.
(class TestClass (defunc func-name ( [ p1 p2 .. pN ] ) body) (function func-name ( [ p1 p2 .. pN ] ) body) (defunc+ func-name ( [ (p1 "Hello") ] )) ; acl: public, p1 defaults to a string value (defunc# func-name ( [ (p1 :ref) ] )) ; acl: protected, p1 is a reference variable (defunc- func-name ( [ (p1 ()) ] )) ; acl: private p1 defaults to 'NULL' );class
See also DEFUN CLASS MVAR CVAR Coding for PHP Coding for javaScript
This instructions is used to define "class" or "static" methods in a class for OO based target languages that support that feature. You should read and understand the DEFUN instruction as these are very very similar except that this instruction should only be used within the lexical scope of a CLASS definition or it will more than likely cause compilation errors. FELT let's you try anything, but your final compiler / interpreter is always the final arbiter of what's acceptable. Please refer to the CLASS page for complete instructions on using classes.
As with DEFUN
is the same as defining a method that has public access. FELT supports the notion of methods that can be "public", "private" or "protected" and in order to express those intentions you can choose from the following:
Instruction | Visibility |
---|---|
DEFUNC |
public |
DEFUNC+ |
public |
DEFUNC- |
private |
DEFUNC# |
protected |
This pattern of characters at the end is also the same for when you want to declare a class method or "static method", for which you should refer to the DEFUNC page for more information.
Exactly the same rules apply as for defun/function
so please refer to the help for DEFUN for complete information on composing a function signature.
Here we show a simple FELT class definition and the (tidied) PHP output that results from it so you can see methods and class methods in practice. This is not a complete example. please refer to the help for CLASS for more complete details.
(class Example => class Example { (defun def-pubfun ()) function def_pubfun() {} (defun+ pub-fun ()) public function pub_fun() {} (defun# prot-fun ()) protected function prot_fun(){} (defun- priv-fun ()) private function priv_fun() {} );class }
(defvar var-name 100) ; simple assign (defvar var-name (+ (a-func 1 2) 12)) ; complicated assignment (:= var-name "Short-form of defvar") ; simple string assign (:= var-name (+ 10 32))
Define a variable for subsequent use. Some languages insist that you formally declare your variables before you can use them. To do that, use this instruction the first time you reference a new variable. PHP doesn't require this. javaScript does.
Most scripting languages are pretty lax when it comes to having you formally declare a new variable before you can use it and most if not all compiled languages pretty much insist that you must declare your variables before you can use them as the compiler needs to know how much space to allocate on the local stack frame for them.
In order that you produce portable code you should always declare new variables within a function by using DEFVAR
or the short-hand form :=
as this indicates to the back-end coder that it should produce a declaration and not just a simple assignment.
Let's define a simple function that declares two local variables, one using DEFVAR
and one that just assigns a value to a variable. Then we can see the PHP code and examine the difference:
(defun defvar-example1 () (defvar good-var 100) (= bad-var "Naughty!"))
and the PHP rendered out is:
function defvar_example1() { $good_var = 100; $bad_var = "Naughty!"; }
Notice the difference? There isn't one! That's because as we have already said, PHP doesn't require that you explicitly state the names of new variables, it just creates them as and when you want to use them.
So what happens if we run that code through the javaScript coder instead, will it work as expected?
(defun defvar-example1 () (defvar good-var 100) (= bad-var "Naughty!"))
with the JavaScript being output like so:
function defvar_example1() { var good_var = 100; bad_var = "Naughty!"; }
This time you can visibly see the difference; the DEFVAR
instruction brought the good_var
variable into local scope by using the javaScript var
declaration syntax, this is a good thing. It means that we know that irrespective of what passed before, that this variable, for the lifetime of this function is ours to do with as we please! Some people call this "unintended variable capture", I call it sloppy.
On the other hand, bad_var
is possibly a disaster waiting to happen... if there is currently no variable called bad_var
then we should be OK, however if there is a variable at file level scope then guess what, we just accidentally clobbered it and now we have a bug to sort out when things unexpected begin to happen.
FELT does not know what your target language is when you write it, and therefore the onus is on you to follow the best-practice rules so once again I say, if you are absolutely sure that you are just using FELT with a single target in mind, don't break the rules because one day you may decide that your PHP only project might be good running under Node.js instead and you will find it riddled with lots of irritating bugs and things because of scoping rules being different.
Some languages do not insist that you declare your variables before you use them, in which case you can safely use the =
instruction to assign an initial value to the variable in the knowledge that it will be brought into existence at that point in the code.
However, the real "Tao of FELT" if there is one is writing code that can be minted out for as wide an audience of back-end coders as possible; that maximises your efforts.
There is another instruction, DEFVAR that is the 'correct' way to define a variable before it is used. Therefore it is highly recommended that all variables are initially brought into scope using it rather than =
.
Here is how you should do it:
(defun simple-total (a b) (defvar total 0) (setq total (+ a b)) (return total))
which would be rendered into PHP as follows:
function simple_total($a, $b) { $total = 0; $total = ($a + $b); return $total; }
And in javaScript:
function simple_total(a, b) { var total = 0; total = (a + b); return total; }
Here you can see that because we used DEFVAR we have the var
keyword in place to introduce the variable, now if we changed the original FELT example so that we had used the assignment operator instead, we will see that although the PHP code still works,
(defun simple-total (a b) (= total 0) (setq total (+ a b)) (return total))
which again, rendered into PHP looks like this:
function simple_total($a, $b) { $total = 0; $total = ($a + $b); return $total; }
And in JavaScript:
function simple_total(a, b) { total = 0; total = (a + b); return total; }
The JavaScript code is now "dangerous" in that there is a possible bug waiting to be discovered because although the code is not in error, at run time, if there is no "total" variable at file scope, as there isn't one at function scope, then something bad is going to happen.
Always use DEFVAR for the first use of a variable. Stay portable. If it feels like too much typing then remember you can use the :=
short form instead.
(emit expr1 expr2 ... exprN) ; emit many expressions (emit "Hello World!") ; emit a single string (o> "Hello again") ; and another, short form this time
See also CONCAT/.
This sends data to "the output stream". The exact meaning of "output stream" varies according to the chosen back-end coder but it will be the most meaningful and useful default for that back-end. You can place as many expressions as you want within it and they are all transmitted in sequence.
The EMIT
instruction has been designed such that for each of the supported back-end coders, it is the "most sensible" place for the output to go.
For PHP, the EMIT
instruction causes its arguments to be sent to either the console if it was run as a CLI process, "stdout" if run as a CGI process and finally, it will be sent to the requesting agent if being run from a web-page / web-server request, as for PHP that makes the most sense.
For JavaScript, the EMIT
instruction will be turned into a document.write()
call as within the context of javaScript, that would appear to be the most sensible thing
For the Node.js back end the EMIT
instruction is translated into a console.log()
call.
In all cases, each of the arguments is handled by the back-end coder in the most sensible way, usually this means the CONCAT/.
operator is used to glue them together into a single entity first. Please refer to the CONCAT/. instruction for further details.
(for initialisers termination-cond step-updates loop-body) where initialisers := ((init-1) (init-2) .. (init-N)) termination-cond := expression step-updates := ((upd-1) (upd-2) .. (upd-N)) loop-body := [ expression, ] (for ((= i 1) (= total 0)) (<= i 5) ((incf i) (incf total i)) ;; body expressions here... )
A value counting looping construct. The initialisers are grouped inside ()
, don't forget that.. The termination is a normal FELT expression. The step updates are also grouped within ()
. The loop body is zero or more FELT expressions.
The for
construct is common in nearly every language out there and FELT provides a way to do this that allows for cross-language deployment. It may be a little uglier and slightly more verbose than 'language X' but remember what FELT does and it shouldn't really be a worry.
The first thing to note that is all initialisation expressions are grouped within a single (). That is important as the outermost parenthetical grouping is what FELT assume to contain all of the intialiasation expressions for the entire loop. You will soon know if you get it wrong.
You can place an valid FELT expression into the initialisation expressions, once again the only proviso is that the chosen back-end coder can sucessfully turn it into valid code for its target language.
The termination condition follows the initial expressions. This is the same structural format as most other languages and so it should be easy to remember. The termination condition must be a valid FELT expression; as simple or as complex as you like. Again the only constraint on what you enter is that it must translate into back-end code to be of service.
As for the initialisation expressions, these must also be grouped within ()
.
Let's just code a really simple and primitive (but effective nonetheless) timing loop that just wastes a whole bunch of time. Sometimes this is useful to slow things down between steps of a process so that you have time to click windows on the screen, examine log files and many other uses.
(for ((i=0)) (< i 10000000) ((incf i)))
That little one liner will burn a lot of CPU time and not really do much but hey, maybe that's what you wanted to do. Anyway, here is what the above looks like as PHP:
for($i = 0; ($i) < (10000000); ($i++)) {}
And as JavaScript:
for(i = 0; (i) < (10000000); (i++)) {}
OK... let's write a function that will be given an array of integers, and our job is to return the largest integer and also the total and average of those values. How would we do that using a FOR
instruction with FELT?
It sounds straight-forward but remember that PHP and JavsScript have different ways of determining the length of an array. The first to tackle then would be some code that knows when either language is being coded for and hands back the appropriate value. Let's do that first and then we can write the common code and then run it with some data.
Here is the solution to getting the length of an array, note that we are using the Node.js back-end so we can use the console output later on:
(defun cruncher (numbers) (#f:when PHPCoder (defvar nlen (count numbers))) (#f:when NodejsCoder (defvar nlen numbers.length)) )
What this says is... define a function called 'cruncher' that takes a single argument called 'numbers'. Next it says that for PHP generation we should call a function, count
and pass it the numbers value and assign the return value to local variable 'nlen'. For JavaScript generation it just copies the value of the 'length' field of the array into 'nlen' instead.
Just to prove that the above works, let's examine the PHP and JavaScript output and then move on to the rest of the code we have discussed. Here's the PHP:
function cruncher($numbers) {
$nlen = count($numbers);
}
And the javaScript:
function cruncher(numbers) { var nlen = numbers.length; }
So you can see that we so far have language-portable way to determine the length of that array. (Yes, we could use ALEN/@# but I wanted to make this a more FELT-heavy example just so you can see how it handles cross-language situations!)
(foreach (array-name element) ...body...) ; enumerate keys in array (foreach (array-name key value) ...body...) ; enumerate keys with values in array (foreach (array-name key (value :ref) ...body...) ; enumerate keys with mutable values
This instruction is for iterating arrays and objects. The first form enumerates the array and binds each successive element to element
. The second form provides bindings to the key and the value. The third form provides a binding to the key and a mutable binding to the value if the back-end coder supports it.
The first form of the FOREACH
instruction is used for simple enumeration of an array or object when you only want the 'values' to work with. Here's a simple example that shows how to iterate the values in an ALIST
, an associative array:
(defvar things {:name "eric" :age 46 :occupation "not sure yet"}) (foreach (things v) (emit "property: " v "\n"))
The above translates to PHP as:
$things = array('name' => "eric", 'age' => 46, 'occupation' => "not sure yet"); foreach($things as $v) { {echo $v;}}
and when piped through the PHP run-time we get the following output:
$ felt docex.felt -cw | php property: eric property: 46 property: not sure yet
and JavaScript (Node.js) as:
felt docex.felt -as nodejscoder var things = {'name' : "eric", 'age' : 46, 'occupation' : "not sure yet"}; for(var v in things) {console.log("property: "+ v+ "\n");}
And this time the output from running through a Node.js session is:
$ felt docex.felt -as nodejscoder | node property: name property: age property: occupation
Let's improve on the previous example by not only displaying the value but the name of the property (key) as well. In PHP this is easily supported using the usual "$k => $v" syntax but that's not possible in JavaScript... is it? That's one of the nice things about FELT... it does these things for you! Let's see how it works...
This is the same code as above but this time we are printing out the value of the key as well, so here is the modified FELT source code:
(defvar things {:name "eric" :age 46 :occupation "not sure yet"}) (foreach (things k v) (emit "key: " k ", property: " v "\n"))
And here is the new PHP output code:
$things = array('name' => "eric", 'age' => 46, 'occupation' => "not sure yet"); foreach($things as $k => $v) { echo "key: ", $k, ", property: ", $v, "\n"; }
And the output we get from running it.
$ felt docex.felt -cw | php key: name, property: eric key: age, property: 46 key: occupation, property: not sure yet
So let's render it as JavaScript now and see what happens, after all there is no such way of doing this as a single instruction in JavaScript, what will FELT do?
var things = {'name' : "eric", 'age' : 46, 'occupation' : "not sure yet"}; for(var k in things) { var v = things[k]; console.log("key: "+ k+ ", property: "+ v+ "\n"); }
So... the back-end coder has kindly provided some 'free code' for us, a local variable binding inside the for loop that takes the current property being iterated over and binds the key into the variable that was named in the source.
Running the above code gets us this output:
$ felt docex.felt -as nodejscoder | node key: name, property: eric key: age, property: 46 key: occupation, property: not sure yet
So you can see that we still have a functionally equivalent program, which is what FELT is all about after all.
This form is almost the same as the last but this time the 'value' binding is expressed in the same way as a function 'reference' parameter is defined, (v :ref)
... for those languages that support this feature, such as PHP, it will instruct the back-end coder that the loop will be altering the actual value within the scope of the loop. This is of course dangerous at the best of times but FELT gives you that freedom.
The sample code so far but modified to use a reference looks like this:
(defvar things {:name "eric" :age 46 :occupation "not sure yet"}) (foreach (things (v :ref)) (emit "property: " v "\n"))
The output for PHP:
$things = array('name' => "eric", 'age' => 46, 'occupation' => "not sure yet"); foreach($things as $k => &$v) { echo "key: ", $k, ", property: ", $v, "\n"; }
The output for JavaScript:
var things = {'name' : "eric", 'age' : 46, 'occupation' : "not sure yet"}; for(var k in things) { var v = things[k]; console.log("key: "+ k+ ", property: "+ v+ "\n"); }
This time FELT cannot do something that JavaScript is not capable of and so the same loop as before is generated.
(function func-name ( [ p1 p2 .. pN ] ) body) (function+ func-name ( [ (p1 "Hello") ] )) ; acl: public, p1 defaults to a string value (function# func-name ( [ (p1 :ref) ] )) ; acl: protected, p1 is a reference variable (function- func-name ( [ (p1 ()) ] )) ; acl: private p1 defaults to 'NULL' (function* ( [ p1 p2 ] ) body ) ; anonymous function with two parameters
See also DEFUNC CLASS MVAR CVAR Coding for PHP Coding for javaScript
This is a synonym for DEFUN
and its variants. Please refer to the DEFUN instruction for further information.
(functionc func-name ( [ p1 p2 .. pN ] ) body) (functionc+ func-name ( [ (p1 "Hello") ] )) ; acl: public, p1 defaults to a string value (functionc# func-name ( [ (p1 :ref) ] )) ; acl: protected, p1 is a reference variable (functionc- func-name ( [ (p1 ()) ] )) ; acl: private p1 defaults to 'NULL' (functionc* ( [ p1 p2 ] ) body ) ; anonymous function with two parameters
See also DEFUNC CLASS MVAR CVAR Coding for PHP Coding for javaScript
This is a synonym for DEFUNC
and its variants. Please refer to the DEFUN instruction for further information.
(if test consequent [ alternate ] ) (if (== language "FELT") (emit "You made the right choice ;"))
The test
can be any valid expression. The consequent
expression will be executed if the test is 'true'. If the optional alternate
expression is supplied then that will be executed if the test is 'false'. You MUST use PROGN if the consequent or alternate expressions are more than a single expression.
There is no else
statement in FELT as they are not really required. FELT tries not to encourage lots of IF.. THEN.. ELSEIF.. ELSEIF.. ELSE type of coding. If you wish to have nested logic like that then you can just next the IF
instructions like this:
(if (time-to-trigger) (progn (trigger-set)) (if (== alarm 0) (process-alarm) (progn ;; do not process alarm! (no-alarm 1) (no-alarm 2))))
The generated (tidied) PHP code for the above is:
if (time_to_trigger()) { trigger_set(); } else { if (($alarm) == (0)) { process_alarm(); } else { no_alarm(1); no_alarm(2); } }
(incf var-name [ delta ] ) (incf x) ; destructivley increments x by 1 (incf x 2) ; by 2 (++ x (get-mode)) ; by whatever (get-mode) returned
This will destructivley modify the contents of the target variable by the amount specified, with the default being one. The optional delta value can be any valid expression for the target language and it will be applied at run-time.
Many languages support the notion of "post-decrement", "pre-decrement", "post-increment" and "pre-increment". I have taken the view, at least for now and the foreseable future that these are, whilst "useful", nothing more than syntactic sugar, and as Alan Perlis has been quoted as saying:
Syntactic sugar causes cancer of the semicolon.
Who is Alan Perlis, and why do I quote him? Go find out...
This means that whilst post-increment and post-decrement are supported, there is currently no way to "inline" the pre-increment and pre-decrement operators found in other languages. That may grate with some but for now FELT stands fast on that mainly because Lisp doesn't offer it either and nobody found a reason to complain loud enough to get it changed!
(include filename1 filename2 ...filenameN)
See also INCLUDE-ONCE REQUIRE REQUIRE-ONCE
This will cause a file to be pulled into the run-time system of the back-end coder if that feature is supported. Please refer to REQUIRE for full information.
(include-once filename1 filename2 ...filenameN)
See also INCLUDE REQUIRE REQUIRE-ONCE
This will cause a file to be pulled into the run-time system of the back-end coder once and once only if that feature is supported. Please refer to REQUIRE for full information.
(incr var-name [ delta ] ) (incr x) ; non-destructivley increments x by 1 for evaluation (incr x 2) ; by 2 (incr x (get-mode)) ; by whatever (get-mode) returned
This will destructivley modify the contents of the target variable by the amount specified, with the default being one. The optional delta value can be any valid expression for the target language and it will be applied at run-time.
What this means is that the targeted variable has the supplied delta
expression applied to it but only for the duration of the expression. Some examples will make this clear; the idiom is very common and you will reocgnise it immediately!
(defvar x 42) (emit "x plus ten: " (incr x 10) "\n") (emit "x is still: " x "\n")
This generate the following PHP:
$x = 42; echo "x plus ten: ", ($x+10), "\n"; echo "x is still: ", $x, "\n";
When executed the above produces:
$ felt docex.felt -cw | php x plus ten: 52 x is still: 42
And the javaScript as well (piped through Nodejscoder):
var x = 42; console.log("x minus ten: "+ (x+10)+ "\n"); console.log("x is still: "+ x+ "\n");
Which when piped through the Node.js coder gives us this output:
$ felt docex.felt -as nodejscoder | node x plus ten: 52 x is still: 42
(instanceof x Class) ; back0end coder specific return, normally boolean ; #t if x is an instance of class Class
See also TYPEOF
This returns a value, normally a boolean type, normally 'true' if the variable being tested is an instance or deriviative of class 'Class'.
(interface (defun function1 (a b c)) (defun function2 (d e)) (defun function3 ()))
See also DEFCLASS
The interface body contains functions but the body of the function is ignored for this instruction.
As the actual details of how an interface is actually implemented can vary greatly from language to language it is better to refer to the language specific guidelines.
As JavaScript doesn't really have "interfaces" (that's not to say they aren't do-able using syntactic juggling with classes) the only currently supported language back-end that renders this instruction is PHP. To that end pleaser refer to the Coding for PHP Guidelines pages for further information.
(defvar foo (new Foo)) (mcall foo (set :age 42)) ; set a property on fictitious instance (mcall foo (set :id 100) (load-from-db)) ; chain two calls (-> foo (get :age)) ; get the age property (-> foo :field-name (exec)) ; call exec() on sub-field field-name
See also CLASS-CALL / :: CLASS
This is how FELT lets you call methods on a class instance. The target of the sends (the 'receiver') is the first expression after the instruction, be in the MCALL
or the short-form ->
, and the remainder of the expressions within the instruction are interpreted as function calls or field-names to be de-referenced. See the fulldocumentation.
This instruction, MCALL
, is probably the second most frequently used instruction when operating within the 'object oriented' paradigm, especially for imperative style back-end languages. FELT allows you to either use MCALL
or a more convenient short-form, ->
, which is identical in appearance to that used in PHP and also some other languages when de-referencing from a pointer to something.
Probably the easiest way to show you how to use this is just to give lots of examples and leave it at that.
(defvar rcvr (new ReceiverObjectClass)) (defvar age (mcall rcvr (get :age))) (defvar id (-> rcvr (get :id)))
Generated PHP:
$rcvr = new ReceiverObjectClass(); $age = $rcvr->get('age'); $id = $rcvr->get('id');
Generated JavaScript:
var rcvr = new ReceiverObjectClass(); var age = rcvr.get('age'); var id = rcvr.get('id');
(defvar rcvr (new ReceiverObjectClass)) (defvar age "age") (defvar id "id_fn") (defvar slot "slot_accessor_fn") (defvar extracted-age (mcall rcvr age)) (defvar extracted-id (-> rcvr id)) (defvar x (-> rcvr slot (read-thing 10))) (defvar y (-> rcvr slot (write-thing 10) (verify-thing 10)))
Generated PHP:
$rcvr = new ReceiverObjectClass(); $age = "age"; $id = "id_fn"; $slot = "slot_accessor_fn"; $extracted_age = $rcvr->$age; $extracted_id = $rcvr->$id; $x = $rcvr->$slot->read_thing(10); $y = $rcvr->$slot->write_thing(10)->verify_thing(10);
Generated JavaScript:
var rcvr = new ReceiverObjectClass(); var age = "age"; var id = "id_fn"; var slot = "slot_accessor_fn"; var extracted_age = rcvr.age; var extracted_id = rcvr.id; var x = rcvr.slot.read_thing(10); var y = rcvr.slot.write_thing(10).verify_thing(10);
(mvar var-name) ; public member variable is the default (mvar+ var-name) ; public member variable (mvar# var-name) ; protected variable (mvar- (var-name default-value)) ; private member variable (mvar var-name-1 ; multiple CVAR-s will all have the var-name-2 ; same level of access control (var-name-3 "default"))
You should only use MVAR
within a current CLASS
declaration as it makes no sense to use it anywhere else. You can define a member variable with or without a default value or you can define many variables at once, again with or without a default value.
FELT supports the notion of member variables that can be "public", "private" or "protected" and in order to express those intentions you can choose from the following:
Instruction | Visibility |
---|---|
MVAR |
public |
MVAR+ |
public |
MVAR- |
private |
MVAR# |
protected |
For full details on how to compose a "class", read the CLASS page as that goes into some detail on how to declares classes in general. This page is more concerned with the actual means of declaring member variables.
To define a member variable you just name the variable within the MVAR
instruction like so:
(class Foo (mvar bar))
If you want to supply a default value then you use the same syntax as for when you write a function signature with DEFUN/FUNCTION, by placing the variable name and the default value inside parentheses like so:
(class Foo (mvar (bar 100)))
Instead of having to laboriously type MVAR
for every single class variable, you can in fact supply multiple of them at once like this:
and the FELT process will generate as much target code as is required to make them work for the chosen back-end language.
(class Foo (mvar# a) (defun ctor (anA) (= (my :a) anA)))
MY
is a small syntactic sugary thing that just makes assigning this with the this
receiver a little shorter. The member variable must be expressed as a keyword to be correctly translated by the back-end coder otherwise it will be seen as a variable name.
(new Classname arg1 arg2 ... argN) ; create instance of Classname (new var-name arg1 arg2 ... argN) ; create instance of String value of var-name (new StreamWrapper "http://feltweb.info") (defvar dyn-class "StreamWrapper") (new dyn-class "http://feltweb.info" 8192)
See also DEFCLASS MVAR CVAR CONST THIS MY MCALL / -> CLASS-CALL / ::
FELT allows "object oriented" development by way of the NEW
instruction. Use a constant Classname or a variable name containing a string value at run-time. All other arguments are passed through to the constructor. A new instance is returned.
The NEW
instruction is how FELT lets you write programs that need to use the object-oriented (OO) paradigm. The instruction takes the first argument as being either a literal classname if it starts with an Uppercase Letter otherwise it assumes it to be the name of the variable. This is not ideal but it is the simplest way and most programming conventions adhere to this; camel-case class names are everywhere and FELT has had to rely upon this.
Translating the example code into PHP we get this:
new StreamWrapper("http://feltweb.info", 8192); $dyn_class = "StreamWrapper"; new $dyn_class("http://feltweb.info", 8192);
In the first instantiation we see the normal usage of the PHP new
function and in the second we can see that the value of the $dyn_class
variable has been used to indirectly
(not expr) ;; test for the logical inverse
This is the usual "logical AND" instruction which will evaluate to "true" if all of the terms within it also evaluate to "true". Some back-end languages use short-circuiting and some do not so you will have to research how the rendered code operates on each chosen backend.
(or expr-1 expr-2 ... exprN)
This is the usual "logical AND" instruction which will evaluate to "true" if all of the terms within it also evaluate to "true". Some back-end languages use short-circuiting and some do not so you will have to research how the rendered code operates on each chosen backend.
(progn expr1 expr2 ... exprN) ; a "block" of expressions (if (== a 42) ; if a is 42 then (progn ; execute this block of expressions ...body..))
See also IF
This instruction is how you logically enclose a block of expressions. In PHP, javaScript and Node.js this is the same as encasing the same block of code within the {}
sequence. Any valid FELT expressions can be placed within the body of the PROGN
instruction.
You can use the PROGN
instruction to introduce a new lexically scoped block of statements into you target language at any point that that is legal to do so but be careful that you do not cause problems for yourself. Some languages have different rules about shadowing when you do this.
In 'C' and 'C++' you can use a block to introduce extra variables into the "case" part of a "switch" statement for example. That's academic right now as neither of those is supported by FELT yet but just bear in mind that whilst FELT takes advantage of the fact that most languages have a large enough overlap to allow it to work at all, you can get bitten if you are not paying full attention at all times.
(require filename1 filename2 ...filenameN)
See also INCLUDE INCLUDE-ONCEREQUIRE-ONCE
This instruction informs the back-end coder that it should insist that the named file(s) being included into the run-time system and that if any one of them fails to do so that some kind of exception should be raised.
You have to bear in mind that FELT originally started life as a Lisp way of coding PHP before it became what it is, a translation system into any language that a back-end coder can be created for.
The difference between requiring a file and including a file really depends upon the final run-time environment.
These again are an indication to the back-end that whatever it does, it should only do so once. This speeds up compilation / interpretation and also prevents "duplicate definition" types of errors from occurring.
All four of these instructions allow for multiple filenames to be specified within the instruction, this saves having to re-enter the instruction everytime. The back-end coder takes care of generating the correct code.
To explain what these instructions do it's probably easist to show a really simple snippet of FELT code and then just show the translated output, so here we go:
(require "one-filename") (require-once "filename-a" "filename-b") (include "another-file") (include-once "yet-another-file" "and-his-dog")
require("one-filename"); require_once("filename-a"); require_once("filename-b"); include("another-file"); include_once("yet-another-file"); include_once("and-his-dog");
<script type="text/javascript" src="one-filename"></script> <script type="text/javascript" src="filename-a"></script> <script type="text/javascript" src="filename-b"></script> <script type="text/javascript" src="another-file"></script> <script type="text/javascript" src="yet-another-file"></script> <script type="text/javascript" src="and-his-dog"></script>
It can be seen that the javaScript coder makes the assumption that the 'file inclusion' is operating from within the contact of a web page and thus creates the correct type of tag to cause it to be included.
require("one-filename") require("filename-a") require("filename-b") require("another-file") require("yet-another-file") require("and-his-dog")
The Node.js system has the idiom of assigning the returned value from the require()
statement into a variable. If you undertand the way that Node.js modules are packaged then you will appreciate this and here then is how in a FELT program that you would capture that value into a variable for subsequent re-use:
(defvar html (require :html)) (defvar amqp (require :amqp))
If you visit the REPL page you will find some Node.js examples that demonstrate this and show how to write a simple Node server in FELT. It's easy!
(require-once filename1 filename2 ...filenameN)
See also INCLUDE INCLUDE-ONCE REQUIRE
This will cause a file to be pulled into the run-time system of the back-end coder once and once only if that feature is supported. Please refer to REQUIRE for full information.
(return [ expression ] ) ; return with optional expression (return 42) (return (foo 3.314)) (return*) ; return 'this', for chaining
See also CALL CLASS-CALL/::
This is the class 'return from a function' instruction. It can be used with or without an expression. The expression is evaluated at run-time. For class authors, use return*
to ensure the 'this' pointer is returned. This makes it easy to produce methods that can be chained together.
There isn't much to say about this instruction; used with an optional expression it will cause that expression to be evaluated at run-time and the resulting value will be passed back to the call site.
(return) (return "Hello World"); (return (file-get-contents filename))
The above simple examples generate the following PHP code;
return; return "Hello World"; return file_get_contents($filename);
And the JavaScript code:
return; return "Hello World"; return file_get_contents(filename);
Yup, the same code in this case but remember that unless there is an implementation of file_get_contents
at run-time this javascript will fail!
If you are writing a class (using CLASS) and you want to produce a set of methods that can be chained then FELT provides a convenience form, return*
that causes the 'this' pointer to be returned, whatever that means for the selected back-end target coder!
(class Foo (defun say (message) (emit message) (return*)))
In PHP the following is produced for the above class definition:
class Foo { function say($message) { echo $message; return $this; } }
And for JavaScript:
function Foo () { this.say = function(message) { console.log(message); return this; } }
(setq var-name expression) (setq total 0)
See also = (ASSIGN) DEFVAR
This instruction is named after the Lisp 'set quote' command. It performs the same task and so please refer to the = (ASSIGN) instruction page for complete instructions and examples.
(switch expression ...example: (switch (parse mode) match-1 handler-1 0 (do-exit) (match-2 .. match-2) handler-2 (:h :help :?) (show-help) match-3 handler 3 "l" (list-files) [ default-handler ] ) (show-unknown-command))
note: You can group clauses as shown for match-2... this will produce multiple case tests for a single handler as you would expect.
See also IF
This tests an expression against various constant values and executes a code block when the first match is found. Exactly how is dependent on the back-end coder but essentially this instruction is the classical "switch-case" statement found in C, C++, PHP, JavaScript et al. The match and handler expressions must be present in pairs. If an odd number of terms is presented, the last expression is evaluated as the 'default' of the switch.
The FELT SWITCH
instruction may look a little different to the PHP or JavaScript equivalent but it is in fact easier to enter as there is less typing involved. The words case
, break
and default
are added automatically by the back-end coder so you don't need to enter them at all.. they are implied by dint of the position of the arguments to SWITCH
.
This need not be a simple variable but can be as complicated a FELT expression as you can think of. So long as the final code that is generated is syntactically correct then the expression will be compiled / interpreted and execute as expected.
The case handlers, like socks, are best supplied in two-s. This means that you don't have to waste time typing all that unnecessary stuff, the FELT back-end coders will do that for you. The only potential downside is that precisely because the back-end coder does generate the code for you, there is no way to implement the "technique" of deliberately omitting a "break" so that one handler runs into the next. If that's the kind of code you like to write then FELT probably won't appeal to you that much on that front; it tends to prefer non-clever but reliable code as opposed to sleight-of-hand trickery.
It is very common to supply a default handler to catch those values that are either invalid or require some common processing than those specifically picked out by the match handlers. All you have to do to instruct FELT that a handler is a default handler is to place it ** at the end of an odd number of terms** in the instruction. FELT will see that after parsing it has 'one left over' and mark that as being the default handler.
The following example shows a simple handler that could be the top level REPL handler (read-eval-print loop) of some simple terminal based utility. Without going into too many details, it could be implemented like so, note how the default handler is used to print an error about the last command not being recognised.
(defvar quit? #f) (while (not quit?) (emit "foo-console > ") (switch (get-command) :open (open-file) (:quit :exit) (= quit? #t) :list (list-files) (show-error))) (emit "foo-console says good-bye!\n")
The PHP code generated from the above FELT source is as follows:
$quit_qm_ = FALSE; while((!($quit_qm_))) { echo "foo-console > "; switch(get_command()) { case 'open': {open_file();} break; case 'quit': case 'exit': {$quit_qm_ = TRUE;} break; case 'list': {list_files();} break; default: {show_error();} break; } } echo "foo-console says good-bye!\n";
And the same code but as JavaScript (Node) looks like this.
var quit_qm_ = false; while((!(quit_qm_))) { console.log("foo-console > "); switch(get_command()) { case 'open': {open_file();} break; case 'quit': case 'exit': {quit_qm_ = true;} break; case 'list': {list_files();} break; default: {show_error();} break; } } console.log("foo-console says good-bye!\n");
By careful planning you can see how easy it is to build language portable code with FELT!
(class Foo (mvar# a) (defun ctor (anA) (= (-> this :a) anA)))
The THIS
instruction is a pseudo-receiver that allows you to call class methods within the class that you are creating. Note that the target membervariable must be expressed as a keyword or it will be interpreted as a variable name in the rendered code. See also MY which offers a shorter and neater way of doing this.
Let's take the exmple code in the header and see how that looks as PHP:
class Foo { protected $a; function __construct($anA) { $this->a = $anA; } }
We can see that the special name ctor
has been translated into the PHP equivalent of the constructor function name for clases. Please see CLASS for further details on this and other special names that are recognised.
The same example translated into JavaScript gives us this:
function Foo (anA) { this.a = anA; }
Here we can see that this time the ctor
function signature has been used in the classname function signature and the body of it forms the "constructor" of the "class".
(throw expression)
For languages that have exception / condition handling systems this instruction is how you raise or signal that an "exception" needs to be thrown and some (hopefully) registered TRY / CRASH handler will grab it and deal with it. Please see that page for more information on catching exceptions.
See also TRY / CRASH
The nature of exception handling across languages us so wide and varied that FELT does little more than allow you to raise an exception. It does not check what you put into the body of thw THROW
instruction at all. This is definitely one of those times where you will need to use the #F:WHEN instruction to handle any differences.
In PHP code you have to use the Exception class as the base for any exception that you raise so at first you mightbe tempted to code like this:
(throw (new Exception "I blew up here"))
You will get that looks like this:
throw new Exception("I blew up here");
Fair enough, but running the same code through the Node.js coder produces this:
throw new Exception("I blew up here");
And I can tell you now that is not going to get past any JavaScript parser on the planet! According to the JavaScript documentation:
The exception can be a string, integer, Boolean or an object.
That's somewhat different that in the PHP world and so you can see that FELT really doesn't have much of a chance and therefore stands back and lets you take the responsibility for it using the conditional inclusion helper: #F:WHEN
. So, if I wanted to make it portable I would use the #F:WHEN
instruction to do it like this:
(defun do-domething-with (x) (if (x-is-valid x) (return TRUE) (progn (#f:when PHPCoder (throw (new Exception "X is rubbish, fix it!"))) (#f:when JSCoder (throw "X is rubbish, sort it out...")))))
Please note that you need to enclose them in a PROGN
instruction because the syntax of IF is such that it only accepts a single instruction on either the consequent or alternate positions.
Here is the PHP code that is rendered from the above FELT source:
function do_domething_with($x) { if (x_is_valid($x)) { return TRUE; } else { throw new Exception("X is rubbish, fix it!"); } }
And the JavaScript back-end produces code that looks like this:
function do_domething_with(x) { if (x_is_valid(x)) { return true; } else { throw "X is rubbish, sort it out..."; } }
Once again we have portable code without too much fuss!
(try expression (catch (Etype-1 handler) (Etype-2 handler) (Etype-N handler)) (finally expression))
This is the FELT instruction to wrap an expression body in a try/catch handler. Note that the expressions are not implied blocks and thus you must use PROGN
for the expression, finally expression and handler expressions if they are more than one instruction long.
See also THROW
FELT understands that lots of languages have the concept of 'exceptions' and 'catching exceptions' and so it tries to cater for this across languages by providing the TRY/CRASH
instruction. CRASH
is just a synonym for TRY
and is there for those days when typing the word crash instead of try just feels like the right thing to do. Everybody has bad days.
Some languages do not have the finally
part available. If you should try to use a finally handler for that coder, it may or may not blow up on you depending upon how it was written. The stock FELT coders will not blow up but will wrap the code in a comment block so at least you can see that it is not being handled as expected should you examine the source code.
Here is a small example of using the TRY/CRASH
instruction so that you can see how to use it. All options are shown, multiple exception handlers and a finally clause:
NOTE: Whatever class-names you use for naming exceptions are not portable as they are going to be target dependent. This of course is usual with FELT... it cannot possibly know the name of every exception across every language and so the responsibility is yours to ensure that meaningful code is ultimately rendered.
It is highly recommended that specifics like this be placed into separate FELT source files and hat your build process only pull in what is correct for the relevant target language being rendered to.
(try (progn (defvar x 0) (defvar f (generate-filename x)) (call-dodgy-file-function f x)) (catch ((FileIOError e) (progn (emit "File io error " e) (handle-file-error))) ((DiskSpaceError dse) (progn (emit "Disk space error " dse) (handle-disk-error)))) (finally (progn (release-resources) (free-disk-handles))))
Here is the rendered PHP code and you can see that, as PHP does not have the 'finally' part within its repertoire that particular code has been commented out so that when you realise that your finally code isn't working, when you examine the source that has been generated you will know why!
/* PHP does not support a "finally" statement. */ /* {release_resources(); free_disk_handles();} */ try { $x = 0; $f = generate_filename($x); call_dodgy_file_function($f, $x); } catch (FileIOError $e) { echo "File io error ", $e; handle_file_error(); } catch (DiskSpaceError $dse) { echo "Disk space error ", $dse; handle_disk_error(); }
The JavaScript for the same FELT source is show here, and this time we can see that as JavaScript does have a try / catch / finally
triumvirate that it has been placed into the output as live code:
try { var x = 0; var f = generate_filename(x); call_dodgy_file_function(f, x); } catch (e) { console.log("File io error "+ e); handle_file_error(); } catch (dse) { console.log("Disk space error "+ dse); handle_disk_error(); } finally { release_resources(); free_disk_handles(); }
(typeof foo) ; returns a representation of foo as a back-end ; specific data type. (typeof var-name) (typeof "a string") (typeof 3.14)
See also INSTANCEOF
This FELT instruction allows you to ask the back-end coder to interrogate the argument and return some representation of the actual type of it.
(while condition body) (defvar i 0) (while (decf i) (emit "Not there yet: " i "\n"))
This is a looping iterator that performs the conditional test after the execution of the body content.
To avoid confusion about do...while
and while...do
constructs, FELT uses the instruction name UNTIL
to indicate that the body will be executed at least once before the condition is checked. In other words, the UNTIL
instruction is the class do...while
after all but hopefully has a clearer name.
Here is a simple example that counts from zero to 9, the FELT source:
(defvar x 0) (until (< x 10) (emit "x is: " (incf x) "\n"))
and here is the translated PHP:
$x = 0; do { echo "x is: ", ($x++), "\n"; } while(($x) < (10));
and the JavaScript code too:
var x = 0; do { console.log("x is: "+ (x++)+ "\n"); } while((x) < (10));
(while condition body) (defvar i 0) (while (decf i) (emit "Not there yet: " i "\n"))
This is a looping iterator that performs the conditional test before the execution of the body content.
To avoid confusion about do...while
and while...do
constructs, FELT uses the instruction name UNTIL
to indicate that the body will be executed at least once before the condition is checked. In other words, the UNTIL
instruction is the class do...while
after all but hopefully has a clearer name.
Here is a simple example that counts from zero to 9, the FELT source:
(defvar x 0) (while (< x 10) (emit "x is: " (incf x) "\n"))
and here is the translated PHP:
$x = 0; while(($x) < (10)) { echo "x is: ", ($x++), "\n"; }
and the JavaScript (Node) code too:
var x = 0; while((x) < (10)) { console.log("x is: "+ (x++)+ "\n"); }