Permalink
Browse files

JavaScript support for a Hermes WDL grammar (#94)

* JavaScript support for a Hermes WDL grammar
  • Loading branch information...
1 parent 90f3514 commit 86f4916e88561e2621314b8261da7db923c56c2e @sidoruka sidoruka committed with geoffjentry Feb 22, 2017
View
@@ -41,11 +41,13 @@ grammar {
enum {
python: r'"([^\\\"\n]|\\[\\"\'nrbtfav\?]|\\[0-7]{1,3}|\\x[0-9a-fA-F]+|\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*"'
java: "\\\"(?>[^\\\\\\\"\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\\\""
- } -> unescape(:string)
+ javascript: "\\\"([^\\\\\\\"\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\\\\?\""
+ } -> wdl_unescape(:string)
enum {
python: r'\'([^\\\'\n]|\\[\\"\'nrbtfav\?]|\\[0-7]{1,3}|\\x[0-9a-fA-F]+|\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\''
java: "'(?>[^\\\\\\'\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*'"
- } -> unescape(:string)
+ javascript: "'([^\\\\\\'\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*'"
+ } -> wdl_unescape(:string)
r':' -> :colon
r',' -> :comma
@@ -74,6 +76,7 @@ grammar {
enum {
python: r'\?'
java: "\\?"
+ javascript: "\\?"
} -> :qmark
r'-?[0-9]+\.[0-9]+' -> :float
r'[0-9]+' -> :integer
@@ -89,6 +92,7 @@ grammar {
enum {
python: r'{%_identifier%}(\.{%_identifier%})*'
java: "{%_identifier%}(\\.{%_identifier%})*"
+ javascript: "{%_identifier%}(\\.{%_identifier%})*"
} -> :fqn
}
mode<wf_output_declaration> {
@@ -139,11 +143,13 @@ grammar {
enum {
python: r'"([^\\\"\n]|\\[\\"\'nrbtfav\?]|\\[0-7]{1,3}|\\x[0-9a-fA-F]+|\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*"'
java: "\\\"(?>[^\\\\\\\"\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\\\""
- } -> unescape(:string)
+ javascript: "\\\"([^\\\\\\\"\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\\\\?\""
+ } -> wdl_unescape(:string)
enum {
python: r'\'([^\\\'\n]|\\[\\"\'nrbtfav\?]|\\[0-7]{1,3}|\\x[0-9a-fA-F]+|\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\''
java: "'(?>[^\\\\\\'\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*'"
- } -> unescape(:string)
+ javascript: "'([^\\\\\\'\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*'"
+ } -> wdl_unescape(:string)
r'-?[0-9]+\.[0-9]+' -> :float
r'[0-9]+' -> :integer
}
@@ -152,6 +158,7 @@ grammar {
enum {
python: r'{%_identifier%}(\.{%_identifier%})*'
java: "{%_identifier%}(\\.{%_identifier%})*"
+ javascript: "{%_identifier%}(\\.{%_identifier%})*"
} -> :fqn %pop
}
mode<scatter> {
@@ -172,7 +179,7 @@ grammar {
python: r'(.*?)(?=\$\{|\})' (DOTALL)
c: r'(.*?)(?=\$\{|\})' (PCRE_DOTALL)
java: r'(.*?)(?=\$\{|\})' (DOTALL)
- javascript: r'(.*?)(?=\$\{|\})' (m)
+ javascript: r'([\s\S]*?)(?=\$\{|\})' (m)
} -> :cmd_part
}
mode<raw_command2> {
@@ -183,7 +190,7 @@ grammar {
python: r'(.*?)(?=\$\{|>>>)' (DOTALL)
c: r'(.*?)(?=\$\{|>>>)' (PCRE_DOTALL)
java: r'(.*?)(?=\$\{|>>>)' (DOTALL)
- javascript: r'(.*?)(?=\$\{|>>>)' (m)
+ javascript: r'([\s\S]*?)(?=\$\{|>>>)' (m)
} -> :cmd_part
}
mode<cmd_param> {
@@ -230,11 +237,13 @@ grammar {
enum {
python: r'"([^\\\"\n]|\\[\\"\'nrbtfav\?]|\\[0-7]{1,3}|\\x[0-9a-fA-F]+|\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*"'
java: "\\\"(?>[^\\\\\\\"\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\\\""
- } -> unescape(:string)
+ javascript: "\\\"([^\\\\\\\"\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\\\\?\""
+ } -> wdl_unescape(:string)
enum {
python: r'\'([^\\\'\n]|\\[\\"\'nrbtfav\?]|\\[0-7]{1,3}|\\x[0-9a-fA-F]+|\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*\''
java: "'(?>[^\\\\\\'\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*'"
- } -> unescape(:string)
+ javascript: "'([^\\\\\\'\\n]|\\\\[\\\"\\'nrbtfav\\\\?]|\\\\[0-7]{1,3}|\\\\x[0-9a-fA-F]+|\\\\[uU]([0-9a-fA-F]{4})([0-9a-fA-F]{4})?)*'"
+ } -> wdl_unescape(:string)
r'-?[0-9]+\.[0-9]+' -> :float
r'[0-9]+' -> :integer
}
@@ -270,7 +279,7 @@ grammar {
if ctx.user_context['context'] == 'workflow':
ctx.stack.append('wf_output')
default_action(ctx, terminal, source_string, line, col)
- def unescape(ctx, terminal, source_string, line, col):
+ def wdl_unescape(ctx, terminal, source_string, line, col):
for regex, c in ctx.user_context['replacements'].items():
source_string = regex.sub(chr(c), source_string)
@@ -305,10 +314,57 @@ grammar {
}
default_action(ctx, terminal, source_string, line, col);
}
- public void unescape(LexerContext ctx, TerminalIdentifier terminal, String source_string, int line, int col) {
+ public void wdl_unescape(LexerContext ctx, TerminalIdentifier terminal, String source_string, int line, int col) {
default_action(ctx, terminal, StringEscapeUtils.unescapeJava(source_string.substring(1, source_string.length() - 1)), line, col);
}
JAVA
+
+ code<javascript> << JAVASCRIPT
+ function init() {
+ return {wf_or_task: null};
+ }
+ function workflow(ctx, terminal, source_string, line, col) {
+ ctx.user_context.wf_or_task = "workflow";
+ default_action(ctx, terminal, source_string, line, col);
+ }
+ function task(ctx, terminal, source_string, line, col) {
+ ctx.user_context.wf_or_task = "task";
+ default_action(ctx, terminal, source_string, line, col);
+ }
+ function output(ctx, terminal, source_string, line, col) {
+ const user_ctx = ctx.user_context;
+ if (user_ctx.wf_or_task != null && user_ctx.wf_or_task === "workflow") {
+ ctx.mode_stack.push("wf_output");
+ }
+ default_action(ctx, terminal, source_string, line, col);
+ }
+ function wdl_unescape(ctx, terminal, source_string, line, col) {
+ var strip_slashes = function(str) {
+ return str
+ .replace(/\\(.?)/g, function (s, n1) {
+ var escapes = {
+ '\\': '\\',
+ '0' : '\u0000',
+ '' : '',
+ 'n' : '\n',
+ 'r' : '\r',
+ 'b' : '\b',
+ 't' : '\t',
+ 'f' : '\f',
+ 'a' : '\a',
+ 'v' : '\v'};
+
+ var symbol = escapes[n1];
+ if (symbol !== undefined) {
+ return symbol;
+ }
+ return n1;
+ });
+ }
+ var repl_str = strip_slashes(source_string.substring(1, source_string.length - 1));
+ default_action(ctx, terminal, repl_str, line, col);
+ }
+ JAVASCRIPT
}
parser {
# Document: https://github.com/broadinstitute/wdl/blob/wdl2/SPEC.md#document
View
@@ -0,0 +1 @@
+tests/node_modules
View
@@ -0,0 +1,66 @@
+# WDL Parser for JS
+
+`wdl_parser.js` is a JavaScript parser for WDL.
+
+Usage example is provided at `sample.js`, which consumes a WDL script and prints AST to console
+
+To run this example make sure that
+* [Node.JS](https://nodejs.org/en/download/) and NPM are installed
+* Current folder is `javascript`
+
+Run the following command:
+
+```
+#Replace <file.wdl> with a WDL file location. Also test cases could be used e.g. ./tests/cases/0/wdl
+node sample.js <file.wdl>
+```
+
+
+# How to generate a parser
+
+To run a generation command make sure that
+* [Hermes Parser Generator](https://github.com/scottfrazer/hermes#installation) and all its dependencies are installed
+* Current folder is `javascript`
+
+To generate WDL parser run the following command:
+
+```
+python hermes generate ../grammar.hgr --language=javascript --name=wdl --nodejs --header --directory .
+```
+
+`wdl_parser.js` will appear in a current folder
+
+# Runnings tests
+
+To tun the tests make sure that
+* [Node.JS](https://nodejs.org/en/download/) and NPM are installed
+* Current folder is `javascript/tests`
+
+Install dependencies
+
+```
+npm install
+```
+
+Run tests
+
+```
+npm run test
+```
+
+When tests are finished - results will be printed to the console, indicating state of test run for each folder in `javascript/tests/cases/`
+
+Below is an example of a successful test run:
+
+```
+JS-WDL Parser
+ √ should correctly generate ast for javascript/tests/cases/0 (53ms)
+ √ should correctly generate ast for javascript/tests/cases/1
+ √ should correctly generate ast for javascript/tests/cases/2
+ √ should correctly generate ast for javascript/tests/cases/3
+ √ should correctly generate ast for javascript/tests/cases/4
+ √ should correctly generate ast for javascript/tests/cases/5
+
+
+ 6 passing (143ms)
+```
View
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+import sys
+import hermes.main
+
+sys.exit(hermes.main.cli())
View
@@ -0,0 +1,20 @@
+const fs = require('fs');
+const path = require('path');
+const parser = require('./wdl_parser.js');
+
+if(process.argv.length < 3) {
+ console.error('node sample.js <file.wdl>');
+ return;
+}
+
+try {
+ const wdlPath = process.argv[2];
+ const wdl = fs.readFileSync(wdlPath).toString();
+ const tokens = parser.lex(wdl);
+ const ast = parser.parse(tokens).to_ast();
+
+ console.log(parser.ast_string(ast, 2, true));
+}
+catch(ex) {
+ console.error(ex.message);
+}
Oops, something went wrong.

0 comments on commit 86f4916

Please sign in to comment.