Skip to content

Commit

Permalink
Merge 83d8b34 into 51c20d3
Browse files Browse the repository at this point in the history
  • Loading branch information
iphydf committed Mar 21, 2020
2 parents 51c20d3 + 83d8b34 commit 8da750e
Show file tree
Hide file tree
Showing 21 changed files with 395 additions and 40 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
language: generic
os: linux
dist: xenial

addons:
apt:
Expand All @@ -15,9 +16,8 @@ cache:
- $HOME/.opam

install:
- opam init -y
- eval `opam env`
- opam install -y menhir ppx_deriving bisect_ppx dune
- bin/install-deps
- eval $(opam env)

script:
- make coveralls
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ check: $(patsubst %,%dune,$(dir $(wildcard test/*/*.api.h)))
BISECT_ENABLE=yes dune runtest

coverage: check
bisect-ppx-report -html _coverage/ -I _build/default _build/default/test/*/bisect*.out
bisect-ppx-report -html _coverage/ -I _build/default _build/default/test/*/bisect*.coverage

coveralls: check
bisect-ppx-report \
-coveralls coverage.json \
-service-name travis-ci \
-service-job-id "${TRAVIS_JOB_ID}" \
-I _build/default _build/default/test/*/bisect*.out
-I _build/default _build/default/test/*/bisect*.coverage
curl -L -F json_file=@coverage.json https://coveralls.io/api/v1/jobs

clean:
Expand Down
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: _build/default/web/apigen.exe -p $PORT
10 changes: 1 addition & 9 deletions apigen.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ type outlang =
| C
| Haskell of string

let dump_api pre api post =
Option.may (Format.pp_print_string Format.str_formatter) pre;
Format.fprintf Format.str_formatter "%a\n"
ApiCodegen.Api.cg_decls api;
Option.may (Format.fprintf Format.str_formatter "\n%s\n") post;

Format.flush_str_formatter ()


let main input =
let api = ApiPasses.parse_file input in
Expand All @@ -22,7 +14,7 @@ let main input =
| C -> print_string (ApiPasses.all pre ast post)
| Haskell modname -> print_string (ApiPasses.haskell modname ast)
| Ast -> print_endline (ApiAst.show_api Format.pp_print_string api)
| Api -> print_endline (dump_api pre ast post)
| Api -> print_endline (ApiPasses.dump_api pre ast post)


let () =
Expand Down
22 changes: 22 additions & 0 deletions bin/compile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

set -eu

BUILD_DIR="$1"
CACHE_DIR="$2"
ENV_DIR="$1"

export PATH="/tmp/opam:$PATH"
export OPAMROOT="$CACHE_DIR/opam"

# Download opam.
echo "/tmp/opam
Y" | sh <(curl -sL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)

# Enter source directory.
cd "$BUILD_DIR"

. "$BUILD_DIR/bin/install-deps"

# Build our app.
dune build --profile release
3 changes: 3 additions & 0 deletions bin/detect
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

exit 0
18 changes: 18 additions & 0 deletions bin/install-deps
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

set -eux

# Set up opam, install dune and menhir (it's not a library dependency).
opam init --disable-sandboxing -n
opam install dune menhir -y

eval $(opam env)

# Install dependencies: we keep installing dependencies until dune stops telling
# us to install more.
DUNE_CMD='dune external-lib-deps --missing --profile release @@default |& grep -o "opam install .*" || true'
INSTALL_CMD="$(eval $DUNE_CMD)"
while [ -n "$INSTALL_CMD" ]; do
$INSTALL_CMD -y
INSTALL_CMD="$(eval $DUNE_CMD)"
done
3 changes: 3 additions & 0 deletions bin/release
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

exit 0
122 changes: 122 additions & 0 deletions js/apidsl-support.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
function processResult(result, error, output) {
if (result[0] === 1) {
error.innerText = result[1];
output.innerText = "";
} else {
error.innerText = "";
output.innerText = result[1];
}
}

function processLocal(input, format, output, error) {
let result = apidsl.parse("input.api.h", input);
if (result[0] === 0) {
if (format === "haskell") {
result = apidsl.haskell("MyAPI", result[1]);
} else {
result = apidsl[format](result[1]);
}
}

processResult(result, error, output);
}

function processRemote(input, format, output, error) {
const apiUrl = "https://apidsl2.herokuapp.com";
fetch(apiUrl + "/parse", {
method: "POST",
body: JSON.stringify(["Request", input])
}).then((data) => data.json()).then((result) => {
if (result[0] === 0) {
fetch(apiUrl + "/" + format, {
method: "POST",
body: JSON.stringify(["Request", result[1]])
}).then((data) => data.json()).then((result) => {
processResult(result, error, output);
});
} else {
processResult(result, error, output);
}
});
}

function exceptionType(err) {
if (err instanceof Array) {
if (err[1] instanceof Object && "c" in err[1]) {
return err[1].c;
}
}
return null;
}

function isStackOverflow(err) {
return exceptionType(err) === "Stack_overflow" ||
(exceptionType(err) === "Js_of_ocaml__Js.Error" &&
err[2].includes("Maximum call stack size exceeded"));
}

function process() {
let error = document.getElementById("error");
let input = document.getElementById("input").value;
let output = document.getElementById("output");
let format = document.querySelector("input[name="format"]:checked").value;

try {
processLocal(input, format, output, error);
} catch (err) {
if (isStackOverflow(err)) {
processRemote(input, format, output, error);
} else {
console.log("Unhandled exception ", err);
}
}
}

function load(snippet) {
const snippets = {
"simple": "static int main();",
"comment":
`/**
* The main function.
*/
static int main();`,
"namespace":
`namespace foo {
/**
* This is $main.
*/
static int32_t main();
}`,
"large":
`class foo {
/**
* The "this" type for all non-static functions.
*/
struct this;
/**
* This is $main.
*/
int32_t main();
namespace bar {
uint32_t blep();
}
uint8_t some_property { get(); set(); }
}`
};

const fileUrl = "https://raw.githubusercontent.com/TokTok/c-toxcore/master/toxcore/tox.api.h";

let input = document.getElementById("input");
if (snippet === "tox") {
fetch(fileUrl).then(data => data.text()).then(text => {
input.value = text;
process();
});
} else {
input.value = snippets[snippet];
process();
}
}
35 changes: 35 additions & 0 deletions js/apigen.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
open Apidsl
open Js_of_ocaml

let () =
Js.Unsafe.global##.apidsl := (object%js
val parse = Js.wrap_callback (fun file contents ->
try
Ok (ApiPasses.parse_string
(Js.to_string file)
(Js.to_string contents))
with
| Failure(x) -> Error(Js.string x)
| ApiLexer.Lexing_error(start_p, token) ->
let error = ApiPasses.format_lex_error start_p token in
Error(Js.string error))

val ast = Js.wrap_callback (fun api ->
try Ok (Js.string (ApiAst.show_api Format.pp_print_string api))
with Failure(x) -> Error(Js.string x))

val api = Js.wrap_callback (fun api ->
let ApiAst.Api (pre, ast, post) = api in
try Ok (Js.string (ApiPasses.dump_api pre ast post))
with Failure(x) -> Error(Js.string x))

val c = Js.wrap_callback (fun api ->
let ApiAst.Api (pre, ast, post) = api in
try Ok (Js.string (ApiPasses.all pre ast post))
with Failure(x) -> Error(Js.string x))

val haskell = Js.wrap_callback (fun modname api ->
let ApiAst.Api (_, ast, _) = api in
try Ok (Js.string (ApiPasses.haskell (Js.to_string modname) ast))
with Failure(x) -> Error(Js.string x))
end)
5 changes: 5 additions & 0 deletions js/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(executable
(name apigen)
(libraries apidsl)
(preprocess (pps js_of_ocaml-ppx))
(modes js))
53 changes: 53 additions & 0 deletions js/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<title>ApiDSL header generator</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="apigen.bc.js"></script>
<script type="text/javascript" src="apidsl-support.js"></script>
<style>
#editor { width: 100%; height: 100%; }
#error { color: red; }
#left { background-color: #eee; width: 50%; }
#right { background-color: #aea; width: 50%; }
#input { width: 100%; height: 100%; box-sizing: border-box; }
#output { width: 100%; height: 100%; }
body { vertical-align: top; }
</style>
</head>
<body>
<h2>ApiDSL editor</h2>
<form onchange="process()">
Output format:
<input type="radio" id="api" name="format" value="api" checked="checked">
<label for="api">ApiDSL</label>
<input type="radio" id="c" name="format" value="c">
<label for="c">C</label>
<input type="radio" id="haskell" name="format" value="haskell">
<label for="haskell">Haskell</label>
<input type="radio" id="ast" name="format" value="ast">
<label for="ast">AST</label>
</form>
<table id="editor">
<tr style="height: 20em;">
<td id="left">
<textarea id="input" oninput="process()"></textarea>
</td>
<td id="right">
<pre id="output"></pre>
</td>
</tr>
<tr>
<td id="error"></td>
</tr>
</table>
<h2>Examples</h2>
<ul id="examples">
<li><a href="#" onclick="load('simple')">Simple declaration</a></li>
<li><a href="#" onclick="load('comment')">Declaration with comment</a></li>
<li><a href="#" onclick="load('namespace')">Namespaced functions and comment references</a></li>
<li><a href="#" onclick="load('large')">Larger complex example</a></li>
<li><a href="#" onclick="load('tox')">tox.api.h</a></li>
</ul>
</body>
</html>

0 comments on commit 8da750e

Please sign in to comment.