feat: Implement enters operator >> (#8)

Also adds parenthesization of fostr expressions.
  Recasts code generation in terms of bottomup processing
  of a local strategy.

  Resolves #1.

Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Reviewed-on: glen/fostr#8
Co-Authored-By: Glen Whitney <glen@nobody@nowhere.net>
Co-Committed-By: Glen Whitney <glen@nobody@nowhere.net>
This commit is contained in:
Glen Whitney 2021-02-01 01:46:31 +00:00
parent 527f802793
commit 2e49065031
8 changed files with 100 additions and 36 deletions

3
.gitignore vendored
View File

@ -10,9 +10,12 @@
/.polyglot.metaborg.yaml /.polyglot.metaborg.yaml
.pydevproject
*.aterm *.aterm
/site /site
tests/extracted/* tests/extracted/*
tests/*.js tests/*.js
tests/*.py tests/*.py
tests/*.hs tests/*.hs
adhoc*

View File

@ -18,10 +18,13 @@ context-free syntax
Ex.Stdio = <stdio> Ex.Stdio = <stdio>
Ex.Sum = {Ex "+"}+ Ex.Sum = {Ex "+"}+
Ex.Receives = [[Ex] << [Ex]] {left} Ex.Receives = [[Ex] << [Ex]] {left}
Ex.Enters = [[Ex] >> [Ex]] {left}
Ex = <(<Ex>)> {bracket}
context-free priorities context-free priorities
Ex.Sum Ex.Enters
> Ex.Sum
> Ex.Receives, > Ex.Receives,
// prevent cycle: no singletons // prevent cycle: no singletons

View File

@ -71,4 +71,37 @@ stdio << 72 + 87 + 33 << 291
/** md /** md
``` ```
Running this program produces a nice palindromic output: "192291". 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:
```fostr
**/
/** md */ test enters_twice [[
(7 + 8 >> stdio + 9) >> stdio
]]/* **/ parse to
Enters(Sum([Int("7"), Enters(Int("8"), Stdio()), Int("9")]), Stdio())
/** writes
824**/
/* Extra tests not in the tour */
test receive_enter [[
stdio << (7 + 8 >> stdio + 9)
]]/* **/ parse to
Receives(Stdio(), Sum([Int("7"), Enters(Int("8"), Stdio()), Int("9")]))
/** writes
824**/
test enter_receive [[
(7 + 8 >> stdio + 9) >> (stdio << 9 + 2)
]]/* **/ parse to
Enters(Sum([Int("7"),Enters(Int("8"),Stdio()),Int("9")]),
Receives(Stdio(),Sum([Int("9"),Int("2")])))
/** writes
81124**/
/** md
```
**/ **/

View File

@ -1,12 +1,20 @@
module haskell module haskell
imports libstrategolib signatures/- imports libstrategolib signatures/- util
signature signature
constructors constructors
TopLevel: Ex -> Ex TopLevel: Ex -> Ex
rules rules
hs: TopLevel(x) -> $[import System.IO /* Approach: Generate code from the bottom up.
At every node, we create a pair of the implementation and
necessary preamble of IO actions.
We concatenate preambles as we go up.
Finally, at the toplevel we emit the preamble before returning the
final value.
*/
hs: TopLevel((c,p)) -> $[import System.IO
data IOStream = StdIO data IOStream = StdIO
stdio :: IO IOStream stdio :: IO IOStream
@ -19,21 +27,23 @@ rules
return temp return temp
main = do main = do
[<hs>x]] [p]return [c]]
hs: Stdio() -> $[stdio] hs: Stdio() -> ("stdio", "")
hs: Int(x) -> x hs: Int(x) -> (x, "")
hs: Sum(x) -> $[sum [<hs>x]] hs: Sum((c,p)) -> ($[sum [c]], p)
hs: Receives(x, y) -> $[[<hs>x] `receives` [<hs>y]] hs: Receives((c, p), (d, s)) -> ($[[c] `receives` [d]], <conc-strings>(p,s))
hs: [] -> $<[]> hs: Enters((c, p), (d, s)) -> <hsenter>(c,d,<conc-strings>(p,s),<newname>"fos")
hs: [x | xs] -> $<[<<hs>x><<hstail>xs>]>
hsenter: (x, s, p, v) -> (v, <concat-strings>[$[[p]let [v] = [x]], "\n",
$[[s] `receives` [v]], "\n"])
hslist: x -> (<map(Fst); join(|", "); brack>x, <map(Snd); concat-strings>x)
brack: x -> $<[<x>]>
strategies strategies
// wrap expression in a toplevel and then apply code generation // wrap expression in a toplevel and then apply code generation
haskell = !TopLevel(<id>); hs haskell = !TopLevel(<id>); bottomup(try(hs <+ hslist))
// translate each element of a list, prepending each with ',', and concatenate
hstail = foldr(!"", \ (x,y) -> $<, <<hs>x><y>> \)
// 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)

View File

@ -1,5 +1,5 @@
module javascript module javascript
imports libstrategolib signatures/- imports libstrategolib signatures/- util
signature signature
constructors constructors
@ -7,23 +7,25 @@ signature
rules rules
js: TopLevel(x) -> $[const Stdio = { js: TopLevel(x) -> $[const Stdio = {
receives: v => { process.stdout.write(String(v)); return Stdio; } receives: v => { process.stdout.write(String(v)); return Stdio; },
} }
[<js>x]] function forwards(data, strm) {
strm.receives(data);
return data;
}
[x]]
js: Stdio() -> $[Stdio] js: Stdio() -> $[Stdio]
js: Int(x) -> x js: Int(x) -> x
js: Sum(x) -> $[[<js>x].reduce((v,w) => v+w)] js: Sum(x) -> $[[x].reduce((v,w) => v+w)]
js: Receives(x, y) -> $[[<js>x].receives([<js>y])] js: Receives(x, y) -> $[[x].receives([y])]
js: [] -> $<[]> js: Enters(x, y) -> $[forwards([x],[y])]
js: [x | xs] -> $<[<<js>x><<jstail>xs>]>
jslist: x -> $<[<<join(|", ")>x>]>
strategies strategies
// wrap expression in a toplevel and then apply code generation // wrap expression in a toplevel, then generate code from bottom up
javascript = !TopLevel(<id>); js javascript = !TopLevel(<id>); bottomup(try(js <+ jslist))
// translate each element of a list, prepending each with ',', and concatenate
jstail = foldr(!"", \ (x,y) -> $<, <<js>x><y>> \)
// 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)

View File

@ -1,32 +1,35 @@
module python module python
imports libstrategolib signatures/- imports libstrategolib signatures/- util
signature signature
constructors constructors
TopLevel: Ex -> Ex TopLevel: Ex -> Ex
rules rules
py: TopLevel(x) -> $[import sys py: TopLevel(x) -> $[import sys
class StdioC: class StdioC:
def receives(self, v): def receives(self, v):
print(v, file=sys.stdout, end='') print(v, file=sys.stdout, end='')
return self return self
def forwards(data,strm):
strm.receives(data)
return data
Stdio = StdioC() Stdio = StdioC()
[<py>x]] [x]]
py: Stdio() -> $[Stdio] py: Stdio() -> $[Stdio]
py: Int(x) -> x py: Int(x) -> x
py: Sum(x) -> $[sum([<py>x])] py: Sum(x) -> $[sum([x])]
py: Receives(x, y) -> $[[<py>x].receives([<py>y])] py: Receives(x, y) -> $[[x].receives([y])]
py: [] -> $<[]> py: Enters(x, y) -> $[forwards([x],[y])]
py: [x | xs] -> $<[<<py>x><<pytail>xs>]>
pylist: x -> $<[<<join(|", ")>x>]>
strategies strategies
// wrap expression in a toplevel and then apply code generation
python = !TopLevel(<id>); py
// translate each element of a list, prepending each with ',', and concatenate // wrap with a toplevel, then generate code from the bottom up
pytail = foldr(!"", \ (x,y) -> $[, [<py>x][y]] \) python = !TopLevel(<id>); bottomup(try(py <+ pylist))
// 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)

View File

@ -10,6 +10,7 @@ rules // single-file entry point
programOk(Sum(_)). programOk(Sum(_)).
programOk(Receives(_,_)). programOk(Receives(_,_)).
programOk(Enters(_,_)).
rules // multi-file entry point rules // multi-file entry point

9
trans/util.str Normal file
View File

@ -0,0 +1,9 @@
module util
imports libstrategolib
rules
join(|infix) : [] -> ""
join(|infix) : [x | xs] -> $[[x][<prejoin(|infix)>xs]]
prejoin(|infix) : [] -> ""
prejoin(|infix) : [x | xs] -> $[[infix][x][<prejoin(|infix)>xs]]