feat: Add OCaml code generation
Some checks failed
continuous-integration/drone/push Build is failing

Also start using nailgun to speed up code generation.

  Resolves #6.
This commit is contained in:
Glen Whitney 2021-02-28 22:21:56 -08:00
parent 380177b274
commit f789ed94fd
10 changed files with 128 additions and 4 deletions

View File

@ -29,6 +29,18 @@ 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 - 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 - 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' - 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: maven
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 - bin/fosgen tests/emit_sum.fos
- name: extract_tests - name: extract_tests
image: xonsh/xonsh image: xonsh/xonsh
@ -55,6 +67,10 @@ steps:
image: haskell image: haskell
commands: commands:
- bin/run_tests runghc hs - bin/run_tests runghc hs
- name: ocaml_tests
image: ocaml/opam
commands:
- bin/run_tests ocaml ml
volumes: volumes:
- name: lib - name: lib

6
.gitignore vendored
View File

@ -12,10 +12,16 @@
.pydevproject .pydevproject
a.out
*.aterm *.aterm
/site /site
bin/ng
tests/extracted/* tests/extracted/*
tests/*.js tests/*.js
tests/*.py tests/*.py
tests/*.hs tests/*.hs
tests/*.ml
tests/*.cmi
tests/*.cmo
adhoc* adhoc*

View File

@ -5,6 +5,7 @@ erro() { printf "%s\n" "$*" >&2; }
##### Set defaults: ##### Set defaults:
SUPPRESS_ERR=YES SUPPRESS_ERR=YES
USE_NAILGUN=YES
LANGUAGE=Python LANGUAGE=Python
##### Extract command line options: ##### Extract command line options:
@ -14,18 +15,23 @@ do
-h|--help) -h|--help)
echo echo
echo "Usage:" echo "Usage:"
echo " fosgen [-d] [-l LANGUAGE] FILE" echo " fosgen [-d] [-j] [-l LANGUAGE] FILE"
echo echo
echo "Writes to standard output the code generated from the fostr" echo "Writes to standard output the code generated from the fostr"
echo "program in FILE, targeting the specified LANGUAGE (which" echo "program in FILE, targeting the specified LANGUAGE (which"
echo "defaults to Python)." echo "defaults to Python)."
echo echo
echo "The -d option writes diagnostic output to standard error." 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 exit
;; ;;
-d) -d)
SUPPRESS_ERR='' SUPPRESS_ERR=''
;; ;;
-j)
USE_NAILGUN=''
;;
-l) -l)
shift shift
LANGUAGE="$1" LANGUAGE="$1"
@ -67,5 +73,12 @@ then
exec 2>/dev/null exec 2>/dev/null
fi fi
if [[ $USE_NAILGUN ]]
then
$BINDIR/let_sun_shine
$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 java -jar $SUNJAR transform -p $PROJDIR -l $PROJDIR -l $MVN_REPO -n $LANGUAGE -i $PROGRAM
exit $? exit $?

View File

@ -4,7 +4,7 @@ failed=0
for dir in tests/extracted/*; do for dir in tests/extracted/*; do
for file in $dir/*.fos; do for file in $dir/*.fos; do
for language in Python Javascript Haskell; do for language in Python Javascript Haskell OCaml; do
echo bin/fosgen -l ${language%.*} $file ... echo bin/fosgen -l ${language%.*} $file ...
bin/fosgen -l $language $file bin/fosgen -l $language $file
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then

26
bin/let_sun_shine Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
BINDIR=$(dirname $BASH_SOURCE)
if $BINDIR/ng sunshine --help
then
# echo "sun already shining."
else
# echo "disperse the clouds."
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
java -jar $SUNJAR server >/dev/null 2>&1 &
sleep 5
$BINDIR/ng sunshine load -l $PROJDIR -l $MVN_REPO
fi

View File

@ -4,3 +4,4 @@ menus
action: "Python" = to-python action: "Python" = to-python
action: "Javascript" = to-javascript action: "Javascript" = to-javascript
action: "Haskell" = to-haskell action: "Haskell" = to-haskell
action: "OCaml" = to-ocaml

View File

@ -108,7 +108,7 @@ again. That way we can use the general (left-associative)
parse to TopLevel( parse to TopLevel(
Gets(Gets(Gets(Gets(DefGets(LitString("'Two and '")),Int("2")), Gets(Gets(Gets(Gets(DefGets(LitString("'Two and '")),Int("2")),
LitString("' make '")),Sum(Int("2"),Int("2"))), LitString("' make '")),Sum(Int("2"),Int("2"))),
EscString("\"./n\""))) EscString("\".\n\"")))
/** writes /** writes
Two and 2 make 4. Two and 2 make 4.
**/ **/

View File

@ -6,6 +6,7 @@ imports
pp pp
outline outline
analysis analysis
ocaml
haskell haskell
javascript javascript
python python

View File

@ -10,7 +10,7 @@ rules
rule. rule.
C) We will use bottomup-para to traverse the full AST with the 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 transformation from A so that we have access to the original expression
(and get get the Statix-associated type when we need to). (and can get the Statix-associated type when we need to).
Hence the transformation in (A) must actually take a pair of Hence the transformation in (A) must actually take a pair of
an (original) term and a term with value strings at each child, an (original) term and a term with value strings at each child,
and be certain to return a value string. and be certain to return a value string.

61
trans/ocaml.str Normal file
View File

@ -0,0 +1,61 @@
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 }
let rec stdio = {
getS = (fun s -> print_string s; stdio)
};;
(* End of preamble *)
[x]]
ml: (_, Stream()) -> $[stdio]
ml: (_, Int(x)) -> x
ml: (_, LitString(x)) -> $[{|[<un-single-quote>x]|}]
ml: (_, EscString(x)) -> x
ml: (_, Sum(x,y)) -> $[[x] + [y]]
ml: (Gets(_,yn), Gets(x, y))
-> $[([x]).getS ([<ml_str>(yn,y)])]
ml: (To(xn,_), To(x, y))
-> $[let _fto = ([x]) in (ignore (([y]).getS ([<ml_str>(xn,"_fto")])); _fto)]
ml: (_, Terminate(x)) -> x
ml: (_, Sequence(l)) -> <ml_seq>l
ml_seq: [x] -> x
ml_seq: [x | xs ] -> $[ignore ([x]);
[<ml_seq>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 <is-string>x
ml_str: (node, code) -> $[[<ml_string_cast>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 := <guarantee-extension(|"ml")> path
; result := <ocaml> selected