Glen Whitney
804a00902a
Some checks failed
continuous-integration/drone/push Build is failing
Caveat: type is still not being assigned for the Sequence() constructor. Also fixes the parsing of literal strings (whitespace just after the opening quote was being ignored, and was ambiguous just before the opening quote).
234 lines
5.9 KiB
Cheetah
234 lines
5.9 KiB
Cheetah
module basic
|
|
language fostr
|
|
|
|
test hw1_type [[
|
|
[[stream]] << [['Hello, world! ']] << [[3+2]] << ' times.'
|
|
]]
|
|
run get-type on #1 to STREAM()
|
|
run get-type on #2 to STRING()
|
|
run get-type on #3 to INT()
|
|
run get-type to STREAM()
|
|
/** writes
|
|
Hello, world! 5 times.**/
|
|
|
|
/** 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())
|
|
]))
|