Glen Whitney
991976d3a8
All checks were successful
continuous-integration/drone/push Build is passing
feat: sequencing of expressions with newline to same indent Also revised README to reflect greater emphasis on streams. Haskell code generation unsurprisingly required a fairly significant rework. Resolves #3. Co-authored-by: Glen Whitney <glen@studioinfinity.org> Reviewed-on: #11 Co-Authored-By: Glen Whitney <glen@nobody@nowhere.net> Co-Committed-By: Glen Whitney <glen@nobody@nowhere.net>
164 lines
4.1 KiB
Cheetah
164 lines
4.1 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_break [[
|
|
(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. fostr enforces that successive expressions in sequence
|
|
must line up at the left, i.e., the following will not parse:
|
|
|
|
```fostr
|
|
**/
|
|
|
|
/** md */ test emit_thrice_bad_alignment [[
|
|
stream << 72 + 87
|
|
stream << 88
|
|
+ 96
|
|
99 + 12 >> stream
|
|
]] /* **/ parse fails
|
|
|
|
/** md
|
|
```
|
|
**/
|