forked from glen/fostr
Glen Whitney
b9c8532899
Note that ultimately a terminated sequence may have a slightly different semantics (applying streams to `_|_`, most likely) but for now they don't.
224 lines
5.6 KiB
Cheetah
224 lines
5.6 KiB
Cheetah
module basic
|
|
language fostr
|
|
|
|
/** md
|
|
Title: A whirlwind tour of fostr
|
|
|
|
## Whirlwind tour
|
|
|
|
fostr is just in its infancy, so it's not yet even ready for
|
|
Hello, World. The best we can offer now is this little snippet
|
|
that writes the sum of the ASCII codes for 'H', 'W', and '!' to standard output:
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test emit_sum [[
|
|
stream << 72 + 87 + 33
|
|
]] /* **/
|
|
parse to TopLevel(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))))
|
|
/** writes
|
|
192**/
|
|
|
|
/** md
|
|
```
|
|
|
|
At the moment, there are only two ways to run a file containing fostr code
|
|
(you can find the above in `tests/emit_sum.fos`). They both start by
|
|
cloning this fostr project. Then, either:
|
|
|
|
1. Open the project in Eclipse and build it, visit your program file,
|
|
generate code from it in your preferred target language (among
|
|
the options available in the "Spoofax > Generate" menu), and execute the
|
|
resulting code.
|
|
|
|
1. Use the `bin/fosgen` bash script to generate code in a target language,
|
|
and execute the resulting code.
|
|
|
|
For example, this snippet generates the following Python:
|
|
```python
|
|
{! ../tests/emit_sum.py extract:
|
|
start: 'Stdio\s='
|
|
!}
|
|
```
|
|
(which writes "192" to standard output); it also generates identical code in
|
|
this simple example for
|
|
Javascript, although it generates a different preamble defining Stdio in each
|
|
case. (Haskell code generation is also currently supported.)
|
|
|
|
### Everything has a value
|
|
|
|
As mentioned in the [Introduction](../README.md), everything in a fostr
|
|
program (including the entire program itself) is an expression and has
|
|
a value. So what's the value of that expression above? Well, appropriately
|
|
enough, `stream` is our
|
|
first example of a stream, and for convenience, the value of a stream
|
|
receiving an item is (usually) just the stream back again. The `<<` operator
|
|
is also left-associative, so that way we can chain insertions into a stream:
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test emit_twice [[
|
|
stream << 72 + 87 + 33 << 291
|
|
]] /* **/
|
|
parse to TopLevel(
|
|
Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291")))
|
|
/** writes
|
|
192291**/
|
|
|
|
/** md
|
|
```
|
|
Running this program produces a nice palindromic output: "192291".
|
|
|
|
And because sometimes you want to emphasize the value and propagate that
|
|
instead of the stream, you can also write these expressions "the other way"
|
|
with `>>`; both forms return the first argument, so the following writes "824":
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test enters_twice [[
|
|
(7 + 8 >> stream + 9) >> stream
|
|
]] /* **/
|
|
parse to TopLevel(
|
|
To(Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9")), Stream()))
|
|
/** writes
|
|
824**/
|
|
|
|
/** md
|
|
```
|
|
|
|
### Layout in fostr
|
|
|
|
Expressions may be laid out onto multiple lines, as long as all continuation
|
|
lines are indented from the start of the initial line:
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test receive_enter_break [[
|
|
stream <<
|
|
7
|
|
+ 8 >> stream
|
|
+ 9
|
|
]] /* **/
|
|
parse to TopLevel(
|
|
Gets(Stream(), Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9"))))
|
|
/** writes
|
|
824**/
|
|
|
|
/** md
|
|
```
|
|
(So for example you will get a parse error with something like this:)
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test enter_receive_bad_continuation [[
|
|
(7 + 8 >> stream + 9)
|
|
>> (stream << 9 + 2)
|
|
]] /* **/
|
|
parse fails
|
|
|
|
/* Extra tests not in the tour */
|
|
test enter_receive [[
|
|
(7 + 8 >> stream + 9) >> (stream << 9 + 2)
|
|
]] /* **/
|
|
parse to TopLevel(
|
|
To(Sum(Sum(Int("7"),To(Int("8"),Stream())),Int("9")),
|
|
Gets(Stream(),Sum(Int("9"),Int("2")))))
|
|
/** writes
|
|
81124**/
|
|
|
|
/** md
|
|
```
|
|
|
|
Of course, fostr programs are not limited to one line; expressions on successive
|
|
lines are evaluated in sequence. For example, the program
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test emit_thrice [[
|
|
stream << 72 + 87
|
|
stream << 88
|
|
+ 96
|
|
99 + 12 >>
|
|
stream
|
|
]] /* **/
|
|
parse to TopLevel(Sequence([
|
|
Gets(Stream(), Sum(Int("72"), Int("87"))),
|
|
Gets(Stream(), Sum(Int("88"), Int("96"))),
|
|
Sum(Int("99"), To(Int("12"), Stream()))
|
|
]))
|
|
/** writes
|
|
15918412**/
|
|
|
|
/** md
|
|
```
|
|
|
|
will write 15918412. The fostr parser enforces that successive expressions
|
|
in sequence align at the left; e.g., the following fails to parse:
|
|
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test emit_thrice_bad_alignment [[
|
|
stream << 72 + 87
|
|
stream << 88
|
|
+ 96
|
|
99 + 12 >> stream
|
|
]] /* **/
|
|
parse fails
|
|
|
|
/** md
|
|
```
|
|
|
|
Note you can optionally terminate an expression in a sequence with a semicolon,
|
|
and you may place multiple expressions on a single line if the earlier one(s)
|
|
are so terminated. So the following is OK:
|
|
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test emit_several [[
|
|
stream << 1 + 2; 3 >> stream
|
|
(4 + 5) >> stream; stream << 6;
|
|
stream << 7
|
|
stream << 8
|
|
+ (9+10);
|
|
11 + 12 >> stream; 13 >> stream
|
|
>> stream
|
|
]] /* **/
|
|
parse to TopLevel(Sequence([
|
|
ISequence(Prior([Terminate(Gets(Stream(), Sum(Int("1"), Int("2"))))]),
|
|
To(Int("3"), Stream())),
|
|
ISequence(Prior([Terminate(To(Sum(Int("4"), Int("5")), Stream()))]),
|
|
Terminate(Gets(Stream(), Int("6")))),
|
|
Gets(Stream(), Int("7")),
|
|
Terminate(Gets(Stream(), Sum(Int("8"), Sum(Int("9"), Int("10"))))),
|
|
ISequence(Prior([Terminate(Sum(Int("11"), To(Int("12"), Stream())))]),
|
|
To(To(Int("13"), Stream()), Stream()))
|
|
]))
|
|
/** writes
|
|
3396727121313**/
|
|
|
|
/** md
|
|
```
|
|
**/
|
|
|
|
test emit_several_desugar [[
|
|
stream << 1 + 2; 3 >> stream
|
|
(4 + 5) >> stream; stream << 6;
|
|
stream << 7
|
|
stream << 8
|
|
+ (9+10);
|
|
11 + 12 >> stream; 13 >> stream
|
|
>> stream
|
|
]] /* don't test */
|
|
run desugar-fostr to TopLevel(Sequence([
|
|
Terminate(Gets(Stream(), Sum(Int("1"), Int("2")))),
|
|
To(Int("3"), Stream()),
|
|
Terminate(To(Sum(Int("4"), Int("5")), Stream())),
|
|
Terminate(Gets(Stream(), Int("6"))),
|
|
Gets(Stream(), Int("7")),
|
|
Terminate(Gets(Stream(), Sum(Int("8"), Sum(Int("9"), Int("10"))))),
|
|
Terminate(Sum(Int("11"), To(Int("12"), Stream()))),
|
|
To(To(Int("13"), Stream()), Stream())
|
|
]))
|