Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flowconfig parser js_of_ocaml package #7698

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,35 @@ js: _build/scripts/ppx_gen_flowlibs.native $(BUILT_OBJECT_FILES) $(COPIED_FLOWLI
exit 1; \
fi

REL_DIR=src/commands/config

flowconfig-js:
$(OCB) \
-pkgs js_of_ocaml \
-pkgs js_of_ocaml-ppx \
-lflags -custom \
$(INCLUDE_OPTS) $(FINDLIB_OPTS) \
-lflags "$(BYTECODE_LINKER_FLAGS) -warn-error -31" \
$(REL_DIR)/flowConfig_dot_js.byte; \
[ -e "$(REL_DIR)/flowconfig_parser.js" -a "$(REL_DIR)/flowconfig_parser.js" -nt "_build/$(REL_DIR)/flowConfig_dot_js.byte" ] || \
js_of_ocaml \
--opt 3 \
--disable genprim \
--extern-fs \
-o $(REL_DIR)/flowconfig_parser.js \
$(REL_DIR)/js/stdlib.js $(JS_STUBS) \
_build/$(REL_DIR)/flowConfig_dot_js.byte;
# Disable errors because we are overriding primitives
# ret=$$?; \
# if [ ! $$ret ]; then \
# exit $$ret; \
# elif [ -s _build/$(REL_DIR)/js_of_ocaml.err ]; then \
# printf "js_of_ocaml produced output on stderr:\n" 1>&2; \
# cat _build/$(REL_DIR)/js_of_ocaml.err 1>&2; \
# exit 1; \
# fi


dist/flow/flow$(EXE): build-flow
mkdir -p $(@D)
cp _build/src/flow.native $@
Expand Down
219 changes: 211 additions & 8 deletions js/str.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,217 @@
// Partially from
// https://github.com/ejgallego/jscoq/blob/2718f9caf31398704c2d84ff089e3f6f0321eada/coq-js/js_stub/str.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is AGPL-3+, and the part based on strstubs.c is LGPL2 + static linking exception. we will have to review license compatibility...

without looking into how hard it'd be first, i'd be more inclined to remove the uses of Str instead. might even make sense to use a real parser (we already depend on sedlex and ocamllex) but that's a bigger project.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll remove it for now, I think LGPL2 is compatible with MIT, not sure about AGPL3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Str also would be useful for flow.js overall, not just flowconfig parser

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Js_of_ocaml (3.6) now include all full support for Str


//Provides: str_ll
function str_ll(s, args) { if (str_ll.log) joo_global_object.console.warn(s, args); }
str_ll.log = false;

//Provides: re_string_match
//Requires: str_ll, re_match
function re_string_match(re, s, pos) {
// external re_string_match : regexp -> string -> int -> int array
//str_ll('re_string_match', arguments);
var res = re_match(re, s, pos, 0);
return (res === 0) ? [0] : res;
}

//Provides: re_search_forward
function re_search_forward() {
throw new Error("re_search_forward: not implemented in JS");
//Requires: str_ll, re_search_forward_naive
function re_search_forward(re, s, pos) {
// external re_search_forward: regexp -> string -> int -> int array
//str_ll('re_search_forward', arguments);
return re_search_forward_naive(re, s, pos);
}

//Provides: re_partial_match
//Requires: str_ll
// external re_partial_match: regexp -> string -> int -> int array
function re_partial_match() {
//str_ll('re_partial_match', arguments);
return [0]; }
//Provides: re_replacement_text
function re_replacement_text() {
throw new Error('re_replacement_text: not implemented in JS');
}
//Requires: str_ll
// external re_replacement_text: string -> int array -> string -> string
function re_replacement_text(r, a, s) {
//str_ll('re_replacement_text', arguments);
return s; }
//Provides: re_search_backward
//Requires: str_ll
// external re_search_backward: regexp -> string -> int -> int array
function re_search_backward() {
//str_ll('re_search_backward', arguments);
return [0]; }

//Provides: re_string_match
function re_string_match() {
throw new Error('re_string_match: not implemented in JS');
//Provides: re_match
// Based on
// https://github.com/ocaml/ocaml/blob/4.07/otherlibs/str/strstubs.c

var re_match = function () {

var opcodes = {
CHAR: 0, CHARNORM: 1, STRING: 2, STRINGNORM: 3, CHARCLASS: 4,
BOL: 5, EOL: 6, WORDBOUNDARY: 7,
BEGGROUP: 8, ENDGROUP: 9, REFGROUP: 10,
ACCEPT: 11,
SIMPLEOPT: 12, SIMPLESTAR: 13, SIMPLEPLUS: 14,
GOTO: 15, PUSHBACK: 16, SETMARK: 17,
CHECKPROGRESS: 18
};

function in_bitset(s, i) {
return (s.c.charCodeAt(i >> 3) >> (i & 7)) & 1;
}

function re_match_impl(re, s, pos, partial) {

var prog = re[1].slice(1),
cpool = re[2].slice(1),
numgroups = re[4],
numregisters = re[5],
startchars = re[6];

var pc = 0, quit = false, txt = s.c,
stack = [],
groups = new Array(numgroups).fill(0).map(function () { return {}; }),
re_register = new Array(numregisters);

groups[0].start = pos;

var backtrack = function () {
while (stack.length) {
var item = stack.pop(), obj, prop;
if (item.undo) {
[obj, prop] = item.undo.loc;
obj[prop] = item.undo.value;
}
else {
[pc, pos] = [item.pos.pc, item.pos.txt];
return;
}
}
quit = true;
};
var push = function (item) { stack.push(item); };


var accept = function () {
return Array.prototype.concat.apply([0],
groups.map(function (g) { return g.start >= 0 && g.end >= 0 ? [g.start, g.end] : [-1, -1]; }))
};

/* Main DFA interpreter loop */
while (!quit) {
var op = prog[pc] & 0xff,
sarg = prog[pc] >> 8,
uarg = sarg & 0xff,
c = txt.charCodeAt(pos),
group;

pc++;

switch (op) {
case opcodes.CHAR:
case opcodes.CHARNORM:
if (c === uarg) pos++;
else backtrack();
break;
case opcodes.STRING:
case opcodes.STRINGNORM:
for (var w = cpool[uarg].c, i = 0; i < w.length; i++) {
if (c === w.charCodeAt(i))
c = txt.charCodeAt(++pos);
else { backtrack(); break; }
}
break;
case opcodes.CHARCLASS:
if (!isNaN(c) && in_bitset(cpool[uarg], c)) pos++;
else backtrack();
break;

case opcodes.BOL:
if (pos > c.t && !isNaN(c) && txt.charCodeAt(pos - 1) !== '\n')
backtrack()
break

case opcodes.EOL:
if (pos < c.l && !isNaN(c) && c !== '\n')
backtrack()
break

case opcodes.BEGGROUP:
group = groups[uarg];
push({
undo: {
loc: [group, 'start'],
value: group.start
}
});
group.start = pos;
break;
case opcodes.ENDGROUP:
group = groups[uarg];
push({
undo: {
loc: [group, 'end'],
value: group.end
}
});
group.end = pos;
break;

case opcodes.SIMPLEOPT:
if (!isNaN(c) && in_bitset(cpool[uarg], c)) pos++;
break;
case opcodes.SIMPLESTAR:
while (!isNaN(c) && in_bitset(cpool[uarg], c))
c = txt.charCodeAt(++pos);
break;
case opcodes.SIMPLEPLUS:
if (!isNaN(c) && in_bitset(cpool[uarg], c)) {
do {
c = txt.charCodeAt(++pos);
} while (!isNaN(c) && in_bitset(cpool[uarg], c));
}
else backtrack();
break;

case opcodes.ACCEPT:
groups[0].end = pos;
return accept();

case opcodes.GOTO:
pc = pc + sarg;
break;
case opcodes.PUSHBACK:
push({ pos: { pc: pc + sarg, txt: pos } });
break;
case opcodes.SETMARK:
push({ undo: { loc: [re_register, uarg], value: re_register[uarg] } });
re_register[uarg] = pos;
break;
case opcodes.CHECKPROGRESS:
if (re_register[uarg] === pos) backtrack();
break;

default:
throw new Error("unimplemented regexp opcode " + op + "(" + sarg + ")");
}
}

return 0;
}

return re_match_impl;
}();


//Provides: re_search_forward_naive
//Requires: re_match
function re_search_forward_naive(re, s, pos) {
while (pos < s.l) {
var res = re_match(re, s, pos);
if (res) return res;
pos++;
}

return [0]; /* [||] : int array */
}
2 changes: 2 additions & 0 deletions packages/flowconfig-parser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modues/
flowconfig_parser.js
26 changes: 26 additions & 0 deletions packages/flowconfig-parser/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

.PHONY: all
all: js

.PHONY: clean
clean:
rm -rf flowconfig_parser.js

# copies flowconfig_parser.js into place, either using a copy that was put in
# dist/flowconfig_parser.js externally (e.g. during a Circle CI build), or by
# building it.
.PHONY: flowconfig_parser.js
flowconfig_parser.js:
if [ -e dist/flowconfig_parser.js ]; then \
[ $@ -nt dist/flowconfig_parser.js ] || cp dist/flowconfig_parser.js $@; \
else \
cd ../..; \
$(MAKE) flowconfig-js; \
cp src/commands/config/flowconfig_parser.js $@; \
fi

js: flowconfig_parser.js
26 changes: 26 additions & 0 deletions packages/flowconfig-parser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "flowconfig-parser",
"version": "0.98.1",
"homepage": "https://flow.org",
"license": "MIT",
"author": {
"name": "Flow Team",
"email": "flow@fb.com"
},
"files": [
"flowconfig_parser.js"
],
"main": "flowconfig_parser.js",
"repository": {
"type": "git",
"url": "https://github.com/facebook/flow.git"
},
"scripts": {
"test": "node test/run_tests.js",
"prepublish": "make js"
},
"dependencies": {},
"engines": {
"node": ">=0.4.0"
}
}
1 change: 1 addition & 0 deletions src/commands/config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flowconfig_parser.js
5 changes: 5 additions & 0 deletions src/commands/config/flowConfig.mli
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
*)

type config

type line = int * string
type warning = int * string
type error = int * string

val get: ?allow_cache:bool -> string -> (config * warning list, error) result
val get_hash: ?allow_cache:bool -> string -> Xx.hash
val empty_config: config

val is_not_comment: line -> bool
val parse: config -> line list -> (config * warning list, error) result

val init:
ignores: string list ->
untyped: string list ->
Expand Down
Loading