├── .hgignore ├── forbidden.properties ├── tests ├── test14.js ├── test7.js ├── test13.js ├── test3.js ├── test9.js ├── test12.js ├── test11.js ├── test5.js ├── test6.js ├── fail │ ├── fail1.js │ └── fail2.js ├── test1.js ├── test15.js ├── test.sh ├── test8.js ├── test4.js ├── test10.js └── test2.js ├── version.ml ├── ecma-262.pdf ├── _tags ├── pffpsf.ml ├── CHANGES ├── Makefile ├── AUTHORS ├── ansi.mli ├── fake_ansi.ml ├── test_ecma.ml ├── excerpt.ml ├── minefield.ml ├── conduit.ml ├── conduit.mli ├── cache.ml ├── ansi.ml ├── myocamlbuild.ml ├── qwerty.ml ├── README ├── opt.ml ├── misc └── options.rb ├── liner.ml ├── levenshtein.ml ├── ecmarex.peg ├── source.ml ├── ast.ml ├── manual.txt ├── ecmalexer.lex ├── LICENSE ├── generate.ml ├── jsure.ml ├── convert.ml ├── eval.ml ├── process.ml ├── check.ml └── ecma.peg /.hgignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /forbidden.properties: -------------------------------------------------------------------------------- 1 | name 2 | toto 3 | -------------------------------------------------------------------------------- /tests/test14.js: -------------------------------------------------------------------------------- 1 | var toto = titi; 2 | -------------------------------------------------------------------------------- /version.ml: -------------------------------------------------------------------------------- 1 | let version = (1,0,4) 2 | -------------------------------------------------------------------------------- /ecma-262.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/berke/jsure/HEAD/ecma-262.pdf -------------------------------------------------------------------------------- /tests/test7.js: -------------------------------------------------------------------------------- 1 | var foo = "toto"; 2 | var bar = foo; 3 | var gogol = foo ++; 4 | -------------------------------------------------------------------------------- /tests/test13.js: -------------------------------------------------------------------------------- 1 | function bar(w, j) { 2 | var x = a^b^c^d; 3 | return x; 4 | } 5 | -------------------------------------------------------------------------------- /tests/test3.js: -------------------------------------------------------------------------------- 1 | return true; 2 | return a().f().z; 3 | var Abstract = new Object(); 4 | -------------------------------------------------------------------------------- /tests/test9.js: -------------------------------------------------------------------------------- 1 | var f = 4; 2 | 3 | function foo(a,b,c) { 4 | var d; 5 | d += c; 6 | e ++; 7 | f ++; 8 | } 9 | -------------------------------------------------------------------------------- /tests/test12.js: -------------------------------------------------------------------------------- 1 | function bar() { 2 | var gogol = /si(:fu98e)r9[a-z0-9]/gi; 3 | gogol.onmouseu = 55; 4 | return gogol; 5 | } 6 | -------------------------------------------------------------------------------- /tests/test11.js: -------------------------------------------------------------------------------- 1 | function foo(a, b, unused, c_) 2 | { 3 | var x, y, z; 4 | x = 3; 5 | y = 4; 6 | z = a + b + c_; 7 | c_ = 3; 8 | } 9 | -------------------------------------------------------------------------------- /tests/test5.js: -------------------------------------------------------------------------------- 1 | var x = true; 2 | var y = true; 3 | var a, b = true, c = false; 4 | if(true) return false else return true 5 | d = false; 6 | -------------------------------------------------------------------------------- /_tags: -------------------------------------------------------------------------------- 1 | <*.{ml,mli,byte,native,cma}>: use_aurochs_lib, use_libaurochs, use_unix, use_pack_lib, use_dbm, use_str, debug 2 | "jsure.ml": pp(camlp4o) 3 | -------------------------------------------------------------------------------- /pffpsf.ml: -------------------------------------------------------------------------------- 1 | (* Pffpsf *) 2 | 3 | let pf = Printf.printf;; 4 | let fp = Printf.fprintf;; 5 | let sf = Printf.sprintf;; 6 | let bf = Printf.bprintf;; 7 | -------------------------------------------------------------------------------- /tests/test6.js: -------------------------------------------------------------------------------- 1 | function foo(a) { 2 | //a.name = true; 3 | //a[name] = true; 4 | //a["name"] = true; 5 | a = b.name; 6 | a = b["name"]; 7 | } 8 | -------------------------------------------------------------------------------- /tests/fail/fail1.js: -------------------------------------------------------------------------------- 1 | /* Dangling comma */ 2 | 3 | var foo = { 4 | bar: 33, 5 | baz: { 6 | gogol: true, 7 | zorgol: false 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /tests/test1.js: -------------------------------------------------------------------------------- 1 | return true; 2 | function foo(x,y,z) { 3 | x.name = "foo"; 4 | if(x) { 5 | return true 6 | } else { 7 | return x.y.z(a,b); 8 | } 9 | } 10 | function f(x) { return 2 * 1 + 1 } 11 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | 2011-04-08 1.0.4 Implement -no-color option. 2 | 2009-01-16 1.0.3 Fixed parser bug involving hex constants and float lexemes (again!). 3 | 2008-03-11 1.0.2 Fixed parser bug involving unsigned right shift and float lexemes. 4 | 2008-03-10 1.0.1 Fixed parser bug involving do ... while(...) statements. 5 | 2008-01-13 1.0.0 First public version 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | 3 | .PHONY: all clean install tests jsure 4 | 5 | PREFIX?=/usr/local 6 | 7 | all: jsure 8 | 9 | jsure: 10 | ocamlbuild jsure.native 11 | 12 | install: all 13 | install -m 0755 jsure.native $(PREFIX)/bin/jsure 14 | 15 | clean: 16 | rm -rf _build _log jsure.native 17 | 18 | tests: jsure 19 | @cd tests; ./test.sh 20 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Copyright 2 | ========= 3 | This software is copyright (C)2007 Exalead SA. It is released under the GNU Lesser General Public License (see LICENSE). 4 | 5 | Author 6 | ====== 7 | - Berke Durak, Ph.D., Exalead SA R&D Engineer 8 | berke.durak@exalead.com 9 | 10 | Grammar derived from a Yacc implementation by Jean-Baptiste Tristan 11 | of the ECMA grammar. 12 | -------------------------------------------------------------------------------- /ansi.mli: -------------------------------------------------------------------------------- 1 | (* Ansi *) 2 | 3 | val control : bool -> unit 4 | 5 | val black : int 6 | val red : int 7 | val green : int 8 | val yellow : int 9 | val blue : int 10 | val magenta : int 11 | val cyan : int 12 | val white : int 13 | val uncoloured : int 14 | 15 | val foreground : int -> string 16 | val background : int -> string 17 | val move_to : out_channel -> int -> unit 18 | val none : unit -> string 19 | -------------------------------------------------------------------------------- /tests/test15.js: -------------------------------------------------------------------------------- 1 | // Sent by Michael Dayah 2 | 3 | var numbers = [1e3, 2e-3]; 4 | var numbers2 = [1E-12]; 5 | foo = 0xff; 6 | bar = 0xFF; 7 | result = start + ((( Math.round(((((end & 0xFF0000) >> 16) - ((start 8 | & 0xFF0000) >> 16)) * n))) << 16) + (( Math.round(((((end & 0x00FF00) >> 8) 9 | - ((start & 0x00FF00) >> 8)) * n))) << 8) + (( Math.round((((end & 0x0000FF) 10 | - (start & 0x0000FF)) * n))))); 11 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/zsh 2 | 3 | JSURE=(../jsure.native -quiet) 4 | 5 | jsure() { 6 | $JSURE >/dev/null 2>&1 $1 7 | } 8 | 9 | fail=0 10 | tests=0 11 | 12 | for x in *.js ; do 13 | tests=$((tests + 1)) 14 | ( jsure $x ) || ( fail=$((fail + 1)) ; echo "FAIL POSITIVE $x" ) 15 | done 16 | 17 | for x in fail/*.js ; do 18 | tests=$((tests + 1)) 19 | ( jsure $x ) && ( fail=$((fail + 1)) ; echo "FAIL NEGATIVE $x" ) 20 | done 21 | 22 | echo "TESTS : " $tests 23 | echo "PASS : " $((tests-fail)) 24 | echo "FAIL : " $fail 25 | 26 | if [ $fail -lt 0 ]; then 27 | exit 1 28 | else 29 | exit 0 30 | fi 31 | -------------------------------------------------------------------------------- /fake_ansi.ml: -------------------------------------------------------------------------------- 1 | let black = 0;; 2 | let red = 1;; 3 | let green = 2;; 4 | let yellow = 3;; 5 | let blue = 4;; 6 | let magenta = 5;; 7 | let cyan = 6;; 8 | let white = 7;; 9 | let uncoloured = 8;; 10 | 11 | let foreground = [| 12 | ""; 13 | ""; 14 | ""; 15 | ""; 16 | ""; 17 | ""; 18 | ""; 19 | ""; 20 | ""; 21 | |];; 22 | 23 | let background = [| 24 | ""; 25 | ""; 26 | ""; 27 | ""; 28 | ""; 29 | ""; 30 | ""; 31 | ""; 32 | ""; 33 | |];; 34 | 35 | let move_to oc x = () 36 | let up = "" 37 | let ceol = "" 38 | let home = "" 39 | let none = "" 40 | ;; 41 | -------------------------------------------------------------------------------- /tests/test8.js: -------------------------------------------------------------------------------- 1 | gogol = 3; 2 | 3 | function foo() { 4 | a = 3; 5 | var a = 3; 6 | a = 4; 7 | this.a; 8 | return a; 9 | } 10 | 11 | function 12 | foobar(a,b,c,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d) { 13 | if(a) { 14 | if(b) { 15 | c = d; 16 | b += a; 17 | } else { 18 | if(c) a += b; 19 | if(d) { a += b; c += d; c += c } 20 | } 21 | } 22 | a(b,c); 23 | } 24 | 25 | function bar(z) { 26 | z = 3; 27 | } 28 | 29 | function barbar(z) { 30 | var z = 3; 31 | } 32 | 33 | var x; 34 | 35 | gogol() { 36 | x = foo; 37 | } 38 | -------------------------------------------------------------------------------- /test_ecma.ml: -------------------------------------------------------------------------------- 1 | module M = Ecma_parser;; 2 | 3 | let read_file fn = 4 | let ic = open_in fn in 5 | let m = in_channel_length ic in 6 | let u = String.create m in 7 | really_input ic u 0 m; 8 | close_in ic; 9 | u 10 | ;; 11 | 12 | let _ = 13 | let fn = Sys.argv.(1) in 14 | let u = read_file fn in 15 | let t = M.parse u in 16 | M.print_tree stdout t 17 | ;; 18 | 19 | let parsing_prefixes u = 20 | let m = String.length u in 21 | for i = 1 to m - 1 do 22 | let v = String.sub u 0 i in 23 | try 24 | let _t = M.parse v in 25 | Printf.printf ">> %d\n%!" i 26 | with 27 | | _ -> () 28 | done 29 | ;; 30 | -------------------------------------------------------------------------------- /tests/test4.js: -------------------------------------------------------------------------------- 1 | var a = 1, b = 2; 2 | for(var i = 0, length = arguments.length; i < 33; i ++) { 3 | print(i); 4 | } 5 | 6 | var toto = { 7 | onTimerEvent: function() { 8 | /*if (!this.currentlyExecuting)*/ { 9 | } 10 | } 11 | } 12 | 13 | var gogol = /[a-z]/; 14 | 15 | var foo = ' '; 16 | 17 | var foo = ''; 18 | 19 | var foo = [1, 2]; 20 | 21 | function foo() { 22 | switch(a) { 23 | case 3: return true; 24 | } 25 | for(var i = 0, length = arguments.length; i < length; i++) { 26 | var value = this[arguments[i]]; 27 | return 1.0; 28 | if (x) { 29 | return true; 30 | } else { } 31 | delete this[arguments[i]]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /excerpt.ml: -------------------------------------------------------------------------------- 1 | (* Excerpt *) 2 | 3 | open Pffpsf 4 | 5 | (*** excerpt *) 6 | let excerpt liner text start_pos end_pos = 7 | let lines = Source.extract_lines liner text start_pos end_pos in 8 | let b = Buffer.create 256 in 9 | Array.iter 10 | begin fun (l, u, v, w) -> 11 | if Buffer.length b > 0 then bf b "\n"; 12 | bf b "%s%5d%s %s%s%s%s%s%s%s" 13 | (Ansi.foreground !Opt.line_number_color) (l + 1) (Ansi.none ()) 14 | (Ansi.foreground !Opt.code_color) u 15 | (Ansi.foreground !Opt.code_hl_color) v 16 | (Ansi.foreground !Opt.code_color) w 17 | (Ansi.none ()) 18 | end 19 | lines; 20 | Buffer.contents b 21 | (* ***) 22 | -------------------------------------------------------------------------------- /tests/fail/fail2.js: -------------------------------------------------------------------------------- 1 | var a = 1, b = 2; 2 | for(var i = 0, length = arguments.length; i < ; i ++) { 3 | print(i); 4 | } 5 | 6 | var toto = { 7 | onTimerEvent: function() { 8 | /*if (!this.currentlyExecuting)*/ { 9 | } 10 | } 11 | } 12 | 13 | var gogol = /[a-z]/; 14 | 15 | var foo = ' '; 16 | 17 | var foo = ''; 18 | 19 | var foo = [1, 2]; 20 | 21 | function foo() { 22 | switch(a) { 23 | case 3: return true; 24 | } 25 | for(var i = 0, length = arguments.length; i < length; i++) { 26 | var value = this[arguments[i]]; 27 | return 1.0; 28 | if (x) { 29 | return true; 30 | } else { } 31 | delete this[arguments[i]]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/test10.js: -------------------------------------------------------------------------------- 1 | function toto(b) { 2 | with(this) { 3 | var options = select.getElementsByTagName("option"); 4 | assertEqual("choice1", options[0].value); 5 | assertEqual("other", options[1].value); 6 | assert( options[1].selected, "second option is selected"); 7 | assertEqual("other", select.getValue()); 8 | 9 | select.addOption("choice3", "third", true); 10 | assert( options[1].selected, "second option is still selected (multiple=true)"); 11 | assert( options[2].selected, "third option is also selected"); 12 | var options = select.getElementsByTagName("option"); 13 | if(x) { 14 | return false; 15 | } else { 16 | return false; 17 | } 18 | return foo; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /minefield.ml: -------------------------------------------------------------------------------- 1 | (* Minefield *) 2 | 3 | module SS = Set.Make(String);; 4 | module SM = Map.Make(String);; 5 | 6 | type t = { 7 | mutable mf_words : int SM.t; 8 | } 9 | 10 | module L = Levenshtein.Levenshtein_string;; 11 | 12 | let create () = { mf_words = SM.empty };; 13 | 14 | let add_word mf u = mf.mf_words <- SM.add u 0 mf.mf_words;; 15 | 16 | let check_proximity mf u = 17 | if SM.mem u mf.mf_words then 18 | None 19 | else 20 | begin 21 | let min_dist = ref max_float in 22 | let nearest_word = ref None in 23 | SM.iter 24 | begin fun v _ -> 25 | let d = L.distance u v in 26 | if d < !min_dist then 27 | begin 28 | min_dist := d; 29 | nearest_word := Some v 30 | end 31 | end 32 | mf.mf_words; 33 | match !nearest_word with 34 | | None -> None 35 | | Some v -> Some(!min_dist, v) 36 | end 37 | ;; 38 | -------------------------------------------------------------------------------- /conduit.ml: -------------------------------------------------------------------------------- 1 | (* Conduit *) 2 | 3 | type 'channel conduit = { 4 | cd_out_channel : 'channel; 5 | cd_print : 'a . 'channel -> ('a, 'channel, unit) format -> 'a; 6 | cd_flush : 'channel -> unit; 7 | };; 8 | 9 | let stdoutcd = { 10 | cd_out_channel = stdout; 11 | cd_print = Printf.fprintf; 12 | cd_flush = flush 13 | };; 14 | 15 | let stderrcd = { 16 | cd_out_channel = stderr; 17 | cd_print = Printf.fprintf; 18 | cd_flush = flush 19 | };; 20 | 21 | let conduit_of_channel oc = { 22 | cd_out_channel = oc; 23 | cd_print = Printf.fprintf; 24 | cd_flush = flush 25 | };; 26 | 27 | let conduit_of_buffer b = 28 | { cd_out_channel = b; 29 | cd_print = Printf.bprintf; 30 | cd_flush = ignore } 31 | ;; 32 | 33 | let scribe_string cd oc u = cd.cd_print oc "%s" u;; 34 | 35 | (*** stringify *) 36 | let stringify f = 37 | let b = Buffer.create 128 in 38 | let cd = conduit_of_buffer b in 39 | f cd cd.cd_out_channel; 40 | Buffer.contents b 41 | ;; 42 | (* ***) 43 | -------------------------------------------------------------------------------- /conduit.mli: -------------------------------------------------------------------------------- 1 | (* Conduit *) 2 | (* Copyright 2005-2007 Berke DURAK, INRIA Rocquencourt and The EDOS Project. *) 3 | (* Released under the GNU LGPL version 2. *) 4 | 5 | (** A conduit is a channel for using formatting functions on buffers or channels. *) 6 | type 'a conduit = { 7 | cd_out_channel : 'a; (** Can be a buffer or a channel. *) 8 | cd_print : 'b. 'a -> ('b, 'a, unit) format -> 'b; (** The print function. *) 9 | cd_flush : 'a -> unit; (** The flush function. *) 10 | } 11 | 12 | val stdoutcd : out_channel conduit (** The conduit linked to standard output *) 13 | 14 | val stderrcd : out_channel conduit (** The conduit linked to standard error *) 15 | 16 | val conduit_of_channel : out_channel -> out_channel conduit (** Builds a conduit from an output channel. *) 17 | 18 | val conduit_of_buffer : Buffer.t -> Buffer.t conduit (** Builds a conduit from a buffer. *) 19 | 20 | val scribe_string : 'a conduit -> 'a -> string -> unit (** Writes a string into a conduit. *) 21 | 22 | val stringify : (Buffer.t conduit -> Buffer.t -> unit) -> string 23 | -------------------------------------------------------------------------------- /cache.ml: -------------------------------------------------------------------------------- 1 | (* Cache *) 2 | 3 | type t = { 4 | c_db : Dbm.t; 5 | c_version : string 6 | };; 7 | 8 | let create ?(version=0) fn = 9 | let db = Dbm.opendbm fn [Dbm.Dbm_rdwr; Dbm.Dbm_create] 0o644 in 10 | let s = Printf.sprintf "%d;" version in 11 | { c_db = db; 12 | c_version = s } 13 | ;; 14 | 15 | let digest_length = 16;; 16 | 17 | (*** get *) 18 | let get c name u = 19 | let data = Dbm.find c.c_db name in 20 | let digest = Digest.string u in 21 | let version = String.sub data 0 (String.length c.c_version) in 22 | if version <> c.c_version then raise Not_found; 23 | let digest' = String.sub data (String.length c.c_version) digest_length in 24 | if digest = digest' then 25 | Marshal.from_string data (String.length c.c_version + digest_length) 26 | else 27 | raise Not_found 28 | ;; 29 | (* ***) 30 | (*** set *) 31 | let set c name u x = 32 | let digest = Digest.string u in 33 | let data = Marshal.to_string x [] in 34 | Dbm.replace c.c_db name (c.c_version ^ digest ^ data) 35 | ;; 36 | (* ***) 37 | (*** close *) 38 | let close c = 39 | Dbm.close c.c_db 40 | ;; 41 | (* ***) 42 | -------------------------------------------------------------------------------- /ansi.ml: -------------------------------------------------------------------------------- 1 | (* Ansi *) 2 | 3 | open Pffpsf;; 4 | 5 | let enabled = ref true;; 6 | 7 | let control x = enabled := x 8 | 9 | let filter u = if !enabled then u else "";; 10 | 11 | let black = 0;; 12 | let red = 1;; 13 | let green = 2;; 14 | let yellow = 3;; 15 | let blue = 4;; 16 | let magenta = 5;; 17 | let cyan = 6;; 18 | let white = 7;; 19 | let uncoloured = 8;; 20 | 21 | let foreground_array = [| 22 | "\027[30m"; 23 | "\027[31m"; 24 | "\027[32m"; 25 | "\027[33m"; 26 | "\027[34m"; 27 | "\027[35m"; 28 | "\027[36m"; 29 | "\027[37m"; 30 | "\027[0m"; 31 | |];; 32 | 33 | let foreground i = filter foreground_array.(i);; 34 | 35 | let background_array = [| 36 | "\027[40m"; 37 | "\027[41m"; 38 | "\027[42m"; 39 | "\027[43m"; 40 | "\027[44m"; 41 | "\027[45m"; 42 | "\027[46m"; 43 | "\027[47m"; 44 | "\027[0m"; 45 | |];; 46 | 47 | let background i = filter background_array.(i);; 48 | 49 | let move_to oc x = if !enabled then fp oc "\r\027[%dC\027[K" x else ();; 50 | let up () = filter "\027[1A" 51 | let ceol () = filter "\027[K" 52 | let home () = filter "\r" 53 | let none () = filter "\027[0m" 54 | ;; 55 | -------------------------------------------------------------------------------- /myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | (* Myocamlbuild for Jsure *) 2 | 3 | open Ocamlbuild_pack;; 4 | open Ocamlbuild_plugin;; 5 | open Command;; 6 | open Ocaml_specific;; 7 | 8 | let aurochs = ref (S[A"aurochs";A"-quiet";A"-target";A"ml"]);; 9 | let system_lib_dir = "/usr/lib";; 10 | 11 | dispatch 12 | begin function 13 | | After_rules -> 14 | begin 15 | List.iter 16 | begin fun lib -> 17 | flag ["ocaml"; "link"] (S[A"-I"; A("+"^lib)]); 18 | flag ["ocaml"; "compile"] (S[A"-I"; A("+"^lib)]); 19 | end 20 | ["aurochs_lib"]; 21 | 22 | Options.ocamlopt := S[A"ocamlopt";A"-verbose"]; 23 | 24 | ocaml_lib ~extern:true "aurochs_lib"; 25 | 26 | flag ["link"; "ocaml"; "byte"; "use_libaurochs"] 27 | (S[A"-dllib";A("-laurochs"); A"-cclib";A("-laurochs")]); 28 | 29 | flag ["link"; "ocaml"; "use_libaurochs"] 30 | (S[A"-ccopt"; A("-L"^system_lib_dir); A"-cclib"; A"-laurochs"]); 31 | 32 | rule "aurochs: .peg -> .ml,.mli" 33 | 34 | ~prods:["%.ml";"%.mli"] 35 | 36 | ~dep:"%.peg" 37 | begin fun env _build -> 38 | let peg = env "%.peg" and ml = env "%.ml" in 39 | let tags = tags_of_pathname ml++"aurochs" in 40 | Cmd(S[!aurochs; T tags; P peg]) 41 | end 42 | end 43 | | _ -> () 44 | end 45 | -------------------------------------------------------------------------------- /qwerty.ml: -------------------------------------------------------------------------------- 1 | (* Qwerty *) 2 | (* Written by Berke DURAK, released in the public Domain. *) 3 | 4 | let row y0 x0 w u1 u2 = 5 | let m = String.length u1 in 6 | let r = ref [] in 7 | let _shift_distance = 10.0 in 8 | for i = 0 to m - 1 do 9 | r := (y0, x0 +. (float_of_int i) *. w, 0.0, u1.[i])::!r; 10 | r := (y0, x0 +. (float_of_int i) *. w, 10.0, u2.[i])::!r 11 | done; 12 | !r 13 | ;; 14 | 15 | let qwerty_description = List.concat [ 16 | row 0.0 0.0 1.7461 17 | "`1234567890-=" 18 | "~!@#$%^&*()_+"; 19 | [1.5,1.0,0.0,'\t']; 20 | row 1.5 2.8 1.7461 "qwertyuiop[]" "QWERTYUIOP{}"; 21 | row 3.0 3.4 1.7461 "asdfghjkl;'\\" "ASDFGHJKL:\"|"; 22 | row 4.5 2.8 1.7461 "ZXCVBNM<>?"; 23 | [6.0,12.5,0.0,' '] 24 | ];; 25 | 26 | let qwerty_dummy = (20.0,4.0,0.5);; 27 | 28 | let qwerty_map = 29 | let a = Array.make 256 qwerty_dummy in 30 | List.iter (fun (x,y,z,c) -> a.(Char.code c) <- (x,y,z)) qwerty_description; 31 | a 32 | ;; 33 | 34 | let euclidian_distance (x1,y1,z1) (x2,y2,z2) = 35 | let f a1 a2 = (a1 -. a2) *. (a1 -. a2) in 36 | sqrt ((f x1 x2) +. (f y1 y2) +. (f z1 z2)) 37 | ;; 38 | 39 | let qwerty_insertion_cost = 4.0 40 | let qwerty_deletion_cost = qwerty_insertion_cost 41 | ;; 42 | 43 | let qwerty_distance c1 c2 = 44 | let p c = qwerty_map.(Char.code c) in 45 | match (c1,c2) with 46 | None,None -> 0.0 47 | | Some(_),None -> qwerty_deletion_cost 48 | | None,Some(_) -> qwerty_insertion_cost 49 | | Some(c1),Some(c2) -> 50 | euclidian_distance (p c1) (p c2) 51 | ;; 52 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Installation instructions 2 | ========================= 3 | 4 | * Pre-compiled binaries for Linux are available in bin/ 5 | * To compile Jsure: 6 | - Get, build and install Aurochs from http://aurochs.fr/ 7 | - make 8 | - PREFIX=/usr/local make install 9 | 10 | Important note 11 | ============== 12 | I have recently discovered that the Javascript grammar allows you to omit 13 | semicolons in most circumstances. Actually, it is more on the order 14 | of tolerating forgotten semicolons than "allowing" their omission, because the 15 | parser described in the ECMA specification is supposed to backtrack and retry 16 | when it doesn't find an expected semicolon. Of course this (1) supposes 17 | a particular parsing method and (2) makes the suggested parser run slowly. 18 | 19 | Unfortunately I would need to completely rewrite my Javascript grammar 20 | (and possibly make hacks to Aurochs) to support the omission of semicolons. 21 | 22 | I think omitting semicolons is very bad practice. For one thing, it 23 | makes the source more ambiguous and harder to read. For another thing, 24 | it departs from common C and Java syntactic conventions. Fortunately, 25 | it seems that only a handful of people systematically omit semicolons. 26 | 27 | I'm saying a handful but I have only seen one such case: someone whose 28 | Javascript looked like 29 | 30 | function GetLen_(G) { var K, L, C = "" // Get good margin 31 | G.Len.value = K = L = Math.max(G.Len.value, 1) 32 | while (--K) C += " " ; G.Result.value = C + "|" 33 | return L } 34 | 35 | I guess people who'll use Jsure are not in the business of obfuscating 36 | their source (there are tools for that), so I'll leave semicolons mandatory. 37 | -------------------------------------------------------------------------------- /opt.ml: -------------------------------------------------------------------------------- 1 | (* Opt *) 2 | 3 | open Source;; 4 | 5 | let dump_ast = ref false;; 6 | let dump_raw_ast = ref false;; 7 | let dump_env = ref false;; 8 | let just_syntax = ref false;; 9 | let dont_catch = ref false;; 10 | let list_props = ref false;; 11 | let quiet = ref false;; 12 | let no_warnings = ref false;; 13 | let warnify : string list ref = ref [];; 14 | let ignorify : string list ref = ref [];; 15 | let minefield : string option ref = ref None;; 16 | let forbidden_props_file : string option ref = ref None;; 17 | let check = ref true;; 18 | let typecheck = ref false;; 19 | let mine_distance = ref 5.0;; 20 | let unused_ident_regexp = ref "^_.*_$";; 21 | let safe_property_regexp = ref "[a-zA-Z_][a-zA-Z0-9_]*";; 22 | let filename_regexp = ref ".*\\.js";; 23 | let generate = ref false;; 24 | let read_from_stdin = ref false;; 25 | let line_number_offset = ref 0;; 26 | 27 | let undefined_variables = ref Ign;; 28 | let unbound_variables = ref Wrn;; 29 | let toplevel_bindings = ref Ign;; 30 | let assigning_to_args = ref Ign;; 31 | let unused_vars = ref Wrn;; 32 | let unused_funs = ref Wrn;; 33 | let unused_args = ref Wrn;; 34 | let using_unused = ref Wrn;; 35 | let uninitialized_vars = ref Wrn;; 36 | let shadowing_args = ref Wrn;; 37 | let unreachable_code = ref Wrn;; 38 | let dangling_commas = ref Err;; 39 | let bad_regexps = ref Err;; 40 | 41 | let error_visualization = ref Txt;; 42 | let warning_visualization = ref Txt;; 43 | 44 | let info_color = ref Ansi.green;; 45 | let warning_color = ref Ansi.yellow;; 46 | let error_color = ref Ansi.red;; 47 | let line_number_color = ref Ansi.blue;; 48 | let code_color = ref Ansi.uncoloured;; 49 | let code_hl_color = ref Ansi.red;; 50 | 51 | let cache : string option ref = ref None;; 52 | -------------------------------------------------------------------------------- /misc/options.rb: -------------------------------------------------------------------------------- 1 | def allowed_options { 2 | :just_syntax => {:type => 'bool', :default => false, :description => 'Just check the syntax of input files'}, 3 | :no_warnings => {:type => 'bool', :default => false, :description => 'Suppress warnings'}, 4 | :no_check => {:type => 'bool', :default => false, :description => 'Disable basic semantic checks'}, 5 | :assigning_to_args => {:type => 'wei', :default => 'i', :description => 'Assigning to arguments'}, 6 | :bad_regexps => {:type => 'wei', :default => 'e', :description => 'Bad regular expression litterals'} 7 | :dangling_commas => {:type => 'wei', :default => 'e', :description => 'Dangling commas in object and array litterals'}, 8 | :shadowing_args => {:type => 'wei', :default => 'w', :description => 'Arguments shadowed by variables'}, 9 | :toplevel_bindings => {:type => 'wei', :default => 'i', :description => 'Setting global properties (without a "var")'}, 10 | :unbound_variables => {:type => 'wei', :default => 'w', :description => 'Assigning to apparently unbound global variables, except in the toplevel'}, 11 | :undefined_variables => {:type => 'wei', :default => 'i', :description => 'Referring to global names not obviously declared'}, 12 | :uninitialized_vars => {:type => 'wei', :default => 'w', :description => 'Uninitialized variables'}, 13 | :unreachable_code => {:type => 'wei', :default => 'w', :description => 'Unreachable code'}, 14 | :unused_args => {:type => 'wei', :default => 'w', :description => 'Unused arguments'}, 15 | :unused_funs => {:type => 'wei', :default => 'w', :description => 'Unused functions'}, 16 | :unused_vars => {:type => 'wei', :default => 'w', :description => 'Unused variables'}, 17 | :using_unused => {:type => 'wei', :default => 'w', :description => 'Using variables or arguments lexically declared to be unused'}, 18 | 19 | # typecheck is too beta 20 | #:typecheck => {:type => 'bool', :default => true, :description => 'Attempt to typecheck source files'}, 21 | } 22 | -------------------------------------------------------------------------------- /tests/test2.js: -------------------------------------------------------------------------------- 1 | var x = 3; 2 | var x = { foo: bar, zonk: 33, foobar: true, toto: "shit", otot: 'shit' }; 3 | var gogol = { 4 | fun: { 5 | bar: baz, 6 | zonk: zink[33], 7 | fifi: function () { return false; }, 8 | toutou: !!toutou 9 | } 10 | } 11 | function foo() { 12 | return true; 13 | } 14 | function bar() { 15 | } 16 | var Prototype = { 17 | Version: '1.5.0', 18 | BrowserFeatures: { 19 | XPath: !!document.evaluate 20 | }, 21 | 22 | ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', 23 | emptyFunction: function() {}, 24 | K: function(x) { return x } 25 | } 26 | 27 | var Class = { 28 | create: function() { 29 | return function() { 30 | this.initialize.apply(this, arguments); 31 | } 32 | } 33 | } 34 | 35 | var Abstract = new Object(3,4); 36 | 37 | Object.extend = function(destination, source) { 38 | for (var property in source) { 39 | destination[property] = source[property]; 40 | } 41 | return destination; 42 | } 43 | 44 | Object.extend(Object, { 45 | inspect: function(object) { 46 | try { 47 | if (object === undefined) return 'undefined'; 48 | if (object === null) return 'null'; 49 | return object.inspect ? object.inspect() : object.toString(); 50 | } catch (e) { 51 | if (e instanceof RangeError) return '...'; 52 | throw e; 53 | } 54 | }, 55 | 56 | keys: function(object) { 57 | var keys = []; 58 | for (var property in object) 59 | keys.push(property); 60 | return keys; 61 | }, 62 | 63 | values: function(object) { 64 | var values = []; 65 | for (var property in object) 66 | values.push(object[property]); 67 | return values; 68 | }, 69 | 70 | clone: function(object) { 71 | return Object.extend({}, object); 72 | } 73 | }); 74 | 75 | Function.prototype.bind = function() { 76 | var __method = this, args = $A(arguments), object = args.shift(); 77 | return function() { 78 | return __method.apply(object, args.concat($A(arguments))); 79 | } 80 | } 81 | 82 | Function.prototype.bindAsEventListener = function(object) { 83 | return f(a).f(b); 84 | } 85 | -------------------------------------------------------------------------------- /liner.ml: -------------------------------------------------------------------------------- 1 | (* Liner *) 2 | 3 | (*** t *) 4 | type t = { 5 | l_length : int; 6 | l_table : (int * int) array; 7 | l_offset : int 8 | } 9 | (* ***) 10 | (*** length *) 11 | let length l = l.l_length 12 | (* ***) 13 | (*** create *) 14 | let create ?(offset=0) u = 15 | let m = String.length u in 16 | let t = ref [] in 17 | let line = ref 0 in 18 | for i = 0 to m do 19 | if i < m && u.[i] = '\n' or i = m then 20 | begin 21 | t := (!line, i) :: !t; 22 | incr line 23 | end 24 | done; 25 | { l_length = m; 26 | l_table = Array.of_list (List.rev !t); 27 | l_offset = offset } 28 | (* ***) 29 | (*** line_to_range *) 30 | let line_to_range l ?(offset=l.l_offset) i = 31 | let i = i - offset in 32 | let (_, end_pos) = l.l_table.(i) in 33 | if i = 0 then 34 | (0, end_pos) 35 | else 36 | let (_, start_pos) = l.l_table.(i - 1) in 37 | (start_pos + 1, end_pos) 38 | (* ***) 39 | (*** position_to_line *) 40 | let position_to_line l j = 41 | let m = Array.length l.l_table in 42 | let in_line i = 43 | let (start_pos, end_pos) = line_to_range l ~offset:0 i in 44 | start_pos <= j && j <= end_pos 45 | in 46 | let rec loop i0 m = 47 | if m = 0 then 48 | raise Not_found 49 | else 50 | begin 51 | if m < 8 then 52 | if in_line i0 then 53 | i0 54 | else 55 | loop (i0 + 1) (m - 1) 56 | else 57 | let i = i0 + m / 2 in 58 | let (start_pos, end_pos) = line_to_range l ~offset:0 i in 59 | if start_pos <= j && j <= end_pos then 60 | i 61 | else 62 | if j < start_pos then 63 | loop i0 (m / 2) 64 | else 65 | loop (i + 1) (m - m / 2 - 1) 66 | end 67 | in 68 | l.l_offset + loop 0 m 69 | (* ***) 70 | (*** test *) 71 | let test () = 72 | let u = "alpha\nbeta\ngamma delta\nepsilon\n\nphi\n" in 73 | let l = create u in 74 | for i = 0 to String.length u - 1 do 75 | let j = position_to_line l i in 76 | let (s,e) = line_to_range l j in 77 | Printf.printf "%d -> #%d [%d,%d]\n%!" i j s e 78 | done 79 | (* ***) 80 | -------------------------------------------------------------------------------- /levenshtein.ml: -------------------------------------------------------------------------------- 1 | (* Levenshtein *) 2 | (* Written by Berke DURAK, released in the public Domain. *) 3 | 4 | module type COSTS = 5 | sig 6 | type t 7 | val min : t -> t -> t 8 | val sum : t -> t -> t 9 | val zero : t 10 | end 11 | 12 | module type WORDS = 13 | sig 14 | module K:COSTS 15 | type t 16 | type letter 17 | val length : t -> int 18 | val get : t -> int -> letter 19 | val distance : letter option -> letter option -> K.t 20 | end 21 | 22 | module Make(A:WORDS) = 23 | struct 24 | module K = A.K 25 | let distance s t = 26 | let m = A.length s and 27 | n = A.length t in 28 | let a = Array.make ((m + 1) * (n + 1)) K.zero in 29 | let put i j x = 30 | begin 31 | assert (0 <= i && i <= m && 0 <= j && j <= n); 32 | a.((n + 1) * i + j) <- x 33 | end 34 | and take i j = 35 | begin 36 | assert (0 <= i && i <= m && 0 <= j && j <= n); 37 | a.((n + 1) * i + j) 38 | end 39 | in 40 | begin 41 | put 0 0 K.zero; 42 | for i = 1 to m do 43 | put i 0 (K.sum (take (i - 1) 0) (A.distance None (Some(A.get s (i - 1))))) 44 | done; 45 | for j = 1 to n do 46 | put 0 j (K.sum (take 0 (j - 1)) (A.distance (Some(A.get t (j - 1))) None)) 47 | done; 48 | for i = 0 to m - 1 do 49 | for j = 0 to n - 1 do 50 | let x1 = K.sum (take i (j + 1)) (A.distance (Some (A.get s i)) None) 51 | and x2 = K.sum (take (i + 1) j ) (A.distance (Some (A.get t j)) None) 52 | and x3 = K.sum (take i j ) (A.distance (Some (A.get s i)) (Some (A.get t j))) 53 | in 54 | let x = K.min x1 (K.min x2 x3) in 55 | put (i + 1) (j + 1) x 56 | done 57 | done; 58 | take m n 59 | end 60 | end 61 | 62 | module Float_cost = 63 | struct 64 | type t = float 65 | let min = min 66 | let sum = (+.) 67 | let zero = 0.0 68 | end 69 | 70 | module String_words = 71 | struct 72 | module K = Float_cost 73 | type t = string 74 | type letter = char 75 | let length = String.length 76 | let get = String.get 77 | let distance = Qwerty.qwerty_distance 78 | (*let distance c1 c2 = 79 | match (c1,c2) with 80 | | None, None -> 0.0 81 | | Some _, None -> 1.0 82 | | None, Some _ -> 1.0 83 | | Some c1, Some c2 -> 84 | if c1 = c2 then 85 | 0.0 86 | else 87 | 1.0*) 88 | end 89 | 90 | module Levenshtein_string = Make(String_words) 91 | -------------------------------------------------------------------------------- /ecmarex.peg: -------------------------------------------------------------------------------- 1 | start ::= rx_pattern EOF; 2 | 3 | rx_pattern ::= rx_disjunction; 4 | 5 | rx_disjunction ::= rx_alternative '|' rx_disjunction | rx_alternative; 6 | 7 | rx_alternative ::= rx_term rx_alternative | epsilon; 8 | 9 | rx_term ::= rx_assertion | rx_atom rx_quantifier | rx_atom; 10 | 11 | rx_assertion ::= "^" | "$" | "\\b" | "\\B"; 12 | 13 | rx_quantifier ::= 14 | rx_quantifier_prefix '?' 15 | | rx_quantifier_prefix 16 | ; 17 | 18 | rx_quantifier_prefix ::= 19 | '*' 20 | | '+' 21 | | '?' 22 | | '{' start:[0-9]+ (',' end:[0-9]*)? '}' 23 | ; 24 | 25 | rx_atom ::= 26 | value:rx_pattern_char+ 27 | | '.' 28 | | '(' "?=" rx_disjunction')' 29 | | '(' "?!" rx_disjunction ')' 30 | | '(' "?:" rx_disjunction ')' 31 | | '(' rx_disjunction ')' 32 | | '\\' rx_atom_escape 33 | | rx_character_class 34 | ; 35 | 36 | rx_pattern_char ::= ~[$\\.*+?()\[\]{}|^] sigma; 37 | 38 | rx_atom_escape ::= 39 | 40 | ( rx_decimal_escape 41 | | rx_character_escape 42 | | rx_character_class_escape 43 | | rx_identity_escape ) 44 | 45 | ; 46 | 47 | rx_identity_escape ::= '\\' value:sigma; 48 | 49 | rx_class_escape ::= 50 | rx_character_class_escape 51 | | rx_decimal_escape 52 | | 'b' 53 | | rx_character_escape 54 | ; 55 | 56 | rx_character_escape ::= 57 | [fnrtv] 58 | | 'c' value:[a-zA-Z] 59 | | 'x' value:[0-9a-fA-F][0-9a-fA-F] 60 | | 'u' value:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] 61 | | value:sigma 62 | ; 63 | 64 | rx_decimal_escape ::= value:[0-9]+; 65 | 66 | rx_character_class_escape ::= 67 | 'd' 68 | | 'D' 69 | | 's' 70 | | 'S' 71 | | 'w' 72 | | 'W' 73 | ; 74 | 75 | rx_character_class ::= 76 | '[' '^' rx_class_ranges ']' 77 | | '[' rx_class_ranges ']' 78 | ; 79 | 80 | rx_class_ranges ::= rx_nonempty_class_ranges?; 81 | 82 | rx_nonempty_class_ranges ::= 83 | rx_class_atom '-' rx_class_atom rx_class_ranges 84 | | rx_class_atom rx_nonempty_class_ranges_no_dash 85 | | rx_class_atom 86 | ; 87 | 88 | rx_nonempty_class_ranges_no_dash ::= 89 | rx_class_atom_no_dash '-' rx_class_atom rx_class_ranges 90 | | rx_class_atom rx_nonempty_class_ranges_no_dash 91 | | rx_class_atom 92 | ; 93 | 94 | rx_class_atom ::= 95 | '-' 96 | | rx_class_atom_no_dash 97 | ; 98 | 99 | rx_class_atom_no_dash ::= 100 | '\\' rx_class_escape 101 | | value:rx_source_char 102 | ; 103 | 104 | rx_source_char ::= ~[\\\]\-] sigma; 105 | -------------------------------------------------------------------------------- /source.ml: -------------------------------------------------------------------------------- 1 | (* Source *) 2 | 3 | open Ast;; 4 | open Conduit;; 5 | 6 | type treatment = Ign|Wrn|Err;; 7 | type view = Pos|Txt;; 8 | 9 | type source = { 10 | s_file : string; 11 | s_text : string; 12 | s_source : program; 13 | s_liner : Liner.t; 14 | s_ignorify : bool; 15 | s_warnify : bool 16 | };; 17 | 18 | type position = int * int;; 19 | 20 | module PS = Set.Make(struct type t = position let compare = compare end);; 21 | 22 | (*** scribe_position *) 23 | let scribe_position liner cd oc (i,j) = 24 | let j = j - 1 in 25 | let l_i = Liner.position_to_line liner i 26 | and l_j = Liner.position_to_line liner j 27 | in 28 | let (s_i, e_i) = Liner.line_to_range liner l_i 29 | and (s_j, _) = Liner.line_to_range liner l_j 30 | in 31 | let c_i = i - s_i 32 | and c_j = j - s_j 33 | in 34 | if l_i = l_j then 35 | if j = e_i - 1 then 36 | cd.cd_print oc "line %d" (l_i + 1) 37 | else 38 | if c_i = c_j then 39 | cd.cd_print oc "line %d, column %d" (l_i + 1) c_i 40 | else 41 | cd.cd_print oc "line %d, columns %d to %d" (l_i + 1) c_i c_j 42 | else 43 | cd.cd_print oc "line %d, column %d to line %d, column %d" (l_i + 1) c_i (l_j + 1) c_j 44 | ;; 45 | (* ***) 46 | (*** scribe_position_set *) 47 | let scribe_position_set liner cd oc set = 48 | cd.cd_print oc "{"; 49 | let first = ref true in 50 | PS.iter 51 | begin fun pos -> 52 | if !first then 53 | first := false 54 | else 55 | cd.cd_print oc ";"; 56 | 57 | cd.cd_print oc " %a" (scribe_position liner cd) pos 58 | end 59 | set; 60 | cd.cd_print oc " }" 61 | ;; 62 | (* ***) 63 | (*** slice_left *) 64 | let slice_left u i = 65 | let m = String.length u in 66 | if i <= 0 then 67 | ("", u) 68 | else 69 | if i >= m then 70 | (u, "") 71 | else 72 | (String.sub u 0 i, String.sub u i (m - i)) 73 | ;; 74 | (* ***) 75 | (*** slice_right *) 76 | let slice_right u i = 77 | let m = String.length u in 78 | if i <= 0 then 79 | (u, "") 80 | else 81 | if i >= m then 82 | ("", u) 83 | else 84 | (String.sub u 0 (m - i), String.sub u (m - i) i) 85 | ;; 86 | (* ***) 87 | (*** extract_lines *) 88 | let extract_lines liner text start_pos end_pos = 89 | let l_i = Liner.position_to_line liner start_pos 90 | and l_j = Liner.position_to_line liner (end_pos - 1) 91 | in 92 | let m = l_j - l_i + 1 in 93 | Array.init m 94 | begin fun i -> 95 | let l = l_i + i in 96 | let (x, y) = Liner.line_to_range liner l in 97 | let u = String.sub text x (y - x) in 98 | let (prefix, u) = 99 | if l = l_i then 100 | let c_i = start_pos - x in 101 | slice_left u c_i 102 | else 103 | "", u 104 | in 105 | let (u, suffix) = 106 | if l = l_j then 107 | let c_j = y - end_pos in 108 | slice_right u c_j 109 | else 110 | u, "" 111 | in 112 | (l, prefix, u, suffix) 113 | end 114 | (* ***) 115 | -------------------------------------------------------------------------------- /ast.ml: -------------------------------------------------------------------------------- 1 | (* AST *) 2 | 3 | open Conduit;; 4 | 5 | type info = { 6 | i_start : int; 7 | i_end : int; 8 | i_extra : bool 9 | };; 10 | 11 | let version = 8;; 12 | 13 | type program = source_element list 14 | and source_element = 15 | | St of int * int * st 16 | | FunDecl of int * int * func 17 | and block = st list 18 | and st = 19 | | Position of int * int * st 20 | | Expr of expr 21 | | If of expr * st * st option 22 | | Do of st * expr 23 | | While of expr * st 24 | | For of st option * st option * st option * st 25 | | Continue of label option 26 | | Break of label option 27 | | Return of expr option 28 | | With of expr * st 29 | | Labeled of label * st 30 | | Switch of expr * (case_clause list * st) list 31 | | Throw of expr 32 | | Try of st * (arg * st) option * st option 33 | | Variable of variable_declaration list 34 | | Block of st list 35 | | ForIn of lhs_or_var * expr * st 36 | | Nop 37 | and lhs_or_var = 38 | | LHS of expr 39 | | Vars of variable_declaration list 40 | and variable_declaration = name * expr option 41 | and case_clause = Default | Case of expr 42 | and func = name option * name list * source_element list 43 | and extra = int * int * extra_tag 44 | and extra_tag = 45 | | DanglingComma 46 | and expr = 47 | | Assign of expr * assignment_operator * expr 48 | | Sq of expr list 49 | | Function of int * int * func 50 | | L of litteral 51 | | U of unop * expr 52 | | B of binop * expr * expr 53 | | V of name 54 | | Object of (property_name * expr) list 55 | | Array of array_litteral 56 | | Apply of expr * expr list 57 | | Conditional of expr * expr * expr 58 | | This 59 | | Extra of extra 60 | and property_name = 61 | | PN_String of string 62 | | PN_Float of float 63 | | PN_Int of int32 64 | | PN_Empty 65 | and binop = 66 | | B_mul 67 | | B_div 68 | | B_mod 69 | | B_add 70 | | B_sub 71 | | B_le 72 | | B_ge 73 | | B_lt 74 | | B_gt 75 | | B_instanceof 76 | | B_in 77 | | B_equal 78 | | B_lsr 79 | | B_asr 80 | | B_lsl 81 | | B_notequal 82 | | B_physequal 83 | | B_physnotequal 84 | | B_bitand 85 | | B_bitor 86 | | B_bitxor 87 | | B_and 88 | | B_or 89 | | B_bracket 90 | and unop = 91 | | U_bitnot 92 | | U_delete 93 | | U_void 94 | | U_typeof 95 | | U_pre_increment 96 | | U_pre_decrement 97 | | U_post_increment 98 | | U_post_decrement 99 | | U_plus 100 | | U_minus 101 | | U_not 102 | | U_new 103 | and assignment_operator = 104 | | A_eq 105 | | A_mul 106 | | A_div 107 | | A_mod 108 | | A_add 109 | | A_sub 110 | | A_lsl 111 | | A_lsr 112 | | A_asr 113 | | A_and 114 | | A_xor 115 | | A_or 116 | and litteral = 117 | | Float of float 118 | | Int of int32 119 | | String of string 120 | | Regexp of string * string 121 | | Bool of bool 122 | | Null 123 | | Undefined 124 | and array_litteral = expr list 125 | and name = string 126 | and arg = string 127 | and label = string 128 | 129 | let info0 = { i_start = 0; i_end = max_int; i_extra = true };; 130 | 131 | let rec iter_over_expr_in_program info f p = List.iter (iter_over_expr_in_source_element info f) p 132 | and iter_over_expr_in_source_element info f = function 133 | | St(start_pos, end_pos, s) -> iter_over_expr_in_st { info with i_start = start_pos; i_end = end_pos } f s 134 | | FunDecl(_,_,(_,_,sl)) -> iter_over_expr_in_program info f sl 135 | and iter_over_expr_in_sto info f = function 136 | | None -> () 137 | | Some s -> iter_over_expr_in_st info f s 138 | and iter_over_expr_in_variable_declaration_list info f vl = 139 | List.iter (fun (_, xo) -> 140 | match xo with 141 | | None -> () 142 | | Some x -> f info x) vl 143 | and iter_over_expr_in_st info f = function 144 | | Position(start_pos, end_pos, s) -> iter_over_expr_in_st { info with i_start = start_pos; i_end = end_pos } f s 145 | | Expr x -> f info x 146 | | If(x, s1, so) -> f info x; iter_over_expr_in_st info f s1; 147 | begin 148 | match so with 149 | | None -> () 150 | | Some s -> iter_over_expr_in_st info f s 151 | end 152 | | Do(s, x)|While(x,s)|With(x,s) -> iter_over_expr_in_st info f s; f info x 153 | | For(so1, so2, so3, s) -> 154 | iter_over_expr_in_sto info f so1; 155 | iter_over_expr_in_sto info f so2; 156 | iter_over_expr_in_sto info f so3; 157 | iter_over_expr_in_st info f s 158 | | Return(Some x)|Throw x -> f info x 159 | | Continue _|Break _|Return None -> () 160 | | Labeled(_, s) -> iter_over_expr_in_st info f s 161 | | Switch(x, cls) -> 162 | f info x; 163 | List.iter 164 | begin fun (cl, s) -> 165 | iter_over_expr_in_st info f s; 166 | List.iter 167 | begin function 168 | | Default -> () 169 | | Case x -> f info x 170 | end 171 | cl 172 | end 173 | cls 174 | | Try(s, aso, so) -> 175 | iter_over_expr_in_st info f s; 176 | begin 177 | match aso with 178 | | None -> () 179 | | Some(_, s) -> iter_over_expr_in_st info f s 180 | end; 181 | begin 182 | match so with 183 | | None -> () 184 | | Some s -> iter_over_expr_in_st info f s 185 | end 186 | | Variable vl -> iter_over_expr_in_variable_declaration_list info f vl 187 | | Block sl -> List.iter (iter_over_expr_in_st info f) sl 188 | | ForIn(l, x, s) -> 189 | f info x; 190 | iter_over_expr_in_lhs info f l; 191 | iter_over_expr_in_st info f s 192 | | Nop -> () 193 | and iter_over_expr_in_lhs info f = function 194 | | LHS x -> f info x 195 | | Vars vl -> iter_over_expr_in_variable_declaration_list info f vl 196 | ;; 197 | 198 | (*** scribe_property_name *) 199 | let scribe_property_name cd oc = function 200 | | PN_String u -> cd.cd_print oc "%S" u 201 | | PN_Float f -> cd.cd_print oc "%f" f 202 | | PN_Int x -> cd.cd_print oc "%ld" x 203 | | PN_Empty -> cd.cd_print oc "*empty*" 204 | ;; 205 | (* ***) 206 | -------------------------------------------------------------------------------- /manual.txt: -------------------------------------------------------------------------------- 1 | Jsure 2 | ===== 3 | Copyright(C) 2007 Exalead SA. 4 | Released under the GNU Library General Public License. 5 | 6 | Description 7 | =========== 8 | Jsure is a "lint" for Javascript, which is also known as Ecmascript. 9 | It checks syntax and a little bit of semantics. 10 | 11 | 12 | Usage 13 | ===== 14 | You run jsure on your source files and jsure spits out ANSI-colored 15 | warnings and errors with excerpts from your code on your terminal. 16 | 17 | jsure [options] file_1.js file_2.js ... file_n.js 18 | 19 | This is equivalent to 20 | 21 | cat file_1.js ... file_n.js > file.js 22 | jsure [options] file.js 23 | 24 | except for the reported error locations. 25 | 26 | 27 | Actions 28 | ======= 29 | jsure will read and parse the input files. If there is any unparseable file, 30 | it will report an error and stop. 31 | 32 | By default, Jsure will only check the syntax. For more advanced checking, 33 | you must use on or more of the following options: 34 | -check : The most useful, all-purpose check (unused vars, etc.) 35 | -forbidden-properties : Some property names such as 'name' cause problems; 36 | jsure can detect static instances of those problematic names. 37 | -list-props : Will list all the (static) property names used in the 38 | file. 39 | 40 | Miscellaneous options 41 | ===================== 42 | -quiet Suppress informative messages 43 | -no-warnings Suppress warnings 44 | 45 | Changing behaviour on certain subdirectories 46 | ============================================ 47 | Errors and warnings produced by source files whose name starts with a prefix 48 | given by an -ignorify (resp. -warnify) option will be ignored (resp. transformed 49 | into warnings). 50 | 51 | 52 | Detection of anomalies 53 | ====================== 54 | Anomalies, such as using an uninitialized variable, are legal language 55 | constructs that are bad coding practice or likely errors. 56 | 57 | Each anomaly has an associated action, which can be changed with a one-letter 58 | flag by an option. The actions are ignore (i), warn (w) or error (e). 59 | 60 | 61 | Anomalies 62 | ========= 63 | 64 | -toplevel-bindings 65 | Setting global properties without a "var". 66 | 67 | -assigning-to-args 68 | Assigning to a function argument. 69 | 70 | -uninitialized-vars 71 | Using uninitialized variables. In Javascript, uninitialized variables are set 72 | to undefined. 73 | 74 | 75 | Unused identifiers 76 | ================== 77 | Jsure can detect unused variables (-unused-vars), arguments (-unused-args) or 78 | functions (-unused-funs). 79 | 80 | Sometimes it is unavoidable to have unused arguments - for example when you have 81 | to furnish a callback but have no need for all of its arguments. 82 | 83 | Jsure will ignore any variable, argument or function whose name starts with an 84 | underscore (_) will be ignored. (The rule for flagging unused variables can be 85 | changed with the -unused-ident-regexp option.) Using such a variable is an 86 | anomaly (-using-unused). 87 | 88 | 89 | Summary of anomalies 90 | ==================== 91 | -unused-args Function arguments unused in the body of the function. 92 | -using-unused Using variables or arguments lexically declared to be unused 93 | -unused-vars Unused variables 94 | -unused-funs Unused functions 95 | -shadowing-args Arguments shadowed by variables 96 | -dangling-commas Dangling commas in object and array litterals 97 | 98 | Not yet implemented: 99 | -unreachable-code Unreachable code 100 | 101 | 102 | Environment variables 103 | ===================== 104 | It is possible to change the behaviour of jsure thru the environment variables 105 | JSURE and JSURE_BEFORE. These variables define options to be processed before 106 | (JSURE_BEFORE) and after (JSURE) the processing of command-line options. 107 | 108 | The syntax for the environment variable is a little different than that of the 109 | command-line options. For instance, the set of command-line options 110 | 111 | -toplevel-bindings e -dont-catch 112 | 113 | would translate to 114 | 115 | JSURE="toplevel_bindings=e;dont_catch" 116 | 117 | In other words, the first dash (-) of the option names is to be removed, 118 | remaining dashes are to be replaced by underscores (_), options are to 119 | be separated by semicolons (;) and an equal sign (=) should separate 120 | option names from their arguments. 121 | 122 | 123 | Excerpts and colorization 124 | ========================= 125 | Jsure can show the context surrounding errors. This is settable with the 126 | -errors option: -errors t will display text, -errors p will only display the 127 | error position. The same thing applies to warnings with the -warnings option. 128 | 129 | Jsure can also brighten up your life by partially colorizing error or warning 130 | messages. These colors can be configured using the following options: 131 | 132 | -info-color Info color 133 | -warning-color Warning color 134 | -error-color Error color 135 | -number-color Line number color 136 | -code-color Code color 137 | -hl-color Code highlight color 138 | 139 | Here, is one of; 140 | black 141 | red 142 | green 143 | yellow 144 | blue 145 | magenta 146 | cyan 147 | white 148 | none 149 | 150 | and will produce corresponding ANSI escape sequences, which may or may not, 151 | given the configuration of your terminal, produce the expected colors. 152 | 153 | 154 | Caching parse trees 155 | =================== 156 | To accelerate parsing, use the -cache option; jsure will then 157 | cache parse trees in the file .dir, which will also be linked to 158 | .pag (this behaviour is defined by your system's DBM library). 159 | -------------------------------------------------------------------------------- /ecmalexer.lex: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "ecmaparser.tab.h" 3 | #include "ast.h" 4 | #include 5 | #include 6 | #undef YY_INPUT 7 | 8 | #define YY_INPUT(buf, result, max_size) do { \ 9 | if (js_parser_input_buffer_pos < js_parser_input_buffer_size) { \ 10 | unsigned int size = \ 11 | (js_parser_input_buffer_pos + (unsigned int) max_size < js_parser_input_buffer_size) ? \ 12 | (unsigned int) max_size : (unsigned int) (js_parser_input_buffer_size - js_parser_input_buffer_pos); \ 13 | memcpy(buf, js_parser_input_buffer, size); \ 14 | js_parser_input_buffer_pos += size; \ 15 | result = size; \ 16 | } else { \ 17 | buf[0] = EOF; \ 18 | result = 0; \ 19 | } \ 20 | } while(0) 21 | 22 | %} 23 | %% 24 | 25 | "\x09" {} 26 | "\x0B" {} 27 | "\x0C" {} 28 | "\x20" {} 29 | "\xA0" {} 30 | "\x0A" {} 31 | "\x0D" {} 32 | 33 | "