From f9c6e04c8ccb1b2dfc5d766755701d518597ae3a Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 19 Feb 2021 08:08:24 -0800 Subject: [PATCH] docs: Finally get the tour to start from the real helloworld Also improves the testing situation for the features to date. Resolves #17. --- tests/basic.spt | 151 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 50 deletions(-) diff --git a/tests/basic.spt b/tests/basic.spt index 494cafb..215c83f 100644 --- a/tests/basic.spt +++ b/tests/basic.spt @@ -16,15 +16,21 @@ 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: +There seems only to be one way to start a tour like this. So here goes: ```fostr **/ -/** md */ test emit_sum [[ -stream << 72 + 87 + 33 +/** md */ test hello_world [[ +<<< '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")))) /** writes 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 -(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: 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: ```python -{! ../tests/emit_sum.py extract: +{! ../tests/hw.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.) +It generates nearly identical code in +this simple example for Javascript (just with `"Hello, world!"` +in place of `r'Hello, world!'`), although it generates a different +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 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: +a value. So what's the value of that expression above? Well, for convenience, +the value of a stream receiving an item is (generally) just the stream back +again. That way we can use the general (left-associative) +`_stream_ << _value_` operator to chain insertions into a stream: ```fostr **/ -/** md */ test emit_twice [[ -stream << 72 + 87 + 33 << 291 +/** md */ test state_obvious [[ +<<< '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( Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291"))) /** writes @@ -77,24 +123,28 @@ parse to TopLevel( /** 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": +with `>>>` for sending to the default stream or `>>` in general; these forms +(generally) return the value sent, so the following writes "824": ```fostr **/ /** md */ test enters_twice [[ -(7 + 8 >> stream + 9) >> stream +(7 + 8 >> stream + 9) >>> ]] /* **/ 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 824**/ /** 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 @@ -104,13 +154,13 @@ lines are indented from the start of the initial line: **/ /** md */ test receive_enter_break [[ -stream << +<<< 7 - + 8 >> stream + + 8 >>> + 9 ]] /* **/ 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 824**/ @@ -121,8 +171,8 @@ parse to TopLevel( **/ /** md */ test enter_receive_bad_continuation [[ -(7 + 8 >> stream + 9) ->> (stream << 9 + 2) +(7 + 8 >>> + 9) +>> (<<< 9 + 2) ]] /* **/ parse fails @@ -145,16 +195,17 @@ lines are evaluated in sequence. For example, the program **/ /** md */ test emit_thrice [[ - stream << 72 + 87 - stream << 88 + <<< 72 + 87 + <<< 88 + 96 - 99 + 12 >> - stream + 99 + 12 + >>> + ]] /* **/ 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())) + DefGets(Sum(Int("72"), Int("87"))), + DefGets(Sum(Int("88"), Int("96"))), + Sum(Int("99"), DefTo(Int("12"))) ])) /** writes 15918412**/ @@ -169,10 +220,10 @@ in sequence align at the left; e.g., the following fails to parse: **/ /** md */ test emit_thrice_bad_alignment [[ - stream << 72 + 87 -stream << 88 + <<< 72 + 87 +<<< 88 + 96 - 99 + 12 >> stream + 99 + 12 >>> ]] /* **/ parse fails @@ -187,23 +238,23 @@ are so terminated. So the following is OK: **/ /** md */ test emit_several [[ - stream << 1 + 2; 3 >> stream - (4 + 5) >> stream; stream << 6; - stream << 7 - stream << 8 + <<< 1 + 2; 3 >>> + (4 + 5) >>>; stream << 6; + <<< 7 + <<< 8 + (9+10); - 11 + 12 >> stream; 13 >> stream - >> stream + 11 + 12 >>>; 13 >>> + >>> ]] /* **/ 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()))]), + ISequence(Prior([Terminate(DefGets(Sum(Int("1"), Int("2"))))]), + DefTo(Int("3"))), + ISequence(Prior([Terminate(DefTo(Sum(Int("4"), Int("5"))))]), 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())) + DefGets(Int("7")), + Terminate(DefGets(Sum(Int("8"), Sum(Int("9"), Int("10"))))), + ISequence(Prior([Terminate(Sum(Int("11"), DefTo(Int("12"))))]), + DefTo(DefTo(Int("13")))) ])) /** writes 3396727121313**/