diff --git a/.drone.yml b/.drone.yml index 90b928b..452f163 100644 --- a/.drone.yml +++ b/.drone.yml @@ -29,18 +29,7 @@ steps: - java -jar /drone/lib/spt/org.metaborg.spt.cmd/target/org.metaborg.spt.cmd* -l . -s /drone/lib/spt/org.metaborg.meta.lang.spt -t tests - mkdir -p lib - curl -o lib/sunshine.jar -L 'http://artifacts.metaborg.org/service/local/artifact/maven/redirect?r=snapshots&g=org.metaborg&a=org.metaborg.sunshine2&v=LATEST' - - name: setup_gen - image: gcc - volumes: - - name: m2 - path: /root/.m2 - commands: - - git clone https://github.com/facebook/nailgun.git - - cd nailgun - - make - - cd ../bin - - ln -s ../nailgun/nailgun-client/target/ng . - - cd .. + - bin/fosgen tests/emit_sum.fos - name: extract_tests image: xonsh/xonsh commands: @@ -52,8 +41,7 @@ steps: path: /drone/lib - name: m2 path: /root/.m2 - commands: # Note we first make sure that fosgen is working - - bin/fosgen -d tests/emit_sum.fos + commands: - bin/generate_test_code - name: python_tests image: python:slim @@ -67,13 +55,6 @@ steps: image: haskell commands: - bin/run_tests runghc hs - - name: ocaml_tests - image: ocaml/opam - commands: - - ls -als tests/extracted - - opam init - - eval $(opam env) - - bin/run_tests ocaml ml volumes: - name: lib diff --git a/.gitignore b/.gitignore index 35e6d95..80a4a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,16 +12,10 @@ .pydevproject -a.out - *.aterm /site -bin/ng tests/extracted/* tests/*.js tests/*.py tests/*.hs -tests/*.ml -tests/*.cmi -tests/*.cmo adhoc* diff --git a/bin/extract_tests.xsh b/bin/extract_tests.xsh index 422acb1..7cf131a 100644 --- a/bin/extract_tests.xsh +++ b/bin/extract_tests.xsh @@ -13,16 +13,12 @@ DESTINATION = 'tests/extracted' # Extension for extracted files: EXT = 'fos' -# Extension for desired input: -INP = 'in' - # Extension for expectations: EXP = 'expect' for path in TEST_LIST: destdir = pf"{DESTINATION}/{path.stem}" mkdir -p @(destdir) - chmod ugo+rwx @(destdir) contents = path.read_text() tests = re.split(r'test\s*(.+?)\s*\[\[.*?\n', contents)[1:] testit = iter(tests) @@ -37,11 +33,6 @@ 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/fosgen b/bin/fosgen index b03656b..c5af48b 100755 --- a/bin/fosgen +++ b/bin/fosgen @@ -5,7 +5,6 @@ erro() { printf "%s\n" "$*" >&2; } ##### Set defaults: SUPPRESS_ERR=YES -USE_NAILGUN=YES LANGUAGE=Python ##### Extract command line options: @@ -15,23 +14,18 @@ do -h|--help) echo echo "Usage:" - echo " fosgen [-d] [-j] [-l LANGUAGE] FILE" + echo " fosgen [-d] [-l LANGUAGE] FILE" echo echo "Writes to standard output the code generated from the fostr" echo "program in FILE, targeting the specified LANGUAGE (which" echo "defaults to Python)." echo echo "The -d option writes diagnostic output to standard error." - echo "The -j option uses the Spoofax Sunshine JAR directly, rather" - echo "than via nailgun." exit ;; -d) SUPPRESS_ERR='' ;; - -j) - USE_NAILGUN='' - ;; -l) shift LANGUAGE="$1" @@ -73,17 +67,5 @@ then exec 2>/dev/null fi -if [[ $USE_NAILGUN ]] -then - if [[ $SUPPRESS_ERR ]] - then - $BINDIR/let_sun_shine - else - $BINDIR/let_sun_shine noisy - fi - $BINDIR/ng sunshine transform -p $PROJDIR -n $LANGUAGE -i $PROGRAM - exit $? -fi - java -jar $SUNJAR transform -p $PROJDIR -l $PROJDIR -l $MVN_REPO -n $LANGUAGE -i $PROGRAM exit $? diff --git a/bin/generate_test_code b/bin/generate_test_code index 0d151a8..2c443d6 100755 --- a/bin/generate_test_code +++ b/bin/generate_test_code @@ -4,7 +4,7 @@ failed=0 for dir in tests/extracted/*; do for file in $dir/*.fos; do - for language in Python Javascript Haskell OCaml; do + for language in Python Javascript Haskell; do echo bin/fosgen -l ${language%.*} $file ... bin/fosgen -l $language $file if [[ $? -ne 0 ]]; then diff --git a/bin/let_sun_shine b/bin/let_sun_shine deleted file mode 100755 index cb3c67a..0000000 --- a/bin/let_sun_shine +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# Helper for fosgen, not intended to be used directly -# With an argument, print diagnostic output - -BINDIR=$(dirname $BASH_SOURCE) - -if $BINDIR/ng sunshine --help -then - if [[ $1 ]] - then - echo "sun already shining." - fi -else - if [[ $1 ]] - then - echo "disperse the clouds." - fi - SUNJAR="$BINDIR/../lib/sunshine.jar" - PROJDIR="$BINDIR/.." - if [[ ! $MVN_REPO ]]; then - MVN_REPO="$HOME/.m2/repository" - fi - if [[ ! -d $MVN_REPO ]]; then - MVN_REPO="/root/.m2/repository" - fi - if [[ ! -d $MVN_REPO ]]; then - echo "Cannot find your Maven repository. Please set environment variable" - echo "MVN_REPO to its full path and re-run." - exit 1 - fi - if [[ $1 ]] - then - java -jar $SUNJAR server & - else - java -jar $SUNJAR server >/dev/null 2>&1 & - fi - sleep 5 - $BINDIR/ng sunshine load -l $PROJDIR -l $MVN_REPO -fi diff --git a/bin/run_tests b/bin/run_tests index da49485..90ccaa7 100755 --- a/bin/run_tests +++ b/bin/run_tests @@ -9,14 +9,8 @@ diffed=0 for dir in tests/extracted/*; do for file in $dir/*.$ext; do ((total++)) - 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 + $command $file > $file.out + if [[ $? -ne 0 ]]; then echo ERROR: $command $file failed. ((failed++)) else diff --git a/editor/Generation.esv b/editor/Generation.esv index 9e5051c..3d71fe2 100644 --- a/editor/Generation.esv +++ b/editor/Generation.esv @@ -4,4 +4,3 @@ menus action: "Python" = to-python action: "Javascript" = to-javascript action: "Haskell" = to-haskell - action: "OCaml" = to-ocaml diff --git a/mkdocs.yml b/mkdocs.yml index c2d5242..e316a98 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,7 +9,7 @@ plugins: - search - semiliterate: ignore_folders: [target, lib] - exclude_extensions: ['.o', '.hi', '.cmi', '.cmo'] + exclude_extensions: ['.o', '.hi'] extract_standard_markdown: terminate: theme: diff --git a/syntax/fostr.sdf3 b/syntax/fostr.sdf3 index f782466..8624b58 100644 --- a/syntax/fostr.sdf3 +++ b/syntax/fostr.sdf3 @@ -42,13 +42,10 @@ context-free syntax Ex.EscString = STRING Ex.Stream = Ex.Sum = < + > {left} - Ex.Concat = < ++ > {left} Ex.Gets = [[Ex] << [Ex]] {left} Ex.DefGets = [<<< [Ex]] Ex.To = [[Ex] >> [Ex]] {left} Ex.DefTo = [[Ex] >>>] - Ex.Emits = <!> - Ex.DefEmits = Ex = <()> {bracket} @@ -56,7 +53,7 @@ context-free priorities Ex.To > Ex.DefTo - > {Ex.Sum Ex.Concat} + > Ex.Sum > Ex.DefGets > Ex.Gets, diff --git a/tests/basic.spt b/tests/basic.spt index 3d5a4a6..215c83f 100644 --- a/tests/basic.spt +++ b/tests/basic.spt @@ -59,8 +59,8 @@ For example, this snippet generates the following Python: It generates nearly identical code in this simple example for Javascript (just with `"Hello, world!"` in place of `r'Hello, world!'`), although it generates a different -preamble defining Stdio for each language. (Currently, Haskell and OCaml -code generation are also supported.) +preamble defining Stdio for each language. (Currently, Haskell code +generation is also supported.) There's not much to break down in such a tiny program as this, but let's do it. The prefix operator `<<<` could be read as "the default stream receives...", @@ -108,7 +108,7 @@ again. That way we can use the general (left-associative) parse to TopLevel( Gets(Gets(Gets(Gets(DefGets(LitString("'Two and '")),Int("2")), LitString("' make '")),Sum(Int("2"),Int("2"))), - EscString("\".\n\""))) + EscString("\"./n\""))) /** writes Two and 2 make 4. **/ @@ -293,38 +293,4 @@ test emit_several_default [[ >>> ]] parse succeeds /** writes -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. -**/ +3399677527121313*/ diff --git a/trans/desugar.str b/trans/desugar.str index b22ebd8..7276f38 100644 --- a/trans/desugar.str +++ b/trans/desugar.str @@ -17,7 +17,6 @@ rules defStream: DefGets(x) -> Gets(Stream(), x) defStream: DefTo(x) -> To(x, Stream()) - defStream: DefEmits() -> Emits(Stream()) strategies diff --git a/trans/fostr.str b/trans/fostr.str index 91194d6..d9a585a 100644 --- a/trans/fostr.str +++ b/trans/fostr.str @@ -6,7 +6,6 @@ imports pp outline analysis - ocaml haskell javascript python diff --git a/trans/haskell.str b/trans/haskell.str index 0e0eb15..0e26050 100644 --- a/trans/haskell.str +++ b/trans/haskell.str @@ -10,7 +10,7 @@ rules rule. C) We will use bottomup-para to traverse the full AST with the transformation from A so that we have access to the original expression - (and can get the Statix-associated type when we need to). + (and get get the Statix-associated type when we need to). Hence the transformation in (A) must actually take a pair of an (original) term and a term with value strings at each child, and be certain to return a value string. @@ -23,7 +23,6 @@ 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) @@ -34,10 +33,6 @@ rules putStr(d) return s - emit s = do - l <- getLine - return (l ++ "\n") - main = do [()]return [val]] @@ -46,7 +41,6 @@ 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" @@ -58,10 +52,6 @@ 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 9b596ad..3a847e2 100644 --- a/trans/javascript.str +++ b/trans/javascript.str @@ -3,41 +3,24 @@ 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; }, - emit: async () => { - const [line] = await _fostr_events.once(_fostr_rl, 'line'); - return line + "\n"; } + gets: v => { process.stdout.write(String(v)); return Stdio; }, } function to(data, strm) { strm.gets(data); return data; } - - const _fostr_body = async () => { // End of preamble - [x] - - // Fostr coda - _fostr_rl.close() - } - _fostr_body(); - ] - with line := "[line]" + [x]] js: Stream() -> $[Stdio] js: Int(x) -> x js: LitString(x) -> x js: EscString(x) -> x - js: Sum(x, y) -> $[[x] + [y]] - js: Concat(x, y) -> $[[x] + [y]] + js: Sum(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 deleted file mode 100644 index 049dbf4..0000000 --- a/trans/ocaml.str +++ /dev/null @@ -1,66 +0,0 @@ -module ocaml -imports libstrategolib signatures/- util signature/TYPE analysis - -/* Note will use bottomup-para to traverse the full AST so that - we have access to the original expression (and can get the - Statix-associated type when we need to). - - This means that every one of our local rules must take a pair - of an original term and a term with every child replaced by - its generated code. - */ - -rules - ml: (_, TopLevel(x)) -> $[(* fostr preamble *) - type stream = { getS: string -> stream; emitS: unit -> string } - let rec stdio = { - getS = (fun s -> print_string s; stdio); - emitS = (fun () -> (read_line ()) ^ "\n"); - };; - (* End of preamble *) - - [x]] - - ml: (_, Stream()) -> $[stdio] - ml: (_, Int(x)) -> x - ml: (_, LitString(x)) -> $[{|[x]|}] - ml: (_, EscString(x)) -> x - 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 - - ml_seq: [x] -> x - ml_seq: [x | xs ] -> $[ignore ([x]); -[xs]] - - /* One drawback of using paramorphism is we have to handle lists - explicitly: - */ - ml: (_, []) -> [] - ml: (_, [x | xs]) -> [x | xs] - - /* Another drawback of using paramorphism is at the very leaves we have - to undouble the tuple: - */ - ml: (x, x) -> x where x - - ml_str: (node, code) -> $[[node]([code])] - -strategies - - ml_string_cast = get-type; (?INT() < !"string_of_int" + !"") - - ocaml = bottomup-para(try(ml)) - - // Interface ocaml code generation with editor services and file system - to-ocaml: (selected, _, _, path, project-path) -> (filename, result) - with filename := path - ; result := selected diff --git a/trans/python.str b/trans/python.str index f42b1cb..6449417 100644 --- a/trans/python.str +++ b/trans/python.str @@ -8,8 +8,6 @@ rules def gets(self, v): print(v, file=sys.stdout, end='') return self - def emit(self): - return input() + "\n" # Python inconsistently strips when using input def to(data,strm): strm.gets(data) return data @@ -23,10 +21,8 @@ rules py: LitString(x) -> $[r[x]] py: EscString(x) -> x py: Sum(x,y) -> $[[x] + [y]] - py: Concat(x,y) -> $[[x] + [y]] py: Gets(x, y) -> $[[x].gets([y])] py: To(x, y) -> $[to([x],[y])] - py: Emits(x) -> $[[x].emit()] py: Terminate(x) -> $[[x];] py: Sequence(l) -> l diff --git a/trans/statics.stx b/trans/statics.stx index ce99bf7..340a1df 100644 --- a/trans/statics.stx +++ b/trans/statics.stx @@ -222,13 +222,6 @@ This pattern lets us specify error messages. 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 ```