From 2e490650319e8daa2fdc056cae21bb494cc36b24 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Mon, 1 Feb 2021 01:46:31 +0000 Subject: [PATCH] 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 Reviewed-on: https://code.studioinfinity.org/glen/fostr/pulls/8 Co-Authored-By: Glen Whitney Co-Committed-By: Glen Whitney --- .gitignore | 3 +++ syntax/fostr.sdf3 | 5 ++++- tests/basic.spt | 33 +++++++++++++++++++++++++++++++++ trans/haskell.str | 36 +++++++++++++++++++++++------------- trans/javascript.str | 26 ++++++++++++++------------ trans/python.str | 23 +++++++++++++---------- trans/statics.stx | 1 + trans/util.str | 9 +++++++++ 8 files changed, 100 insertions(+), 36 deletions(-) create mode 100644 trans/util.str diff --git a/.gitignore b/.gitignore index cd46533..80a4a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,12 @@ /.polyglot.metaborg.yaml +.pydevproject + *.aterm /site tests/extracted/* tests/*.js tests/*.py tests/*.hs +adhoc* diff --git a/syntax/fostr.sdf3 b/syntax/fostr.sdf3 index 0f52846..b454b2f 100644 --- a/syntax/fostr.sdf3 +++ b/syntax/fostr.sdf3 @@ -18,10 +18,13 @@ context-free syntax Ex.Stdio = Ex.Sum = {Ex "+"}+ Ex.Receives = [[Ex] << [Ex]] {left} + Ex.Enters = [[Ex] >> [Ex]] {left} + Ex = <()> {bracket} context-free priorities - Ex.Sum + Ex.Enters + > Ex.Sum > Ex.Receives, // prevent cycle: no singletons diff --git a/tests/basic.spt b/tests/basic.spt index 6757d31..9b00d89 100644 --- a/tests/basic.spt +++ b/tests/basic.spt @@ -71,4 +71,37 @@ stdio << 72 + 87 + 33 << 291 /** 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: +```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 +``` **/ diff --git a/trans/haskell.str b/trans/haskell.str index afa5a6a..028a7bd 100644 --- a/trans/haskell.str +++ b/trans/haskell.str @@ -1,12 +1,20 @@ module haskell -imports libstrategolib signatures/- +imports libstrategolib signatures/- util signature constructors TopLevel: Ex -> Ex 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 stdio :: IO IOStream @@ -19,21 +27,23 @@ rules return temp main = do - [x]] + [p]return [c]] - hs: Stdio() -> $[stdio] - hs: Int(x) -> x - hs: Sum(x) -> $[sum [x]] - hs: Receives(x, y) -> $[[x] `receives` [y]] - hs: [] -> $<[]> - hs: [x | xs] -> $<[<x><xs>]> + hs: Stdio() -> ("stdio", "") + hs: Int(x) -> (x, "") + hs: Sum((c,p)) -> ($[sum [c]], p) + hs: Receives((c, p), (d, s)) -> ($[[c] `receives` [d]], (p,s)) + hs: Enters((c, p), (d, s)) -> (c,d,(p,s),"fos") + + hsenter: (x, s, p, v) -> (v, [$[[p]let [v] = [x]], "\n", + $[[s] `receives` [v]], "\n"]) + + hslist: x -> (x, x) + brack: x -> $<[]> strategies // wrap expression in a toplevel and then apply code generation - haskell = !TopLevel(); hs - - // translate each element of a list, prepending each with ',', and concatenate - hstail = foldr(!"", \ (x,y) -> $<, <x>> \) + haskell = !TopLevel(); bottomup(try(hs <+ hslist)) // Interface haskell code generation with editor services and file system to-haskell: (selected, _, _, path, project-path) -> (filename, result) diff --git a/trans/javascript.str b/trans/javascript.str index d720942..398c29e 100644 --- a/trans/javascript.str +++ b/trans/javascript.str @@ -1,5 +1,5 @@ module javascript -imports libstrategolib signatures/- +imports libstrategolib signatures/- util signature constructors @@ -7,23 +7,25 @@ signature rules js: TopLevel(x) -> $[const Stdio = { - receives: v => { process.stdout.write(String(v)); return Stdio; } + receives: v => { process.stdout.write(String(v)); return Stdio; }, } - [x]] + function forwards(data, strm) { + strm.receives(data); + return data; + } + [x]] js: Stdio() -> $[Stdio] js: Int(x) -> x - js: Sum(x) -> $[[x].reduce((v,w) => v+w)] - js: Receives(x, y) -> $[[x].receives([y])] - js: [] -> $<[]> - js: [x | xs] -> $<[<x><xs>]> + js: Sum(x) -> $[[x].reduce((v,w) => v+w)] + js: Receives(x, y) -> $[[x].receives([y])] + js: Enters(x, y) -> $[forwards([x],[y])] + + jslist: x -> $<[<x>]> strategies - // wrap expression in a toplevel and then apply code generation - javascript = !TopLevel(); js - - // translate each element of a list, prepending each with ',', and concatenate - jstail = foldr(!"", \ (x,y) -> $<, <x>> \) + // wrap expression in a toplevel, then generate code from bottom up + javascript = !TopLevel(); bottomup(try(js <+ jslist)) // Interface javascript code generation with editor services and file system to-javascript: (selected, _, _, path, project-path) -> (filename, result) diff --git a/trans/python.str b/trans/python.str index cea2202..85b845b 100644 --- a/trans/python.str +++ b/trans/python.str @@ -1,32 +1,35 @@ module python -imports libstrategolib signatures/- +imports libstrategolib signatures/- util signature constructors TopLevel: Ex -> Ex rules + py: TopLevel(x) -> $[import sys class StdioC: def receives(self, v): print(v, file=sys.stdout, end='') return self + def forwards(data,strm): + strm.receives(data) + return data Stdio = StdioC() - [x]] + [x]] py: Stdio() -> $[Stdio] py: Int(x) -> x - py: Sum(x) -> $[sum([x])] - py: Receives(x, y) -> $[[x].receives([y])] - py: [] -> $<[]> - py: [x | xs] -> $<[<x><xs>]> + py: Sum(x) -> $[sum([x])] + py: Receives(x, y) -> $[[x].receives([y])] + py: Enters(x, y) -> $[forwards([x],[y])] + + pylist: x -> $<[<x>]> strategies - // wrap expression in a toplevel and then apply code generation - python = !TopLevel(); py - // translate each element of a list, prepending each with ',', and concatenate - pytail = foldr(!"", \ (x,y) -> $[, [x][y]] \) + // wrap with a toplevel, then generate code from the bottom up + python = !TopLevel(); bottomup(try(py <+ pylist)) // Interface python code generation with editor services and file system to-python: (selected, _, _, path, project-path) -> (filename, result) diff --git a/trans/statics.stx b/trans/statics.stx index b7385e2..fcd0c7f 100644 --- a/trans/statics.stx +++ b/trans/statics.stx @@ -10,6 +10,7 @@ rules // single-file entry point programOk(Sum(_)). programOk(Receives(_,_)). + programOk(Enters(_,_)). rules // multi-file entry point diff --git a/trans/util.str b/trans/util.str new file mode 100644 index 0000000..eee9663 --- /dev/null +++ b/trans/util.str @@ -0,0 +1,9 @@ +module util +imports libstrategolib + +rules + join(|infix) : [] -> "" + join(|infix) : [x | xs] -> $[[x][xs]] + + prejoin(|infix) : [] -> "" + prejoin(|infix) : [x | xs] -> $[[infix][x][xs]]