docs: Finally get the tour to start from the real helloworld

Also improves the testing situation for the features to date.
  Resolves #17.
This commit is contained in:
Glen Whitney 2021-02-19 08:08:24 -08:00
parent 5ef816610b
commit f9c6e04c8c

View File

@ -16,15 +16,21 @@ Title: A whirlwind tour of fostr
## Whirlwind tour ## Whirlwind tour
fostr is just in its infancy, so it's not yet even ready for There seems only to be one way to start a tour like this. So here goes:
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 ```fostr
**/ **/
/** md */ test emit_sum [[ /** md */ test hello_world [[
stream << 72 + 87 + 33 <<< 'Hello, world!'
]] /* **/ ]] /* **/
parse to TopLevel(DefGets(LitString("'Hello, world!'")))
/** writes
Hello, world!**/
// Prior proto-hello-world, no longer in the tour.
test emit_sum [[
stream << 72 + 87 + 33
]]
parse to TopLevel(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33")))) parse to TopLevel(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))))
/** writes /** writes
192**/ 192**/
@ -33,7 +39,7 @@ parse to TopLevel(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))))
``` ```
At the moment, there are only two ways to run a file containing fostr code 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 (you can find the above in `tests/hw.fos`). They both start by
cloning this fostr project. Then, either: cloning this fostr project. Then, either:
1. Open the project in Eclipse and build it, visit your program file, 1. Open the project in Eclipse and build it, visit your program file,
@ -46,30 +52,70 @@ cloning this fostr project. Then, either:
For example, this snippet generates the following Python: For example, this snippet generates the following Python:
```python ```python
{! ../tests/emit_sum.py extract: {! ../tests/hw.py extract:
start: 'Stdio\s=' start: 'Stdio\s='
!} !}
``` ```
(which writes "192" to standard output); it also generates identical code in It generates nearly identical code in
this simple example for this simple example for Javascript (just with `"Hello, world!"`
Javascript, although it generates a different preamble defining Stdio in each in place of `r'Hello, world!'`), although it generates a different
case. (Haskell code generation is also currently supported.) preamble defining Stdio for each language. (Currently, Haskell code
generation is also supported.)
There's not much to break down in such a tiny program as this, but let's do
it. The prefix operator `<<<` could be read as "the default stream receives...",
and unsurprisingly in a main program the default stream is standard input and
output. And `'Hello, world!'` is a literal string constant; what you see is
what you get. The only detail to know is that such constants must occur
within a single line of your source file. So depending on how you
ran the program and how closely you looked at its output,
you may have noticed this program does not write a newline at the end
of its message. Nothing is ever implicitly sent to a stream. So if you want
newlines, you should switch to a (double-quoted) string that allows
the usual array of escape sequences:
```fostr
**/
/** md */ test hello_esc_world [[
<<< "Hello,\t\tworld!\n\n"
]] /* **/
parse to TopLevel(DefGets(EscString("\"Hello,\t\tworld!\n\n\"")))
/** writes
Hello, world!
**/
/** md
```
(We threw in two of each so you could clearly see them in the output if
you run this program.)
### Everything has a value ### Everything has a value
As mentioned in the [Introduction](../README.md), everything in a fostr As mentioned in the [Introduction](../README.md), everything in a fostr
program (including the entire program itself) is an expression and has program (including the entire program itself) is an expression and has
a value. So what's the value of that expression above? Well, appropriately a value. So what's the value of that expression above? Well, for convenience,
enough, `stream` is our the value of a stream receiving an item is (generally) just the stream back
first example of a stream, and for convenience, the value of a stream again. That way we can use the general (left-associative)
receiving an item is (usually) just the stream back again. The `<<` operator `_stream_ << _value_` operator to chain insertions into a stream:
is also left-associative, so that way we can chain insertions into a stream:
```fostr ```fostr
**/ **/
/** md */ test emit_twice [[ /** md */ test state_obvious [[
stream << 72 + 87 + 33 << 291 <<< 'Two and ' << 2 << ' make ' << 2+2 << ".\n"
]] /* **/ ]] /* **/
parse to TopLevel(
Gets(Gets(Gets(Gets(DefGets(LitString("'Two and '")),Int("2")),
LitString("' make '")),Sum(Int("2"),Int("2"))),
EscString("\"./n\"")))
/** writes
Two and 2 make 4.
**/
test emit_twice [[
stream << 72 + 87 + 33 << 291
]]
parse to TopLevel( parse to TopLevel(
Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291"))) Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291")))
/** writes /** writes
@ -77,24 +123,28 @@ parse to TopLevel(
/** md /** md
``` ```
Running this program produces a nice palindromic output: "192291".
And because sometimes you want to emphasize the value and propagate that 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" 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": with `>>>` for sending to the default stream or `>>` in general; these forms
(generally) return the value sent, so the following writes "824":
```fostr ```fostr
**/ **/
/** md */ test enters_twice [[ /** md */ test enters_twice [[
(7 + 8 >> stream + 9) >> stream (7 + 8 >> stream + 9) >>>
]] /* **/ ]] /* **/
parse to TopLevel( parse to TopLevel(
To(Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9")), Stream())) DefTo(Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9"))))
/** writes /** writes
824**/ 824**/
/** md /** md
``` ```
Two things are worth noting here: the default stream can always be referred to
directly via the identifier `stream`, and the precedences of `<<` and `>>` are
different so that generally full expressions go to a stream with `<<` but
just individual terms are sent with `>>`.
### Layout in fostr ### Layout in fostr
@ -104,13 +154,13 @@ lines are indented from the start of the initial line:
**/ **/
/** md */ test receive_enter_break [[ /** md */ test receive_enter_break [[
stream << <<<
7 7
+ 8 >> stream + 8 >>>
+ 9 + 9
]] /* **/ ]] /* **/
parse to TopLevel( parse to TopLevel(
Gets(Stream(), Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9")))) DefGets(Sum(Sum(Int("7"), DefTo(Int("8"))), Int("9"))))
/** writes /** writes
824**/ 824**/
@ -121,8 +171,8 @@ parse to TopLevel(
**/ **/
/** md */ test enter_receive_bad_continuation [[ /** md */ test enter_receive_bad_continuation [[
(7 + 8 >> stream + 9) (7 + 8 >>> + 9)
>> (stream << 9 + 2) >> (<<< 9 + 2)
]] /* **/ ]] /* **/
parse fails parse fails
@ -145,16 +195,17 @@ lines are evaluated in sequence. For example, the program
**/ **/
/** md */ test emit_thrice [[ /** md */ test emit_thrice [[
stream << 72 + 87 <<< 72 + 87
stream << 88 <<< 88
+ 96 + 96
99 + 12 >> 99 + 12
stream >>>
]] /* **/ ]] /* **/
parse to TopLevel(Sequence([ parse to TopLevel(Sequence([
Gets(Stream(), Sum(Int("72"), Int("87"))), DefGets(Sum(Int("72"), Int("87"))),
Gets(Stream(), Sum(Int("88"), Int("96"))), DefGets(Sum(Int("88"), Int("96"))),
Sum(Int("99"), To(Int("12"), Stream())) Sum(Int("99"), DefTo(Int("12")))
])) ]))
/** writes /** writes
15918412**/ 15918412**/
@ -169,10 +220,10 @@ in sequence align at the left; e.g., the following fails to parse:
**/ **/
/** md */ test emit_thrice_bad_alignment [[ /** md */ test emit_thrice_bad_alignment [[
stream << 72 + 87 <<< 72 + 87
stream << 88 <<< 88
+ 96 + 96
99 + 12 >> stream 99 + 12 >>>
]] /* **/ ]] /* **/
parse fails parse fails
@ -187,23 +238,23 @@ are so terminated. So the following is OK:
**/ **/
/** md */ test emit_several [[ /** md */ test emit_several [[
stream << 1 + 2; 3 >> stream <<< 1 + 2; 3 >>>
(4 + 5) >> stream; stream << 6; (4 + 5) >>>; stream << 6;
stream << 7 <<< 7
stream << 8 <<< 8
+ (9+10); + (9+10);
11 + 12 >> stream; 13 >> stream 11 + 12 >>>; 13 >>>
>> stream >>>
]] /* **/ ]] /* **/
parse to TopLevel(Sequence([ parse to TopLevel(Sequence([
ISequence(Prior([Terminate(Gets(Stream(), Sum(Int("1"), Int("2"))))]), ISequence(Prior([Terminate(DefGets(Sum(Int("1"), Int("2"))))]),
To(Int("3"), Stream())), DefTo(Int("3"))),
ISequence(Prior([Terminate(To(Sum(Int("4"), Int("5")), Stream()))]), ISequence(Prior([Terminate(DefTo(Sum(Int("4"), Int("5"))))]),
Terminate(Gets(Stream(), Int("6")))), Terminate(Gets(Stream(), Int("6")))),
Gets(Stream(), Int("7")), DefGets(Int("7")),
Terminate(Gets(Stream(), Sum(Int("8"), Sum(Int("9"), Int("10"))))), Terminate(DefGets(Sum(Int("8"), Sum(Int("9"), Int("10"))))),
ISequence(Prior([Terminate(Sum(Int("11"), To(Int("12"), Stream())))]), ISequence(Prior([Terminate(Sum(Int("11"), DefTo(Int("12"))))]),
To(To(Int("13"), Stream()), Stream())) DefTo(DefTo(Int("13"))))
])) ]))
/** writes /** writes
3396727121313**/ 3396727121313**/