feat: sequencing of expressions with newline to same indent (#11)
All checks were successful
continuous-integration/drone/push Build is passing
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>
This commit is contained in:
parent
c4d3f66c51
commit
991976d3a8
17
README.md
17
README.md
@ -8,9 +8,12 @@ dimensions.
|
|||||||
|
|
||||||
So I embarked on this project to see if I could produce as comfortable a
|
So I embarked on this project to see if I could produce as comfortable a
|
||||||
language as possible to work in, given that I inevitably will be doing a
|
language as possible to work in, given that I inevitably will be doing a
|
||||||
bunch of coding. The language will be
|
bunch of coding. The language will be centrally organized around the
|
||||||
organized around (unary) ++f++unctions, (binary) ++o++perators, and
|
concept of "streams" (somewhat in the spirit of
|
||||||
(nullary) ++str++eams, hence the name "fostr".
|
[streem](https://github.com/matz/streem) and/or
|
||||||
|
[Orc](http://orc.csres.utexas.edu/index.shtml)). In fact all higher-type
|
||||||
|
entities will be cast in terms of streams, or in slogan form, "++f++unctions
|
||||||
|
and (binary) ++o++perators are ++str++eams" (hence the name "fostr").
|
||||||
|
|
||||||
Other guiding principles:
|
Other guiding principles:
|
||||||
|
|
||||||
@ -24,11 +27,9 @@ Other guiding principles:
|
|||||||
the language design from the ground up, it can be kept both effective and
|
the language design from the ground up, it can be kept both effective and
|
||||||
natural.
|
natural.
|
||||||
|
|
||||||
* Code uses functions all the time. So needless to say, functions should be
|
* fostr code uses streams (and their specializations to functions and
|
||||||
first-class entities that are exceptionally easy to create, pass around,
|
operators) all the time, so they are first-class entities that are easy
|
||||||
etc.
|
to create, pass around, compose, etc.
|
||||||
|
|
||||||
* And true to the name, operators and streams should be just as easy to handle.
|
|
||||||
|
|
||||||
* Try to keep the constructs available as simple to reason about as possible,
|
* Try to keep the constructs available as simple to reason about as possible,
|
||||||
and practical to use. So side effects are OK, and it should be clear when
|
and practical to use. So side effects are OK, and it should be clear when
|
||||||
|
@ -19,6 +19,7 @@ language:
|
|||||||
sdf:
|
sdf:
|
||||||
pretty-print: fostr
|
pretty-print: fostr
|
||||||
sdf2table: java
|
sdf2table: java
|
||||||
|
jsglr-version: layout-sensitive
|
||||||
placeholder:
|
placeholder:
|
||||||
prefix: "$"
|
prefix: "$"
|
||||||
stratego:
|
stratego:
|
||||||
|
@ -10,28 +10,34 @@ context-free start-symbols
|
|||||||
|
|
||||||
context-free sorts
|
context-free sorts
|
||||||
|
|
||||||
Start Ex
|
Start LineSeq Line Ex
|
||||||
|
|
||||||
context-free syntax
|
context-free syntax
|
||||||
|
|
||||||
Start.TopLevel = prog:Ex {layout(offside prog)}
|
Start.TopLevel = LineSeq
|
||||||
|
|
||||||
|
LineSeq = <<ln:Ex>> {layout(offside ln)}
|
||||||
|
LineSeq.Sequence = sq:Ex+ {layout(align-list sq)}
|
||||||
|
|
||||||
|
Ex+ = Ex+ ln:Ex {layout(offside ln)}
|
||||||
|
|
||||||
Ex.Int = INT
|
Ex.Int = INT
|
||||||
Ex.Stdio = <stdio>
|
Ex.Stream = <stream>
|
||||||
Ex.Sum = {Ex "+"}+
|
Ex.Sum = [[Ex] + [Ex]] {left}
|
||||||
Ex.Receives = [[Ex] << [Ex]] {left}
|
Ex.Gets = [[Ex] << [Ex]] {left}
|
||||||
Ex.Enters = [[Ex] >> [Ex]] {left}
|
Ex.To = [[Ex] >> [Ex]] {left}
|
||||||
|
|
||||||
Ex = <(<Ex>)> {bracket}
|
Ex = <(<Ex>)> {bracket}
|
||||||
|
|
||||||
context-free priorities
|
context-free priorities
|
||||||
|
|
||||||
Ex.Enters
|
Ex.To
|
||||||
> Ex.Sum
|
> Ex.Sum
|
||||||
> Ex.Receives,
|
> Ex.Gets,
|
||||||
|
|
||||||
// prevent cycle: no singletons
|
// prevent cycle: no singletons
|
||||||
Ex.Sum <0> .> {Ex "+"}+ = Ex,
|
LineSeq.Sequence <0> .> Ex+ = Ex,
|
||||||
|
|
||||||
// flat: no Sum immediately in Sum:
|
// flat: No LineSeq immediately in LineSeq
|
||||||
{Ex "+"}+ = Ex <0> .> Ex.Sum,
|
Ex+ = Ex <0> .> LineSeq.Sequence,
|
||||||
{Ex "+"}+ = {Ex "+"}+ "+" Ex <2> .> Ex.Sum
|
Ex+ = Ex+ Ex <1> .> LineSeq.Sequence
|
||||||
|
@ -13,9 +13,9 @@ that writes the sum of the ASCII codes for 'H', 'W', and '!' to standard output:
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test emit_sum [[
|
/** md */ test emit_sum [[
|
||||||
stdio << 72 + 87 + 33
|
stream << 72 + 87 + 33
|
||||||
]]/* **/ parse to TopLevel(Receives(Stdio(),
|
]]/* **/ parse to TopLevel(Gets(Stream(),
|
||||||
Sum([Int("72"), Int("87"), Int("33")])))
|
Sum(Sum(Int("72"), Int("87")), Int("33"))))
|
||||||
/** writes
|
/** writes
|
||||||
192**/
|
192**/
|
||||||
|
|
||||||
@ -40,32 +40,27 @@ For example, this snippet generates the following Python:
|
|||||||
start: 'Stdio\s='
|
start: 'Stdio\s='
|
||||||
!}
|
!}
|
||||||
```
|
```
|
||||||
(which writes "192" to standard output), or this non-idiomatic, inefficient, but
|
(which writes "192" to standard output); it also generates identical code in
|
||||||
working Javascript:
|
this simple example for
|
||||||
```javascript
|
Javascript, although it generates a different preamble defining Stdio in each
|
||||||
{! ../tests/emit_sum.js extract:
|
case. (Haskell code generation is also currently supported.)
|
||||||
start: '^}'
|
|
||||||
!}
|
|
||||||
```
|
|
||||||
In either case, there's also a preamble defining Stdio that's generated.
|
|
||||||
(Haskell code generation is also currently supported.)
|
|
||||||
|
|
||||||
### 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, `stdio` is our
|
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
|
first example of a stream, and for convenience, the value of a stream
|
||||||
receiving an item is just the stream back again. The `<<` operator is also
|
receiving an item is (usually) just the stream back again. The `<<` operator
|
||||||
left-associative, so that way we can 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 emit_twice [[
|
||||||
stdio << 72 + 87 + 33 << 291
|
stream << 72 + 87 + 33 << 291
|
||||||
]]/* **/ parse to TopLevel(
|
]]/* **/ parse to TopLevel(
|
||||||
Receives(Receives(Stdio(), Sum([Int("72"), Int("87"), Int("33")])),
|
Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291")))
|
||||||
Int("291")))
|
|
||||||
/** writes
|
/** writes
|
||||||
192291**/
|
192291**/
|
||||||
|
|
||||||
@ -75,14 +70,14 @@ 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:
|
with `>>`; both forms return the first argument, so the following writes "824":
|
||||||
```fostr
|
```fostr
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test enters_twice [[
|
/** md */ test enters_twice [[
|
||||||
(7 + 8 >> stdio + 9) >> stdio
|
(7 + 8 >> stream + 9) >> stream
|
||||||
]]/* **/ parse to TopLevel(
|
]]/* **/ parse to TopLevel(
|
||||||
Enters(Sum([Int("7"), Enters(Int("8"), Stdio()), Int("9")]), Stdio()))
|
To(Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9")), Stream()))
|
||||||
/** writes
|
/** writes
|
||||||
824**/
|
824**/
|
||||||
|
|
||||||
@ -97,12 +92,12 @@ lines are indented from the start of the initial line:
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test receive_enter_break [[
|
/** md */ test receive_enter_break [[
|
||||||
stdio <<
|
stream <<
|
||||||
7
|
7
|
||||||
+ 8 >> stdio
|
+ 8 >> stream
|
||||||
+ 9
|
+ 9
|
||||||
]]/* **/ parse to TopLevel(
|
]]/* **/ parse to TopLevel(
|
||||||
Receives(Stdio(), Sum([Int("7"), Enters(Int("8"), Stdio()), Int("9")])))
|
Gets(Stream(), Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9"))))
|
||||||
/** writes
|
/** writes
|
||||||
824**/
|
824**/
|
||||||
|
|
||||||
@ -113,19 +108,56 @@ stdio <<
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test enter_receive_bad_break [[
|
/** md */ test enter_receive_bad_break [[
|
||||||
(7 + 8 >> stdio + 9)
|
(7 + 8 >> stream + 9)
|
||||||
>> (stdio << 9 + 2)
|
>> (stream << 9 + 2)
|
||||||
]] /* **/ parse fails
|
]] /* **/ parse fails
|
||||||
|
|
||||||
/* Extra tests not in the tour */
|
/* Extra tests not in the tour */
|
||||||
test enter_receive [[
|
test enter_receive [[
|
||||||
(7 + 8 >> stdio + 9) >> (stdio << 9 + 2)
|
(7 + 8 >> stream + 9) >> (stream << 9 + 2)
|
||||||
]]/* **/ parse to TopLevel(
|
]]/* **/ parse to TopLevel(
|
||||||
Enters(Sum([Int("7"),Enters(Int("8"),Stdio()),Int("9")]),
|
To(Sum(Sum(Int("7"),To(Int("8"),Stream())),Int("9")),
|
||||||
Receives(Stdio(),Sum([Int("9"),Int("2")]))))
|
Gets(Stream(),Sum(Int("9"),Int("2")))))
|
||||||
/** writes
|
/** writes
|
||||||
81124**/
|
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
|
/** md
|
||||||
```
|
```
|
||||||
**/
|
**/
|
||||||
|
@ -1 +1 @@
|
|||||||
stdio << 72 + 87 + 33
|
stream << 72 + 87 + 33
|
||||||
|
5
tests/emit_thrice.fos
Normal file
5
tests/emit_thrice.fos
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
stream << 72 + 87
|
||||||
|
stream << 88
|
||||||
|
+ 96
|
||||||
|
99 + 12 >>
|
||||||
|
stream
|
@ -17,33 +17,32 @@ rules
|
|||||||
hs: TopLevel((c,p)) -> $[import System.IO
|
hs: TopLevel((c,p)) -> $[import System.IO
|
||||||
data IOStream = StdIO
|
data IOStream = StdIO
|
||||||
|
|
||||||
stdio :: IO IOStream
|
gets :: Show b => a -> b -> IO a
|
||||||
stdio = return StdIO
|
gets s d = do
|
||||||
|
|
||||||
receives :: Show b => IO a -> b -> IO a
|
|
||||||
receives s d = do
|
|
||||||
temp <- s
|
|
||||||
putStr(show d)
|
putStr(show d)
|
||||||
return temp
|
return s
|
||||||
|
|
||||||
main = do
|
main = do
|
||||||
[p]return [c]]
|
[p]return [c]]
|
||||||
|
|
||||||
hs: Stdio() -> ("stdio", "")
|
hs: Stream() -> ("StdIO", "")
|
||||||
hs: Int(x) -> (x, "")
|
hs: Int(x) -> (x, "")
|
||||||
hs: Sum((c,p)) -> ($[sum [c]], p)
|
hs: Sum( (c, p), (d, q)) -> ($[([c] + [d])], <conc-strings>(p,q))
|
||||||
hs: Receives((c, p), (d, s)) -> ($[[c] `receives` [d]], <conc-strings>(p,s))
|
hs: Gets((c, p), (d, q)) -> <hsget>(c,d,<conc-strings>(p,q),<newname>"fosgt")
|
||||||
hs: Enters((c, p), (d, s)) -> <hsenter>(c,d,<conc-strings>(p,s),<newname>"fos")
|
|
||||||
|
|
||||||
hsenter: (x, s, p, v) -> (v, <concat-strings>[$[[p]let [v] = [x]], "\n",
|
hsget: (s, x, p, v) -> (v, <concat-strings>[p, $[[v] <- [s] `gets` [x]],
|
||||||
$[[s] `receives` [v]], "\n"])
|
"\n"])
|
||||||
|
|
||||||
hslist: x -> (<map(Fst); join(|", "); brack>x, <map(Snd); concat-strings>x)
|
hs: To( (c, p), (d, q)) -> <hsto>(c,d,<conc-strings>(p,q),<newname>"fosto")
|
||||||
brack: x -> $<[<x>]>
|
|
||||||
|
hsto: (x, s, p, v) -> (v, <concat-strings>[p, $[let [v] = [x]], "\n",
|
||||||
|
$[[s] `gets` [v]], "\n"])
|
||||||
|
|
||||||
|
hs: Sequence(l) -> (<last; Fst>l, <map(Snd); concat-strings>l)
|
||||||
|
|
||||||
strategies
|
strategies
|
||||||
|
|
||||||
haskell = bottomup(try(hs <+ hslist))
|
haskell = bottomup(try(hs))
|
||||||
|
|
||||||
// Interface haskell code generation with editor services and file system
|
// Interface haskell code generation with editor services and file system
|
||||||
to-haskell: (selected, _, _, path, project-path) -> (filename, result)
|
to-haskell: (selected, _, _, path, project-path) -> (filename, result)
|
||||||
|
@ -7,25 +7,24 @@ signature
|
|||||||
|
|
||||||
rules
|
rules
|
||||||
js: TopLevel(x) -> $[const Stdio = {
|
js: TopLevel(x) -> $[const Stdio = {
|
||||||
receives: v => { process.stdout.write(String(v)); return Stdio; },
|
gets: v => { process.stdout.write(String(v)); return Stdio; },
|
||||||
}
|
}
|
||||||
function forwards(data, strm) {
|
function to(data, strm) {
|
||||||
strm.receives(data);
|
strm.gets(data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
[x]]
|
[x]]
|
||||||
|
|
||||||
js: Stdio() -> $[Stdio]
|
js: Stream() -> $[Stdio]
|
||||||
js: Int(x) -> x
|
js: Int(x) -> x
|
||||||
js: Sum(x) -> $[[x].reduce((v,w) => v+w)]
|
js: Sum(x,y) -> $[[x] + [y]]
|
||||||
js: Receives(x, y) -> $[[x].receives([y])]
|
js: Gets(x, y) -> $[[x].gets([y])]
|
||||||
js: Enters(x, y) -> $[forwards([x],[y])]
|
js: To(x, y) -> $[to([x],[y])]
|
||||||
|
js: Sequence(l) -> <join(|";\n")>l
|
||||||
jslist: x -> $<[<<join(|", ")>x>]>
|
|
||||||
|
|
||||||
strategies
|
strategies
|
||||||
|
|
||||||
javascript = bottomup(try(js <+ jslist))
|
javascript = bottomup(try(js))
|
||||||
|
|
||||||
// Interface javascript code generation with editor services and file system
|
// Interface javascript code generation with editor services and file system
|
||||||
to-javascript: (selected, _, _, path, project-path) -> (filename, result)
|
to-javascript: (selected, _, _, path, project-path) -> (filename, result)
|
||||||
|
@ -9,26 +9,25 @@ rules
|
|||||||
|
|
||||||
py: TopLevel(x) -> $[import sys
|
py: TopLevel(x) -> $[import sys
|
||||||
class StdioC:
|
class StdioC:
|
||||||
def receives(self, v):
|
def gets(self, v):
|
||||||
print(v, file=sys.stdout, end='')
|
print(v, file=sys.stdout, end='')
|
||||||
return self
|
return self
|
||||||
def forwards(data,strm):
|
def to(data,strm):
|
||||||
strm.receives(data)
|
strm.gets(data)
|
||||||
return data
|
return data
|
||||||
Stdio = StdioC()
|
Stdio = StdioC()
|
||||||
[x]]
|
[x]]
|
||||||
|
|
||||||
py: Stdio() -> $[Stdio]
|
py: Stream() -> $[Stdio]
|
||||||
py: Int(x) -> x
|
py: Int(x) -> x
|
||||||
py: Sum(x) -> $[sum([x])]
|
py: Sum(x,y) -> $[[x] + [y]]
|
||||||
py: Receives(x, y) -> $[[x].receives([y])]
|
py: Gets(x, y) -> $[[x].gets([y])]
|
||||||
py: Enters(x, y) -> $[forwards([x],[y])]
|
py: To(x, y) -> $[to([x],[y])]
|
||||||
|
py: Sequence(l) -> <join(|"\n")>l
|
||||||
pylist: x -> $<[<<join(|", ")>x>]>
|
|
||||||
|
|
||||||
strategies
|
strategies
|
||||||
|
|
||||||
python = bottomup(try(py <+ pylist))
|
python = bottomup(try(py))
|
||||||
|
|
||||||
// Interface python code generation with editor services and file system
|
// Interface python code generation with editor services and file system
|
||||||
to-python: (selected, _, _, path, project-path) -> (filename, result)
|
to-python: (selected, _, _, path, project-path) -> (filename, result)
|
||||||
|
@ -3,7 +3,7 @@ imports libstrategolib
|
|||||||
|
|
||||||
rules
|
rules
|
||||||
join(|infix) : [] -> ""
|
join(|infix) : [] -> ""
|
||||||
join(|infix) : [x | xs] -> $[[x][<prejoin(|infix)>xs]]
|
join(|infix) : [x | xs] -> <conc-strings>(x, <prejoin(|infix)>xs)
|
||||||
|
|
||||||
prejoin(|infix) : [] -> ""
|
prejoin(|infix) : [] -> ""
|
||||||
prejoin(|infix) : [x | xs] -> $[[infix][x][<prejoin(|infix)>xs]]
|
prejoin(|infix) : [x | xs] -> <concat-strings>[infix,x,<prejoin(|infix)>xs]
|
||||||
|
Loading…
Reference in New Issue
Block a user