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 There seems only to be one way to start a tour like this. So here goes: ```fostr **/ /** 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**/ /** md ``` At the moment, there are only two ways to run a file containing fostr code (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, 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/hw.py extract: start: 'Stdio\s=' !} ``` 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 and OCaml code generation are 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, 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 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 192291**/ /** md ``` 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 `>>>` 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) >>> ]] /* **/ parse to TopLevel( 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 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 [[ <<< 7 + 8 >>> + 9 ]] /* **/ parse to TopLevel( DefGets(Sum(Sum(Int("7"), DefTo(Int("8"))), 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 >>> + 9) >> (<<< 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 [[ <<< 72 + 87 <<< 88 + 96 99 + 12 >>> ]] /* **/ parse to TopLevel(Sequence([ DefGets(Sum(Int("72"), Int("87"))), DefGets(Sum(Int("88"), Int("96"))), Sum(Int("99"), DefTo(Int("12"))) ])) /** 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 [[ <<< 72 + 87 <<< 88 + 96 99 + 12 >>> ]] /* **/ 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 [[ <<< 1 + 2; 3 >>> (4 + 5) >>>; stream << 6; <<< 7 <<< 8 + (9+10); 11 + 12 >>>; 13 >>> >>> ]] /* **/ parse to TopLevel(Sequence([ 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")))), 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**/ /** 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()) ])) test emit_several_default [[ <<< 1 + 2; 3 >>> (4 + 5) >>> >> stream; stream << 6; <<< 7 << 75 <<< 8 + (9+10); 11 + 12 >>>; 13 >>> >>> ]] parse succeeds /** writes 3399677527121313*/