From f827c37baa137b0b32f608f5f853e1726a703d30 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 12 Mar 2021 21:49:34 -0800 Subject: [PATCH] feat: Complete stream extraction Also implements ++ string concatenation operator. Resolves #7, #18. --- bin/extract_tests.xsh | 8 ++++++++ bin/run_tests | 10 ++++++++-- mkdocs.yml | 2 +- tests/basic.spt | 7 ++++--- trans/haskell.str | 8 ++++++++ trans/javascript.str | 23 ++++++++++++++++++++--- trans/ocaml.str | 11 ++++++++--- trans/python.str | 2 +- 8 files changed, 58 insertions(+), 13 deletions(-) diff --git a/bin/extract_tests.xsh b/bin/extract_tests.xsh index 1d8c59f..422acb1 100644 --- a/bin/extract_tests.xsh +++ b/bin/extract_tests.xsh @@ -13,6 +13,9 @@ DESTINATION = 'tests/extracted' # Extension for extracted files: EXT = 'fos' +# Extension for desired input: +INP = 'in' + # Extension for expectations: EXP = 'expect' @@ -34,6 +37,11 @@ for path in TEST_LIST: expath = destdir / f"{name}.{EXT}" expath.write_text(example) echo Wrote @(expath) + im = re.search(r'/\*\*\s+accepts.*?\n([\s\S]*?)\*\*/', details[em.end():]) + if im: + ipath = destdir / f"{name}.{INP}" + ipath.write_text(im[1]) + echo " ...and" @(ipath) xm = re.search(r'/\*\*\s+writes.*?\n([\s\S]*?)\*\*/', details[em.end():]) if xm: xpath = destdir / f"{name}.{EXP}" diff --git a/bin/run_tests b/bin/run_tests index 90ccaa7..da49485 100755 --- a/bin/run_tests +++ b/bin/run_tests @@ -9,8 +9,14 @@ diffed=0 for dir in tests/extracted/*; do for file in $dir/*.$ext; do ((total++)) - $command $file > $file.out - if [[ $? -ne 0 ]]; then + if [[ -f ${file%.*}.in ]]; then + cat ${file%.*}.in | $command $file > $file.out + result=$? + else + $command $file > $file.out + result=$? + fi + if [[ $result -ne 0 ]]; then echo ERROR: $command $file failed. ((failed++)) else diff --git a/mkdocs.yml b/mkdocs.yml index e316a98..c2d5242 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,7 +9,7 @@ plugins: - search - semiliterate: ignore_folders: [target, lib] - exclude_extensions: ['.o', '.hi'] + exclude_extensions: ['.o', '.hi', '.cmi', '.cmo'] extract_standard_markdown: terminate: theme: diff --git a/tests/basic.spt b/tests/basic.spt index c6540b0..3d5a4a6 100644 --- a/tests/basic.spt +++ b/tests/basic.spt @@ -293,7 +293,7 @@ test emit_several_default [[ >>> ]] parse succeeds /** writes -3399677527121313*/ +3399677527121313**/ /** md ### Streams are bidirectional @@ -318,12 +318,13 @@ Kilroy **/ /** writes What is your name? -Hello, Kilroy**/ +Hello, Kilroy +**/ /** md ``` queries users for their name and then writes a customized greeting. It also -illustratesthe use of `++` for string concatenation, as opposed to `+` for +illustrates the use of `++` for string concatenation, as opposed to `+` for (numerical) addition. **/ diff --git a/trans/haskell.str b/trans/haskell.str index 0b08716..cdf4d55 100644 --- a/trans/haskell.str +++ b/trans/haskell.str @@ -23,6 +23,7 @@ rules import System.IO data IOStream = StdIO + -- Danger: These currently assume the stream is StdIO gets :: Show b => a -> b -> IO a gets s d = do putStr(show d) @@ -33,6 +34,8 @@ rules putStr(d) return s + emit s = getLine + main = do [()]return [val]] @@ -41,6 +44,7 @@ rules hs: (_, LitString(x)) -> x hs: (_, EscString(x)) -> x hs: (_, Sum(x, y)) -> $[([x] + [y])] + hs: (_, Concat(x, y)) -> $[([x] ++ [y])] hs: (Gets(_, xn), Gets(s, x)) -> v with v := "_fostr_get" @@ -52,6 +56,10 @@ rules hs_gets: (s, xn, x ) -> $[[s] [xn] [x]] hs_getOp = get-type; (?STRING() < !"`getsStr`" + !"`gets`") + hs: (_, Emits(s)) -> v + with v := "_fostr_emitted" + ; [$[[v] <- emit [s]]] + hs: (_, Terminate(x)) -> $[[x];;] hs: (_, Sequence(l)) -> l /* One drawback of using paramorphism is we have to handle lists diff --git a/trans/javascript.str b/trans/javascript.str index 3a847e2..9b596ad 100644 --- a/trans/javascript.str +++ b/trans/javascript.str @@ -3,24 +3,41 @@ imports libstrategolib signatures/- util rules js: TopLevel(x) -> $[// Fostr preamble + const _fostr_readline = require('readline'); + const _fostr_events = require('events'); + const _fostr_rl = _fostr_readline.createInterface({input: process.stdin}); const Stdio = { - gets: v => { process.stdout.write(String(v)); return Stdio; }, + gets: v => { process.stdout.write(String(v)); return Stdio; }, + emit: async () => { + const [line] = await _fostr_events.once(_fostr_rl, 'line'); + return line + "\n"; } } function to(data, strm) { strm.gets(data); return data; } + + const _fostr_body = async () => { // End of preamble - [x]] + [x] + + // Fostr coda + _fostr_rl.close() + } + _fostr_body(); + ] + with line := "[line]" js: Stream() -> $[Stdio] js: Int(x) -> x js: LitString(x) -> x js: EscString(x) -> x - js: Sum(x,y) -> $[[x] + [y]] + js: Sum(x, y) -> $[[x] + [y]] + js: Concat(x, y) -> $[[x] + [y]] js: Gets(x, y) -> $[[x].gets([y])] js: To(x, y) -> $[to([x],[y])] + js: Emits(x) -> $[(await [x].emit())] js: Terminate(x) -> x js: Sequence(l) -> l diff --git a/trans/ocaml.str b/trans/ocaml.str index 11fcdca..049dbf4 100644 --- a/trans/ocaml.str +++ b/trans/ocaml.str @@ -12,9 +12,10 @@ imports libstrategolib signatures/- util signature/TYPE analysis rules ml: (_, TopLevel(x)) -> $[(* fostr preamble *) - type stream = { getS: string -> stream } + type stream = { getS: string -> stream; emitS: unit -> string } let rec stdio = { - getS = (fun s -> print_string s; stdio) + getS = (fun s -> print_string s; stdio); + emitS = (fun () -> (read_line ()) ^ "\n"); };; (* End of preamble *) @@ -24,11 +25,15 @@ rules ml: (_, Int(x)) -> x ml: (_, LitString(x)) -> $[{|[x]|}] ml: (_, EscString(x)) -> x - ml: (_, Sum(x,y)) -> $[[x] + [y]] + ml: (_, Sum(x, y)) -> $[[x] + [y]] + ml: (_, Concat(x, y)) -> $[[x] ^ [y]] + ml: (Gets(_,yn), Gets(x, y)) -> $[([x]).getS ([(yn,y)])] ml: (To(xn,_), To(x, y)) -> $[let _fto = ([x]) in (ignore (([y]).getS ([(xn,"_fto")])); _fto)] + ml: (_, Emits(s)) -> $[[s].emitS ()] + ml: (_, Terminate(x)) -> x ml: (_, Sequence(l)) -> l diff --git a/trans/python.str b/trans/python.str index a0a2ae7..f42b1cb 100644 --- a/trans/python.str +++ b/trans/python.str @@ -9,7 +9,7 @@ rules print(v, file=sys.stdout, end='') return self def emit(self): - return input() + return input() + "\n" # Python inconsistently strips when using input def to(data,strm): strm.gets(data) return data