diff options
| author | Pascal Rigaux <pixel@mandriva.com> | 2002-11-24 00:07:31 +0000 | 
|---|---|---|
| committer | Pascal Rigaux <pixel@mandriva.com> | 2002-11-24 00:07:31 +0000 | 
| commit | 89de208360b9022db207e1af37bbae992f45002b (patch) | |
| tree | 5248de006e1270590407c7096437f616a83d2733 | |
| parent | 131207a1f99f85d2b8d272e7b47b058076b5c1cf (diff) | |
| download | perl_checker-89de208360b9022db207e1af37bbae992f45002b.tar perl_checker-89de208360b9022db207e1af37bbae992f45002b.tar.gz perl_checker-89de208360b9022db207e1af37bbae992f45002b.tar.bz2 perl_checker-89de208360b9022db207e1af37bbae992f45002b.tar.xz perl_checker-89de208360b9022db207e1af37bbae992f45002b.zip  | |
*** empty log message ***
| -rw-r--r-- | perl_checker.src/.cvsignore | 14 | ||||
| -rw-r--r-- | perl_checker.src/Makefile | 4 | ||||
| -rw-r--r-- | perl_checker.src/common.ml | 62 | ||||
| -rw-r--r-- | perl_checker.src/common.mli | 10 | ||||
| -rw-r--r-- | perl_checker.src/flags.ml | 5 | ||||
| -rw-r--r-- | perl_checker.src/flags.mli | 3 | ||||
| -rw-r--r-- | perl_checker.src/info.ml | 11 | ||||
| -rw-r--r-- | perl_checker.src/info.mli | 1 | ||||
| -rw-r--r-- | perl_checker.src/lexer.mll | 45 | ||||
| -rw-r--r-- | perl_checker.src/parser.mly | 345 | ||||
| -rw-r--r-- | perl_checker.src/parser_helper.ml | 119 | ||||
| -rw-r--r-- | perl_checker.src/parser_helper.mli | 29 | ||||
| -rw-r--r-- | perl_checker.src/perl_checker.ml | 83 | ||||
| -rw-r--r-- | perl_checker.src/tree.ml | 424 | ||||
| -rw-r--r-- | perl_checker.src/tree.mli | 32 | ||||
| -rw-r--r-- | perl_checker.src/types.mli | 11 | 
16 files changed, 925 insertions, 273 deletions
diff --git a/perl_checker.src/.cvsignore b/perl_checker.src/.cvsignore index 9d76706..d715755 100644 --- a/perl_checker.src/.cvsignore +++ b/perl_checker.src/.cvsignore @@ -1,9 +1,13 @@ -.depend -perl_checker -perl_checker_debug -gmon.out +._bcdi +._d +._ncdi  *.cmi  *.cmo  *.cmx +perl_checker +perl_checker_debug +gmon.out +lexer.ml  parser.ml -parser.output
\ No newline at end of file +parser.mli +parser.output diff --git a/perl_checker.src/Makefile b/perl_checker.src/Makefile index 9f66e4a..9b33410 100644 --- a/perl_checker.src/Makefile +++ b/perl_checker.src/Makefile @@ -4,7 +4,7 @@ YFLAGS = -v  TRASH = parser.output TAGS  RESULT  = perl_checker  BCSUFFIX = _debug -SOURCES = common.ml types.mli info.ml parser_helper.ml parser.mly lexer.mll perl_checker.ml +SOURCES = common.ml flags.ml types.mli info.ml parser_helper.ml parser.mly lexer.mll tree.ml perl_checker.ml  LIBS = unix  NAME = shyant @@ -17,4 +17,4 @@ tags:  TAGS:   	ocamltags *.ml --include OCamlMakefile
\ No newline at end of file +-include OCamlMakefile diff --git a/perl_checker.src/common.ml b/perl_checker.src/common.ml index c4600ff..439c460 100644 --- a/perl_checker.src/common.ml +++ b/perl_checker.src/common.ml @@ -37,7 +37,9 @@ let has_env var =      let _ = Sys.getenv var in true    with Not_found -> false -let some = function Some e -> e | None -> failwith "some" +let some = function  +  | Some e -> e +  | None -> failwith "some"  let some_or = function    | None -> id @@ -185,6 +187,12 @@ let rec stack2list s =    let l = ref [] in    Stack.iter (fun e -> l := e :: !l) s ;    !l +   +let rec stack_exists f s = +  try +    Stack.iter (fun e -> if f e then raise Found) s ; +    false +  with Found -> true  let rec queue2list q = rev (Queue.fold (fun b a -> a :: b) [] q) @@ -200,6 +208,11 @@ let rec fix_point_ nb f p =    let p' = f p in    if p = p' then p, nb else fix_point_ (nb+1) f p' +let rec group_by_2 = function +  | [] -> [] +  | a :: b :: l -> (a, b) :: group_by_2 l +  | _ -> failwith "group_by_2" +  (*  let rec lfix_point f e =    let e' = f(e) in @@ -647,6 +660,10 @@ let chomps s =    while !i >= 0 && (s.[!i] = ' ' || s.[!i] = '\t') do decr i done ;    String.sub s 0 (!i+1) +let rec times e = function +  | 0 -> [] +  | n -> e :: times e (n-1) +  let skip_n_char_ beg end_ s =    String.sub s beg (String.length s - beg - end_)  let skip_n_char n s = skip_n_char_ n 0 s @@ -668,12 +685,43 @@ let is_lowercase c = Char.uppercase c <> c  let starts_with_non_lowercase s = s <> "" && s.[0] <> '_' && not (is_lowercase s.[0]) -let get_package_name s =  -  try Some (String.sub s 0 (String.rindex s ':' - 1)) with Not_found -> None - -let split_at_two_colons s = -  let i_fq = String.rindex s ':' in -  String.sub s 0 (i_fq - 1), skip_n_char (i_fq + 1) s +let rec fold_lines f init chan = +  try  +    let line = input_line chan in  +    fold_lines f (f init line) chan +  with End_of_file -> init +let readlines chan = List.rev (fold_lines (fun l e -> e::l) [] chan) + +let split_at c s = +  let rec split_at_ accu i = +    try +      let i' = String.index_from s i c in +      split_at_ (String.sub s i (i' - i) :: accu) (i'+1) +    with Not_found -> rev (skip_n_char i s :: accu) +  in +  split_at_ [] 0 + +let split_at2 c1 c2 s = +  let rec split_at2_ accu i i2 = +    try +      let i3 = String.index_from s i2 c1 in +      if s.[i3+1] = c2 then split_at2_ (String.sub s i (i3 - i) :: accu) (i3+2) (i3+2) else +      split_at2_ accu i i3 +    with Not_found | Invalid_argument _ -> rev (skip_n_char i s :: accu) +  in +  split_at2_ [] 0 0 + +let words s = +  let rec words_ accu i = +    try +      let i2 = non_index_from s i ' ' in +      try +	let i3 = String.index_from s i2 ' ' in +	words_ (String.sub s i2 (i3 - i2) :: accu) (i3+1) +      with Not_found -> rev (skip_n_char i2 s :: accu) +    with Invalid_argument _ -> rev accu +  in +  words_ [] 0  let to_CamelCase s_ =    let l = ref [] in diff --git a/perl_checker.src/common.mli b/perl_checker.src/common.mli index 5398092..1e8078b 100644 --- a/perl_checker.src/common.mli +++ b/perl_checker.src/common.mli @@ -42,10 +42,12 @@ val fold_right1 : ('a -> 'a -> 'a) -> 'a list -> 'a  val for_all2_ : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool  val maxl : 'a list -> 'a  val stack2list : 'a Stack.t -> 'a list +val stack_exists : ('a -> bool) -> 'a Stack.t -> bool  val queue2list : 'a Queue.t -> 'a list  val fix_point : ('a -> 'a) -> 'a -> 'a  val fix_point_withenv : ('a -> 'b -> 'b * 'a) -> 'a -> 'b -> 'b * 'a  val fix_point_ : int -> ('a -> 'a) -> 'a -> 'a * int +val group_by_2 : 'a list -> ('a * 'a) list  val do0_withenv :    (('a -> unit) -> 'b -> 'c) -> ('d -> 'a -> 'd) -> 'd -> 'b -> 'd  val do0_withenv2 : @@ -176,6 +178,7 @@ val str_contains : string -> string -> bool  val str_ends_with : string -> string -> bool  val chop : string -> string  val chomps : string -> string +val times : 'a -> int -> 'a list  val skip_n_char_ : int -> int -> string -> string  val skip_n_char : int -> string -> string  val non_index_from : string -> int -> char -> int @@ -186,8 +189,11 @@ val explode_string : string -> char list  val is_uppercase : char -> bool  val is_lowercase : char -> bool  val starts_with_non_lowercase : string -> bool -val get_package_name : string -> string option -val split_at_two_colons : string -> string * string +val fold_lines : ('a -> string -> 'a) -> 'a -> in_channel -> 'a +val readlines : in_channel -> string list +val split_at : char -> string -> string list +val split_at2 : char -> char -> string -> string list +val words : string -> string list  val to_CamelCase : string -> string option  val string_of_ref : 'a ref -> string  val is_int : float -> bool diff --git a/perl_checker.src/flags.ml b/perl_checker.src/flags.ml index e69de29..b2c40c2 100644 --- a/perl_checker.src/flags.ml +++ b/perl_checker.src/flags.ml @@ -0,0 +1,5 @@ +open Common + +let verbose = ref false +let quiet = ref false + diff --git a/perl_checker.src/flags.mli b/perl_checker.src/flags.mli index 8b13789..9f17f55 100644 --- a/perl_checker.src/flags.mli +++ b/perl_checker.src/flags.mli @@ -1 +1,2 @@ - +val verbose : bool ref +val quiet : bool ref diff --git a/perl_checker.src/info.ml b/perl_checker.src/info.ml index d15d3c0..947dc50 100644 --- a/perl_checker.src/info.ml +++ b/perl_checker.src/info.ml @@ -2,18 +2,21 @@ open List  open Printf  open Common -let (lines_starts : (string * int list ref) list ref) = ref [] +let (lines_starts : (string, int list) Hashtbl.t) = Hashtbl.create 4  let current_file_lines_starts = ref []  let current_file_current_line = ref 0  let current_file = ref ""  let start_a_new_file file =  +  if !current_file <> "" then Hashtbl.add lines_starts !current_file !current_file_lines_starts ;    current_file := file ; -  current_file_lines_starts := [0] ; -  lines_starts := (file, current_file_lines_starts) :: !lines_starts +  current_file_lines_starts := [0] + +let get_lines_starts_for_file file = +  if file = !current_file then !current_file_lines_starts else Hashtbl.find lines_starts file  let raw_pos2raw_line file a = -  let starts = map_index (fun a b -> a,b) (rev !(assoc file !lines_starts)) in +  let starts = map_index (fun a b -> a,b) (rev (get_lines_starts_for_file file)) in    let ((offset, line), _) = find (fun (_,(e,_)) -> e > a) (combine starts (tl starts @ [999999999, 999999999])) in    line, offset diff --git a/perl_checker.src/info.mli b/perl_checker.src/info.mli index 6796a62..4082306 100644 --- a/perl_checker.src/info.mli +++ b/perl_checker.src/info.mli @@ -1,4 +1,3 @@ -val lines_starts : (string * int list ref) list ref  val current_file_lines_starts : int list ref  val current_file_current_line : int ref  val current_file : string ref diff --git a/perl_checker.src/lexer.mll b/perl_checker.src/lexer.mll index 3ebce72..e54e968 100644 --- a/perl_checker.src/lexer.mll +++ b/perl_checker.src/lexer.mll @@ -18,7 +18,7 @@ type raw_token =    | BAREWORD of (string * raw_pos)    | BAREWORD_PAREN of (string * raw_pos)    | REVISION of (string * raw_pos) -  | COMMENT of (string * raw_pos) +  | PERL_CHECKER_COMMENT of (string * raw_pos)    | POD of (string * raw_pos)    | LABEL of (string * raw_pos)    | COMMAND_STRING of (raw_interpolated_string * raw_pos) @@ -38,7 +38,7 @@ type raw_token =    | ARRAYLEN_IDENT of (string option * string * raw_pos)    | FUNC_DECL_WITH_PROTO of (string * string * raw_pos) -  | IF of raw_pos | ELSIF of raw_pos | ELSE of raw_pos | UNLESS of raw_pos | DO of raw_pos | WHILE of raw_pos | UNTIL of raw_pos | MY of raw_pos | CONTINUE of raw_pos | SUB of raw_pos +  | IF of raw_pos | ELSIF of raw_pos | ELSE of raw_pos | UNLESS of raw_pos | DO of raw_pos | WHILE of raw_pos | UNTIL of raw_pos | MY_OUR of (string * raw_pos) | CONTINUE of raw_pos | SUB of raw_pos    | LOCAL of raw_pos | FOR of (string * raw_pos) | USE of raw_pos | PACKAGE of raw_pos | BEGIN of raw_pos | END of raw_pos | PRINT of (string * raw_pos)     | NEW of (raw_pos) | FORMAT of (raw_pos) | AT of raw_pos | DOLLAR of raw_pos | PERCENT of raw_pos | AMPERSAND of raw_pos    | STAR of raw_pos | ARRAYLEN of raw_pos | SEMI_COLON of raw_pos | PKG_SCOPE of raw_pos | PAREN of raw_pos | PAREN_END of raw_pos | BRACKET of raw_pos @@ -72,7 +72,7 @@ let rec raw_token_to_pos_and_token spaces = function    | BAREWORD(s, pos) -> pos, Parser.BAREWORD(s, (spaces, pos))    | BAREWORD_PAREN(s, pos) -> pos, Parser.BAREWORD_PAREN(s, (spaces, pos))    | REVISION(s, pos) -> pos, Parser.REVISION(s, (spaces, pos)) -  | COMMENT(s, pos) -> pos, Parser.COMMENT(s, (spaces, pos)) +  | PERL_CHECKER_COMMENT(s, pos) -> pos, Parser.PERL_CHECKER_COMMENT(s, (spaces, pos))    | POD(s, pos) -> pos, Parser.POD(s, (spaces, pos))    | LABEL(s, pos) -> pos, Parser.LABEL(s, (spaces, pos))    | PRINT(s, pos) -> pos, Parser.PRINT(s, (spaces, pos)) @@ -102,6 +102,7 @@ let rec raw_token_to_pos_and_token spaces = function    | BIT_SHIFT(s, pos) -> pos, Parser.BIT_SHIFT(s, (spaces, pos))    | PLUS(s, pos) -> pos, Parser.PLUS(s, (spaces, pos))    | ONE_SCALAR_PARA(s, pos) -> pos, Parser.ONE_SCALAR_PARA(s, (spaces, pos)) +  | MY_OUR(s, pos) -> pos, Parser.MY_OUR(s, (spaces, pos))    | EOF              (pos) -> pos, Parser.EOF              ((), (spaces, pos))    | IF               (pos) -> pos, Parser.IF               ((), (spaces, pos)) @@ -111,7 +112,6 @@ let rec raw_token_to_pos_and_token spaces = function    | DO               (pos) -> pos, Parser.DO               ((), (spaces, pos))    | WHILE            (pos) -> pos, Parser.WHILE            ((), (spaces, pos))    | UNTIL            (pos) -> pos, Parser.UNTIL            ((), (spaces, pos)) -  | MY               (pos) -> pos, Parser.MY               ((), (spaces, pos))    | CONTINUE         (pos) -> pos, Parser.CONTINUE         ((), (spaces, pos))    | SUB              (pos) -> pos, Parser.SUB              ((), (spaces, pos))    | LOCAL            (pos) -> pos, Parser.LOCAL            ((), (spaces, pos)) @@ -281,6 +281,10 @@ let ident_type_from_char fq name lexbuf c =    | '*' -> STAR_IDENT  (fq, name, pos lexbuf)    | _ -> internal_error "ident_type_from_char" +let split_at_two_colons s = +  let i_fq = String.rindex s ':' in +  String.sub s 0 (i_fq - 1), skip_n_char (i_fq + 1) s +  let ident_from_lexbuf lexbuf =     let fq, name = split_at_two_colons (lexeme lexbuf) in    RAW_IDENT(Some fq, name, pos lexbuf) @@ -325,6 +329,7 @@ rule token = parse      if lexeme_start lexbuf = !not_ok_for_match then not_ok_for_match := lexeme_end lexbuf;       SPACE(lexeme_end lexbuf - lexeme_start lexbuf)    } +| "# perl_checker: " [^ '\n']* { PERL_CHECKER_COMMENT(skip_n_char 16 (lexeme lexbuf), pos lexbuf) }  | '#' [^ '\n']* { SPACE(1) }  | "\n=" {  @@ -395,7 +400,8 @@ rule token = parse  | "until"    { UNTIL(pos lexbuf) }  | "foreach"  { FOR(lexeme lexbuf, pos lexbuf) }  | "for"      { FOR(lexeme lexbuf, pos lexbuf) } -| "my"       { MY(pos lexbuf) } +| "my"       { MY_OUR(lexeme lexbuf, pos lexbuf) } +| "our"      { MY_OUR(lexeme lexbuf, pos lexbuf) }  | "local"    { LOCAL(pos lexbuf) }  | "continue" { CONTINUE(pos lexbuf) }  | "sub"      { SUB(pos lexbuf) } @@ -413,11 +419,11 @@ rule token = parse  | "print " ident ' ' {       putback lexbuf 1;  -    PRINT_TO_STAR(skip_n_char_ 6 1 (lexeme lexbuf), pos lexbuf); +    PRINT_TO_STAR(skip_n_char 6 (lexeme lexbuf), pos lexbuf);    }  | "print $" ident ' ' {       putback lexbuf 1;  -    PRINT_TO_SCALAR(skip_n_char_ 7 1 (lexeme lexbuf), pos lexbuf); +    PRINT_TO_SCALAR(skip_n_char 7 (lexeme lexbuf), pos lexbuf);    }  | ident ' '* "=>" { (* needed so that (if => 1) works *) @@ -556,7 +562,7 @@ rule token = parse  | '$' [^ '{' ' ' '\n' '$']  | "$^" [^ '{' ' ' '\n'] { typed_ident_from_lexbuf lexbuf } -| "$$" [^ 'a'-'z' 'A'-'Z' '_' '{'] { putback lexbuf 1; SCALAR_IDENT(None, "$$", pos lexbuf) } +| "$$" [^ 'a'-'z' 'A'-'Z' '_' '{'] { putback lexbuf 1; SCALAR_IDENT(None, "$", pos lexbuf) }  | stash "::" { putback lexbuf 2; ident_type_from_char None "main" lexbuf (lexeme_char lexbuf 0) } @@ -612,7 +618,7 @@ and string = parse  and delimited_string = parse  | '\\' { Stack.push delimited_string next_rule ; string_escape lexbuf }  | '$'  { Stack.push delimited_string next_rule ; delimited_string_interpolate_scalar lexbuf } -| '@'  { Stack.push delimited_string next_rule ; string_interpolate_array lexbuf } +| '@'  { Stack.push delimited_string next_rule ; delimited_string_interpolate_array lexbuf }  | '\n' {       add_a_new_line(lexeme_end lexbuf);      next delimited_string lexbuf @@ -738,15 +744,14 @@ and delimited_string_interpolate_scalar = parse (* needed for delimited string l     die lexbuf (Printf.sprintf "I really can't handle this, [xxx] can be indexing or not based on stellar position :-(")    } -  | "{"  | ident "->"? '{'  | eof { next_s "$" (Stack.pop next_rule) lexbuf }  | _ {       let c = lexeme_char lexbuf 0 in -    if c <> !delimit_char && c <> '|' && c<>')' && c<>'/' then warn lexbuf (Printf.sprintf "weird \"%s\" in string" (lexeme lexbuf));  +    if c <> !delimit_char && c <> '|' && c<>')' && c<>'/' && c<>' ' then warn lexbuf (Printf.sprintf "weird \"%s\" in string" (lexeme lexbuf));       putback lexbuf 1; -    (Stack.pop next_rule) lexbuf  +    next_s "$" (Stack.pop next_rule) lexbuf    }  and string_interpolate_array = parse @@ -754,10 +759,24 @@ and string_interpolate_array = parse  | '{' [^ '{' '}']* '}'  | (ident | (ident? ("::" ident)+)) { string_interpolate token "@" lexbuf } -| [ '*' '<' '>' ']' '.' '('] { next_s ("@" ^ lexeme lexbuf) (Stack.pop next_rule) lexbuf } +| [ '@' '*' '<' '>' ']' '.' '('] { next_s ("@" ^ lexeme lexbuf) (Stack.pop next_rule) lexbuf }  | eof { next_s "$" (Stack.pop next_rule) lexbuf }  | _ { warn lexbuf (Printf.sprintf "weird \"%s\" in string" (lexeme lexbuf)); next_s ("$" ^ lexeme lexbuf) (Stack.pop next_rule) lexbuf } +and delimited_string_interpolate_array = parse +| '$' ident +| '{' [^ '{' '}']* '}' +| (ident | (ident? ("::" ident)+)) { string_interpolate token "@" lexbuf } + +| [ '@' '*' '<' '>' ']' '.' '('] { next_s ("@" ^ lexeme lexbuf) (Stack.pop next_rule) lexbuf } +| eof { next_s "$" (Stack.pop next_rule) lexbuf } +| _ {  +    let c = lexeme_char lexbuf 0 in +    if c <> !delimit_char then warn lexbuf (Printf.sprintf "weird \"%s\" in string" (lexeme lexbuf)); +    putback lexbuf 1; +    next_s "$" (Stack.pop next_rule) lexbuf +  } +  and pattern_options = parse  | [ 'g' 'i' 'm' 'o' 's' 'x' 'e' 'd' ] { next pattern_options lexbuf }  | _ { putback lexbuf 1; () } diff --git a/perl_checker.src/parser.mly b/perl_checker.src/parser.mly index 05bdfe4..2114de6 100644 --- a/perl_checker.src/parser.mly +++ b/perl_checker.src/parser.mly @@ -12,7 +12,7 @@  %token <unit   * (Types.spaces * Types.raw_pos)> EOF -%token <string * (Types.spaces * Types.raw_pos)> NUM RAW_STRING BAREWORD BAREWORD_PAREN REVISION COMMENT POD LABEL PRINT_TO_STAR PRINT_TO_SCALAR ONE_SCALAR_PARA +%token <string * (Types.spaces * Types.raw_pos)> NUM RAW_STRING BAREWORD BAREWORD_PAREN REVISION COMMENT POD LABEL PERL_CHECKER_COMMENT PRINT_TO_STAR PRINT_TO_SCALAR ONE_SCALAR_PARA  %token <string * (Types.spaces * Types.raw_pos)> QUOTEWORDS COMPACT_HASH_SUBSCRIPT  %token <(string * Types.raw_pos) * (Types.spaces * Types.raw_pos)> RAW_HERE_DOC  %token <(string * ((int * int) * token) list) list * (Types.spaces * Types.raw_pos)> STRING COMMAND_STRING @@ -27,9 +27,9 @@  %token <string * (Types.spaces * Types.raw_pos)> FOR PRINT  %token <unit   * (Types.spaces * Types.raw_pos)> NEW FORMAT  %token <string * (Types.spaces * Types.raw_pos)> COMPARE_OP EQ_OP -%token <string * (Types.spaces * Types.raw_pos)> ASSIGN +%token <string * (Types.spaces * Types.raw_pos)> ASSIGN MY_OUR -%token <unit   * (Types.spaces * Types.raw_pos)> IF ELSIF ELSE UNLESS DO WHILE UNTIL MY CONTINUE SUB LOCAL +%token <unit   * (Types.spaces * Types.raw_pos)> IF ELSIF ELSE UNLESS DO WHILE UNTIL CONTINUE SUB LOCAL  %token <unit   * (Types.spaces * Types.raw_pos)> USE PACKAGE BEGIN END  %token <unit   * (Types.spaces * Types.raw_pos)> AT DOLLAR PERCENT AMPERSAND STAR ARRAYLEN  %token <unit   * (Types.spaces * Types.raw_pos)> SEMI_COLON PKG_SCOPE @@ -89,81 +89,85 @@  %left PAREN PREC_HIGH  %left ARRAYREF BRACKET -%type <Types.fromparser list> prog inside +%type <Types.fromparser list> prog  %type <(Types.priority * Types.fromparser) * (Types.spaces * Types.raw_pos)> expr term -%start prog inside +%start prog  %% -prog: lines EOF {check_package (fst $1); fst $1} -inside: lines EOF {fst $1} +prog: lines EOF {fst $1}  lines: /* A collection of "lines" in the program */  | {[], (Space_none, bpos)}  | sideff {[fst $1], snd $1} -| line lines {fst $1 @ fst $2, pos_range $1 $2} +| line lines {fst $1 @ fst $2, sp_pos_range $1 $2}  line:  | decl {[fst $1], snd $1}  | if_then_else {[fst $1], snd $1}  | loop {[fst $1], snd $1}  | LABEL {sp_cr($1); [Label(fst $1)], snd $1} +| PERL_CHECKER_COMMENT {sp_p($1); [Perl_checker_comment(fst $1, get_pos $1)], snd $1}  | semi_colon {[Semi_colon], snd $1}  | sideff semi_colon {[fst $1 ; Semi_colon], snd $1} -| BRACKET lines BRACKET_END {check_block_sub $2 $3; [Block(fst $2)], pos_range $1 $3} +| BRACKET lines BRACKET_END {check_block_sub $2 $3; [Block(fst $2)], sp_pos_range $1 $3}  if_then_else: /* Real conditional expressions */ -| IF     PAREN expr PAREN_END BRACKET lines BRACKET_END elsif else_ {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("if",     prio_lo P_loose $3 :: Block(fst $6) :: fst $8 @ fst $9), pos_range $1 $9} -| UNLESS PAREN expr PAREN_END BRACKET lines BRACKET_END elsif else_ {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("unless", prio_lo P_loose $3 :: Block(fst $6) :: fst $8 @ fst $9), pos_range $1 $9} +| IF     PAREN expr PAREN_END BRACKET lines BRACKET_END elsif else_ {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("if",     prio_lo P_loose $3 :: Block(fst $6) :: fst $8 @ fst $9), sp_pos_range $1 $9} +| UNLESS PAREN expr PAREN_END BRACKET lines BRACKET_END elsif else_ {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("unless", prio_lo P_loose $3 :: Block(fst $6) :: fst $8 @ fst $9), sp_pos_range $1 $9}  elsif:  |  {[], (Space_none, bpos)} -| ELSIF PAREN expr PAREN_END BRACKET lines BRACKET_END elsif {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; prio_lo P_loose $3 :: Block(fst $6) :: fst $8, pos_range $1 $8} +| ELSIF PAREN expr PAREN_END BRACKET lines BRACKET_END elsif {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; prio_lo P_loose $3 :: Block(fst $6) :: fst $8, sp_pos_range $1 $8}  else_:   |            { [], (Space_none, bpos) } -| ELSE BRACKET lines BRACKET_END {sp_p($1); sp_n($2); check_block_sub $3 $4; [Block(fst $3)], pos_range $1 $4} +| ELSE BRACKET lines BRACKET_END {sp_p($1); sp_n($2); check_block_sub $3 $4; [Block(fst $3)], sp_pos_range $1 $4}  loop: -| WHILE PAREN expr PAREN_END BRACKET lines BRACKET_END cont {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("while", [ prio_lo P_loose $3; Block(fst $6) ]), pos_range $1 $8} -| UNTIL PAREN expr PAREN_END BRACKET lines BRACKET_END cont {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("until", [ prio_lo P_loose $3; Block(fst $6) ]), pos_range $1 $8} -| FOR MY SCALAR_IDENT PAREN expr PAREN_END BRACKET lines BRACKET_END cont {sp_p($1); check_foreach($1); sp_n($4); sp_0($5); sp_0_or_cr($6); sp_p($7); check_block_sub $8 $9; Call_op("foreach my", [ to_Ident $3; prio_lo P_loose $5; Block(fst $8) ]), pos_range $1 $10} +| WHILE PAREN expr PAREN_END BRACKET lines BRACKET_END cont {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("while", [ prio_lo P_loose $3; Block(fst $6) ]), sp_pos_range $1 $8} +| UNTIL PAREN expr PAREN_END BRACKET lines BRACKET_END cont {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; Call_op("until", [ prio_lo P_loose $3; Block(fst $6) ]), sp_pos_range $1 $8} +| FOR PAREN expr_or_empty semi_colon expr_or_empty semi_colon expr_or_empty PAREN_END BRACKET lines BRACKET_END {sp_p($1); check_for($1); sp_n($2); sp_0($3); sp_p($5); sp_p($7); sp_0($8); sp_n($9); check_block_sub $10 $11; Call_op("for", [ fst $3; fst $5; fst $7; Block(fst $10) ]), sp_pos_range $1 $11}  | FOR SCALAR_IDENT PAREN expr PAREN_END BRACKET lines BRACKET_END cont { die_rule "don't use for without \"my\"ing the iteration variable" } -| FOR PAREN expr PAREN_END BRACKET lines BRACKET_END cont {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; check_foreach($1); Call_op("foreach", [ prio_lo P_loose $3; Block(fst $6) ]), pos_range $1 $8} -| FOR PAREN expr_or_empty semi_colon expr_or_empty semi_colon expr_or_empty PAREN_END BRACKET lines BRACKET_END {sp_p($1); check_for($1); sp_n($2); sp_0($3); sp_p($5); sp_p($7); sp_0($8); sp_n($9); check_block_sub $10 $11; Call_op("for", [ fst $3; fst $5; fst $7; Block(fst $10) ]), pos_range $1 $11} +| FOR PAREN expr PAREN_END BRACKET lines BRACKET_END cont {sp_p($1); sp_n($2); sp_0($3); sp_0_or_cr($4); sp_p($5); check_block_sub $6 $7; check_foreach($1); Call_op("foreach", [ prio_lo P_loose $3; Block(fst $6) ]), sp_pos_range $1 $8} +| for_my lines BRACKET_END cont {check_block_sub $2 $3; Call_op("foreach my", fst $1 @ [ Block(fst $2) ]), sp_pos_range $1 $4} + +for_my: +| FOR MY_OUR SCALAR_IDENT PAREN expr PAREN_END BRACKET {sp_p($1); check_my($2); check_foreach($1); sp_n($4); sp_0($5); sp_0_or_cr($6); sp_p($7); [ My_our(fst $2, [I_scalar, sndfst $3], get_pos $3); prio_lo P_loose $5 ], sp_pos_range $1 $7} +  cont: /* Continue blocks */  |  {(), (Space_none, bpos)} -| CONTINUE BRACKET lines BRACKET_END {sp_p($1); sp_n($2); check_block_sub $3 $4; (), pos_range $1 $4} +| CONTINUE BRACKET lines BRACKET_END {sp_p($1); sp_n($2); check_block_sub $3 $4; (), sp_pos_range $1 $4}  sideff: /* An expression which may have a side-effect */  | expr  {sndfst $1, snd $1} -| expr   IF    expr {sp_p($2); sp_p($3);                    Call_op("if infix"    , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), pos_range $1 $3} -| expr UNLESS  expr {sp_p($2); sp_p($3);                    Call_op("unless infix", [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), pos_range $1 $3} -| expr  WHILE  expr {sp_p($2); sp_p($3);                    Call_op("while infix" , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), pos_range $1 $3} -| expr  UNTIL  expr {sp_p($2); sp_p($3);                    Call_op("until infix" , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), pos_range $1 $3} -| expr  FOR    expr {sp_p($2); sp_p($3); check_foreach($2); Call_op("for infix"   , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), pos_range $1 $3} +| expr   IF    expr {sp_p($2); sp_p($3);                    Call_op("if infix"    , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), sp_pos_range $1 $3} +| expr UNLESS  expr {sp_p($2); sp_p($3);                    Call_op("unless infix", [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), sp_pos_range $1 $3} +| expr  WHILE  expr {sp_p($2); sp_p($3);                    Call_op("while infix" , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), sp_pos_range $1 $3} +| expr  UNTIL  expr {sp_p($2); sp_p($3);                    Call_op("until infix" , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), sp_pos_range $1 $3} +| expr  FOR    expr {sp_p($2); sp_p($3); check_foreach($2); Call_op("for infix"   , [ prio_lo P_loose $1 ; prio_lo P_loose $3 ]), sp_pos_range $1 $3}  decl: -| FORMAT BAREWORD ASSIGN {Too_complex, pos_range $1 $3} -| FORMAT ASSIGN {Too_complex, pos_range $1 $2} +| FORMAT BAREWORD ASSIGN {Too_complex, sp_pos_range $1 $3} +| FORMAT ASSIGN {Too_complex, sp_pos_range $1 $2}  | func_decl semi_colon {die_rule (if sndfst $1 = "" then "there is no need to pre-declare in Perl!" else "please don't use prototype pre-declaration") } -| func_decl BRACKET BRACKET_END {sp_n($2); sp_0_or_cr($3); let name, proto = fst $1 in sub_declaration (name, proto) [], pos_range $1 $3} -| func_decl BRACKET lines BRACKET_END {sp_n($2); check_block_sub $3 $4; sub_declaration (fst $1) (fst $3), pos_range $1 $4} -| func_decl BRACKET BRACKET expr BRACKET_END            BRACKET_END {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sub_declaration (fst $1) [Ref(I_hash, prio_lo P_loose $4)], pos_range $1 $6} -| func_decl BRACKET BRACKET expr BRACKET_END semi_colon BRACKET_END {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($7); sub_declaration (fst $1) [Ref(I_hash, prio_lo P_loose $4); Semi_colon], pos_range $1 $7} -| PACKAGE word semi_colon {sp_0_or_cr($1); sp_1($2); Package(fst $2), pos_range $1 $3} -| BEGIN BRACKET lines BRACKET_END {sp_0_or_cr($1); sp_1($2); check_block_sub $3 $4; Sub_declaration(Ident(None, "BEGIN", get_pos $1), "", fst $3), pos_range $1 $4} -| END   BRACKET lines BRACKET_END {sp_0_or_cr($1); sp_1($2); check_block_sub $3 $4; Sub_declaration(Ident(None, "END",   get_pos $1), "", fst $3), pos_range $1 $4} +| func_decl BRACKET BRACKET_END {sp_n($2); sp_0_or_cr($3); let name, proto = fst $1 in sub_declaration (name, proto) [], sp_pos_range $1 $3} +| func_decl BRACKET lines BRACKET_END {sp_n($2); check_block_sub $3 $4; sub_declaration (fst $1) (fst $3), sp_pos_range $1 $4} +| func_decl BRACKET BRACKET expr BRACKET_END            BRACKET_END {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sub_declaration (fst $1) [Ref(I_hash, prio_lo P_loose $4)], sp_pos_range $1 $6} +| func_decl BRACKET BRACKET expr BRACKET_END semi_colon BRACKET_END {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($7); sub_declaration (fst $1) [Ref(I_hash, prio_lo P_loose $4); Semi_colon], sp_pos_range $1 $7} +| PACKAGE word semi_colon {sp_0_or_cr($1); sp_1($2); Package(fst $2), sp_pos_range $1 $3} +| BEGIN BRACKET lines BRACKET_END {sp_0_or_cr($1); sp_1($2); check_block_sub $3 $4; Sub_declaration(Ident(None, "BEGIN", get_pos $1), "", Block(fst $3)), sp_pos_range $1 $4} +| END   BRACKET lines BRACKET_END {sp_0_or_cr($1); sp_1($2); check_block_sub $3 $4; Sub_declaration(Ident(None, "END",   get_pos $1), "", Block(fst $3)), sp_pos_range $1 $4}  | use {$1}  use: -| use_word listexpr semi_colon {sp_n($2); Use(fst $1, sndfst $2), pos_range $1 $3} +| use_word listexpr semi_colon {sp_n($2); Use(fst $1, sndfst $2), sp_pos_range $1 $3}  use_word: -| use_revision word comma {fst $2, pos_range $1 $3} -| use_revision word {fst $2, pos_range $1 $2} +| use_revision word comma {fst $2, sp_pos_range $1 $3} +| use_revision word {fst $2, sp_pos_range $1 $2}  | use_revision {Ident(None, "", get_pos $1), snd $1}  use_revision: @@ -172,7 +176,7 @@ use_revision:  | USE {$1}  func_decl: -| SUB word {(fst $2, ""), pos_range $1 $2} +| SUB word {(fst $2, ""), sp_pos_range $1 $2}  | FUNC_DECL_WITH_PROTO {(Ident(None, fstfst $1, get_pos $1), sndfst $1), snd $1}  listexpr: /* Basic list expressions */ @@ -180,125 +184,127 @@ listexpr: /* Basic list expressions */  | argexpr %prec PREC_LOW {$1}  expr: /* Ordinary expressions; logical combinations */ -| expr AND expr {sp_p($2); sp_p($3); (P_and, Call_op("and", [ prio_lo P_and $1; prio_lo_after P_and $3 ])), pos_range $1 $3} -| expr OR  expr {sp_p($2); sp_p($3); (P_or,  Call_op("or",  [ prio_lo P_or  $1; prio_lo_after P_or  $3 ])), pos_range $1 $3} +| expr AND expr {sp_p($2); sp_p($3); (P_and, Call_op("and", [ prio_lo P_and $1; prio_lo_after P_and $3 ])), sp_pos_range $1 $3} +| expr OR  expr {sp_p($2); sp_p($3); (P_or,  Call_op("or",  [ prio_lo P_or  $1; prio_lo_after P_or  $3 ])), sp_pos_range $1 $3}  | argexpr %prec PREC_LOW {(fstfst $1, List(sndfst $1)), snd $1}  argexpr: /* Expressions are a list of terms joined by commas */ -| argexpr comma {(P_comma, sndfst $1), pos_range $1 $2} -| argexpr comma term {if not_simple (sndfst $3) then sp_p($3); (P_comma, sndfst $1 @ [sndfst $3]), pos_range $1 $3} -| argexpr comma BRACKET expr BRACKET_END {sp_p($3); sp_p($5); (P_comma, sndfst $1 @ [ Ref(I_hash, sndfst $4) ]), pos_range $1 $5} +| argexpr comma {(P_comma, sndfst $1), sp_pos_range $1 $2} +| argexpr comma term {if not_simple (sndfst $3) then sp_p($3); (P_comma, sndfst $1 @ [sndfst $3]), sp_pos_range $1 $3} +| argexpr comma BRACKET expr BRACKET_END {sp_p($3); sp_p($5); (P_comma, sndfst $1 @ [ Ref(I_hash, sndfst $4) ]), sp_pos_range $1 $5}  | term %prec PREC_LOW {(fstfst $1, [sndfst $1]), snd $1}  /********************************************************************************/  term: -| term ASSIGN     term {let pri = P_assign    in call_op(op_p pri (fst $2) $2, $3, [sndfst $1; prio_lo_after pri $3]), pos_range $1 $3} -| term PLUS       term {let pri = P_add       in call_op(op   pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term COMPARE_OP term {let pri = P_cmp       in call_op(op_p pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term LT         term {let pri = P_cmp       in call_op(op_p pri "<"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term GT         term {let pri = P_cmp       in call_op(op_p pri ">"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term EQ_OP      term {let pri = P_eq        in call_op(op_p pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term POWER      term {let pri = P_tight     in call_op(op   pri "**"     $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term BIT_AND    term {let pri = P_expr      in call_op(op_p pri "&"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term BIT_OR     term {let pri = P_expr      in call_op(op   pri "|"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term BIT_XOR    term {let pri = P_expr      in call_op(op_p pri "^"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term AND_TIGHT  term {let pri = P_tight_and in call_op(op_p pri "&&"     $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term OR_TIGHT   term {let pri = P_tight_or  in call_op(op_p pri "||"     $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term MULT       term {let pri = P_mul       in call_op(op   pri (fst $2) $2, $3, [prio_lo_concat $1; prio_lo_after pri $3]), pos_range $1 $3} -| term DOTDOT     term {let pri = P_paren_wanted P_expr  in call_op(op   pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term BIT_SHIFT  term {let pri = P_paren_wanted P_tight in call_op(op   pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} -| term XOR        term {let pri = P_paren_wanted P_expr  in call_op(op_p pri "xor"    $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), pos_range $1 $3} - -| term ASSIGN     BRACKET expr BRACKET_END {sp_p($3); sp_p($4); sp_p($5); call_op(op_p P_assign (fst $2) $2, $3, [prio_lo P_assign    $1; Ref(I_hash, sndfst $4)]), pos_range $1 $5} -| term AND_TIGHT  BRACKET expr BRACKET_END {sp_p($3); sp_p($4); sp_p($5); call_op(op_p P_tight_and "&&"  $2, $3, [prio_lo P_assign    $1; Ref(I_hash, sndfst $4)]), pos_range $1 $5} -| term OR_TIGHT   BRACKET expr BRACKET_END {sp_p($3); sp_p($4); sp_p($5); call_op(op_p P_tight_or  "||"  $2, $3, [prio_lo P_assign    $1; Ref(I_hash, sndfst $4)]), pos_range $1 $5} - - -| term PATTERN_MATCH     PATTERN   {sp_n($2); sp_p($3); (P_expr, Call_op("m//",  sndfst $1 :: from_PATTERN $3)), pos_range $1 $3} -| term PATTERN_MATCH_NOT PATTERN   {sp_n($2); sp_p($3); (P_expr, Call_op("!m//", sndfst $1 :: from_PATTERN $3)), pos_range $1 $3} -| term PATTERN_MATCH PATTERN_SUBST {sp_n($2); sp_p($3); (P_expr, Call_op("s///", sndfst $1 :: from_PATTERN_SUBST $3)), pos_range $1 $3} - -| term PATTERN_MATCH     scalar { (P_expr, Too_complex), pos_range $1 $3} -| term PATTERN_MATCH_NOT scalar { (P_expr, Too_complex), pos_range $1 $3} - -| term PATTERN_MATCH     RAW_STRING {die_with_pos (sndsnd $3) "use a regexp, not a string"} -| term PATTERN_MATCH_NOT RAW_STRING {die_with_pos (sndsnd $3) "use a regexp, not a string"} -| term PATTERN_MATCH     STRING {die_with_pos (sndsnd $3) "use a regexp, not a string"} -| term PATTERN_MATCH_NOT STRING {die_with_pos (sndsnd $3) "use a regexp, not a string"} - - -| term QUESTION_MARK term COLON term {sp_p($2); sp_p($3); sp_p($4); sp_p($5); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; prio_lo_after P_ternary $3; prio_lo_after P_ternary $5])), pos_range $1 $5} -| term QUESTION_MARK term COLON BRACKET expr BRACKET_END {sp_p($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sp_p($7); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; prio_lo_after P_ternary $3; sndfst $6])), pos_range $1 $7} -| term QUESTION_MARK BRACKET expr BRACKET_END COLON term {sp_p($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sp_p($7); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; sndfst $4; prio_lo_after P_ternary $7])), pos_range $1 $7} -| term QUESTION_MARK BRACKET expr BRACKET_END COLON BRACKET expr BRACKET_END {sp_p($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sp_p($7); sp_p($8); sp_p($9); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; sndfst $4; sndfst $8])), pos_range $1 $9} +| term ASSIGN     term {let pri = P_assign    in call_op(op   pri (fst $2) $2, $3, [sndfst      $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term PLUS       term {let pri = P_add       in call_op(op   pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term COMPARE_OP term {let pri = P_cmp       in call_op(op_p pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term LT         term {let pri = P_cmp       in call_op(op_p pri "<"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term GT         term {let pri = P_cmp       in call_op(op_p pri ">"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term EQ_OP      term {let pri = P_eq        in call_op(op_p pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term POWER      term {let pri = P_tight     in call_op(op   pri "**"     $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term BIT_AND    term {let pri = P_expr      in call_op(op_p pri "&"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term BIT_OR     term {let pri = P_expr      in call_op(op   pri "|"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term BIT_XOR    term {let pri = P_expr      in call_op(op_p pri "^"      $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term AND_TIGHT  term {let pri = P_tight_and in call_op(op_p pri "&&"     $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term OR_TIGHT   term {let pri = P_tight_or  in call_op(op_p pri "||"     $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term MULT       term {let pri = P_mul       in call_op(op   pri (fst $2) $2, $3, [prio_lo_concat $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term DOTDOT     term {let pri = P_paren_wanted P_expr  in call_op(op   pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term BIT_SHIFT  term {let pri = P_paren_wanted P_tight in call_op(op   pri (fst $2) $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} +| term XOR        term {let pri = P_paren_wanted P_expr  in call_op(op_p pri "xor"    $2, $3, [prio_lo pri $1; prio_lo_after pri $3]), sp_pos_range $1 $3} + +| term ASSIGN     BRACKET expr BRACKET_END {sp_p($3); sp_p($4); sp_p($5); call_op(op_p P_assign (fst $2) $2, $3, [prio_lo P_assign    $1; Ref(I_hash, sndfst $4)]), sp_pos_range $1 $5} +| term AND_TIGHT  BRACKET expr BRACKET_END {sp_p($3); sp_p($4); sp_p($5); call_op(op_p P_tight_and "&&"  $2, $3, [prio_lo P_assign    $1; Ref(I_hash, sndfst $4)]), sp_pos_range $1 $5} +| term OR_TIGHT   BRACKET expr BRACKET_END {sp_p($3); sp_p($4); sp_p($5); call_op(op_p P_tight_or  "||"  $2, $3, [prio_lo P_assign    $1; Ref(I_hash, sndfst $4)]), sp_pos_range $1 $5} + + +| term PATTERN_MATCH     PATTERN   {sp_n($2); sp_p($3); (P_expr, Call_op("m//",  sndfst $1 :: from_PATTERN $3)), sp_pos_range $1 $3} +| term PATTERN_MATCH_NOT PATTERN   {sp_n($2); sp_p($3); (P_expr, Call_op("!m//", sndfst $1 :: from_PATTERN $3)), sp_pos_range $1 $3} +| term PATTERN_MATCH PATTERN_SUBST {sp_n($2); sp_p($3); (P_expr, Call_op("s///", sndfst $1 :: from_PATTERN_SUBST $3)), sp_pos_range $1 $3} + +| term PATTERN_MATCH     scalar { (P_expr, Too_complex), sp_pos_range $1 $3} +| term PATTERN_MATCH_NOT scalar { (P_expr, Too_complex), sp_pos_range $1 $3} + +| term PATTERN_MATCH     RAW_STRING {die_with_rawpos (sndsnd $3) "use a regexp, not a string"} +| term PATTERN_MATCH_NOT RAW_STRING {die_with_rawpos (sndsnd $3) "use a regexp, not a string"} +| term PATTERN_MATCH     STRING {die_with_rawpos (sndsnd $3) "use a regexp, not a string"} +| term PATTERN_MATCH_NOT STRING {die_with_rawpos (sndsnd $3) "use a regexp, not a string"} + + +| term QUESTION_MARK term COLON term {sp_p($2); sp_p($3); sp_p($4); sp_p($5); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; prio_lo_after P_ternary $3; prio_lo_after P_ternary $5])), sp_pos_range $1 $5} +| term QUESTION_MARK term COLON BRACKET expr BRACKET_END {sp_p($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sp_p($7); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; prio_lo_after P_ternary $3; sndfst $6])), sp_pos_range $1 $7} +| term QUESTION_MARK BRACKET expr BRACKET_END COLON term {sp_p($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sp_p($7); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; sndfst $4; prio_lo_after P_ternary $7])), sp_pos_range $1 $7} +| term QUESTION_MARK BRACKET expr BRACKET_END COLON BRACKET expr BRACKET_END {sp_p($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); sp_p($7); sp_p($8); sp_p($9); (P_ternary, Call_op("?:", [ prio_lo P_ternary $1 ; sndfst $4; sndfst $8])), sp_pos_range $1 $9}  /* Unary operators and terms */ -| PLUS term %prec UNARY_MINUS {if fst $1 <> "-" then die_rule "syntax error"; sp_0($2); (P_tight, Call_op("- unary", [sndfst $2])), pos_range $1 $2} -| TIGHT_NOT term {(P_tight, Call_op("not", [sndfst $2])), pos_range $1 $2} -| BIT_NEG term {(P_expr, Call_op("~", [sndfst $2])), pos_range $1 $2} -| INCR term    {sp_0($2); (P_tight, Call_op("++", [sndfst $2])), pos_range $1 $2} -| DECR term    {sp_0($2); (P_tight, Call_op("--", [sndfst $2])), pos_range $1 $2} -| term INCR    {sp_0($2); (P_tight, Call_op("++ post", [sndfst $1])), pos_range $1 $2} -| term DECR    {sp_0($2); (P_tight, Call_op("-- post", [sndfst $1])), pos_range $1 $2} -| NOT argexpr  {(P_and, Call_op("not", sndfst $2)), pos_range $1 $2} - -| ONE_SCALAR_PARA RAW_STRING               {call_one_scalar_para $1 [to_Raw_string $2], pos_range $1 $2} -| ONE_SCALAR_PARA STRING                   {call_one_scalar_para $1 [to_String $2], pos_range $1 $2} -| ONE_SCALAR_PARA variable                 {call_one_scalar_para $1 [fst $2], pos_range $1 $2} -| ONE_SCALAR_PARA restricted_subscripted   {call_one_scalar_para $1 [fst $2], pos_range $1 $2} -| ONE_SCALAR_PARA parenthesized            {call_one_scalar_para $1 (sndfst $2), pos_range $1 $2} -| ONE_SCALAR_PARA word_paren parenthesized {call_one_scalar_para $1 [Call(fst $2, sndfst $3)], pos_range $1 $3} +| PLUS term %prec UNARY_MINUS {if fst $1 <> "-" then die_rule "syntax error"; sp_0($2); (P_tight, Call_op("- unary", [sndfst $2])), sp_pos_range $1 $2} +| TIGHT_NOT term {(P_tight, Call_op("not", [sndfst $2])), sp_pos_range $1 $2} +| BIT_NEG term {(P_expr, Call_op("~", [sndfst $2])), sp_pos_range $1 $2} +| INCR term    {sp_0($2); (P_tight, Call_op("++", [sndfst $2])), sp_pos_range $1 $2} +| DECR term    {sp_0($2); (P_tight, Call_op("--", [sndfst $2])), sp_pos_range $1 $2} +| term INCR    {sp_0($2); (P_tight, Call_op("++ post", [sndfst $1])), sp_pos_range $1 $2} +| term DECR    {sp_0($2); (P_tight, Call_op("-- post", [sndfst $1])), sp_pos_range $1 $2} +| NOT argexpr  {(P_and, Call_op("not", sndfst $2)), sp_pos_range $1 $2} + +| ONE_SCALAR_PARA RAW_STRING               {call_one_scalar_para $1 [to_Raw_string $2], sp_pos_range $1 $2} +| ONE_SCALAR_PARA STRING                   {call_one_scalar_para $1 [to_String $2], sp_pos_range $1 $2} +| ONE_SCALAR_PARA variable                 {call_one_scalar_para $1 [fst $2], sp_pos_range $1 $2} +| ONE_SCALAR_PARA restricted_subscripted   {call_one_scalar_para $1 [fst $2], sp_pos_range $1 $2} +| ONE_SCALAR_PARA parenthesized            {call_one_scalar_para $1 (sndfst $2), sp_pos_range $1 $2} +| ONE_SCALAR_PARA word_paren parenthesized {call_one_scalar_para $1 [Call(fst $2, sndfst $3)], sp_pos_range $1 $3}  /* Constructors for anonymous data */ -| ARRAYREF ARRAYREF_END {sp_0($2); (P_expr, Ref(I_array, List[])), pos_range $1 $2} -| arrayref_start ARRAYREF_END {(P_expr, Ref(I_array, List(fst $1))), pos_range $1 $2} -| arrayref_start expr ARRAYREF_END {(P_expr, Ref(I_array, List(fst $1 @ [sndfst $2]))), pos_range $1 $3} -| arrayref_start BRACKET expr BRACKET_END ARRAYREF_END {(P_expr, Ref(I_array, List(fst $1 @ [Ref(I_hash, sndfst $3)]))), pos_range $1 $5} +| ARRAYREF ARRAYREF_END {sp_0($2); (P_expr, Ref(I_array, List[])), sp_pos_range $1 $2} +| arrayref_start ARRAYREF_END {(P_expr, Ref(I_array, List(fst $1))), sp_pos_range $1 $2} +| arrayref_start expr ARRAYREF_END {(P_expr, Ref(I_array, List(fst $1 @ [sndfst $2]))), sp_pos_range $1 $3} +| arrayref_start BRACKET expr BRACKET_END ARRAYREF_END {(P_expr, Ref(I_array, List(fst $1 @ [Ref(I_hash, sndfst $3)]))), sp_pos_range $1 $5} -| BRACKET BRACKET_END {(P_expr, Ref(I_hash, List [])), pos_range $1 $2} /* empty hash */ -| BRACKET_HASHREF expr BRACKET_END %prec PREC_HIGH {sp_p($3); (P_expr, Ref(I_hash, sndfst $2)), pos_range $1 $3} /* { foo => "Bar" } */ -| SUB BRACKET BRACKET_END %prec PREC_HIGH {sp_n($2); sp_0($3); (P_expr, Anonymous_sub []), pos_range $1 $3} -| SUB BRACKET lines BRACKET_END %prec PREC_HIGH {sp_n($2); check_block_sub $3 $4; (P_expr, Anonymous_sub(fst $3)), pos_range $1 $4} +| BRACKET BRACKET_END {(P_expr, Ref(I_hash, List [])), sp_pos_range $1 $2} /* empty hash */ +| BRACKET_HASHREF expr BRACKET_END %prec PREC_HIGH {sp_p($3); (P_expr, Ref(I_hash, sndfst $2)), sp_pos_range $1 $3} /* { foo => "Bar" } */ +| SUB BRACKET BRACKET_END %prec PREC_HIGH {sp_n($2); sp_0($3); (P_expr, anonymous_sub []), sp_pos_range $1 $3} +| SUB BRACKET lines BRACKET_END %prec PREC_HIGH {sp_n($2); check_block_sub $3 $4; (P_expr, anonymous_sub(fst $3)), sp_pos_range $1 $4}  | termdo {(P_tok, fst $1), snd $1} -| REF term {(P_expr, Ref(I_scalar, sndfst $2)), pos_range $1 $2} /* \$x, \@y, \%z */ -| my %prec UNIOP {(P_expr, List(fst $1)), snd $1} -| LOCAL term    %prec UNIOP {sp_n($2); (P_expr, Local(sndfst $2)), pos_range $1 $2} +| REF term {(P_expr, Ref(I_scalar, sndfst $2)), sp_pos_range $1 $2} /* \$x, \@y, \%z */ +| my_our %prec UNIOP {(P_expr, fst $1), snd $1} +| LOCAL term    %prec UNIOP {sp_n($2); (P_expr, to_Local $2), sp_pos_range $1 $2}  | parenthesized {(fstfst $1, List(sndfst $1)), snd $1} /* (1, 2) */ -| parenthesized arrayref {sp_0($2); (P_tok, Deref_with(I_array, List(sndfst $1), List(fst $2))), pos_range $1 $2} /* list slice */ +| parenthesized arrayref {sp_0($2); (P_tok, Deref_with(I_array, List(sndfst $1), List(fst $2))), sp_pos_range $1 $2} /* list slice */  | variable {(P_tok, fst $1), snd $1}  | subscripted {(P_tok, fst $1), snd $1} -| array arrayref {(P_expr, Deref_with(I_array, fst $1, List(fst $2))), pos_range $1 $2} /* array slice: @array[vals] */ -| array BRACKET expr BRACKET_END {sp_0($2); sp_0($3); sp_0($4); (P_expr, Deref_with(I_hash, array_ident_to_hash_ident $1, sndfst $3)), pos_range $1 $4} /* hash slice: @hash{@keys} */ +| array arrayref {(P_expr, Deref_with(I_array, from_array $1, List(fst $2))), sp_pos_range $1 $2} /* array slice: @array[vals] */ +| array BRACKET expr BRACKET_END {sp_0($2); sp_0($3); sp_0($4); (P_expr, Deref_with(I_hash, from_array $1, sndfst $3)), sp_pos_range $1 $4} /* hash slice: @hash{@keys} */  /* function_calls */ -| func parenthesized {sp_0($2); (P_tok, call(fst $1, sndfst $2)), pos_range $1 $2} /* &foo(@args) */ -| word argexpr {check_parenthesized_first_argexpr (string_of_Ident (fst $1)) $2; (P_call_no_paren, call(fst $1, sndfst $2)), pos_range $1 $2} /* foo $a, $b */ -| word_paren parenthesized {(P_tok, call(fst $1, sndfst $2)), pos_range $1 $2} /* foo(@args) */ -| word BRACKET lines BRACKET_END listexpr %prec LSTOP {sp_n($2); check_block_sub $3 $4; ((if sndfst $5 = [] then P_tok else P_call_no_paren), call(fst $1, Anonymous_sub(fst $3) :: sndfst $5)), pos_range $1 $5} /* map { foo } @bar */ -| word BRACKET BRACKET expr BRACKET_END            BRACKET_END listexpr %prec LSTOP {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); ((if sndfst $7 = [] then P_tok else P_call_no_paren), call(fst $1, Anonymous_sub [ Ref(I_hash, sndfst $4) ] :: sndfst $7)), pos_range $1 $7} /* map { { foo } } @bar */ -| word BRACKET BRACKET expr BRACKET_END semi_colon BRACKET_END listexpr %prec LSTOP {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($7); ((if sndfst $8 = [] then P_tok else P_call_no_paren), call(fst $1, Anonymous_sub [ Ref(I_hash, sndfst $4); Semi_colon ] :: sndfst $8)), pos_range $1 $8} /* map { { foo }; } @bar */ +| func parenthesized {sp_0($2); (P_tok, call(fst $1, sndfst $2)), sp_pos_range $1 $2} /* &foo(@args) */ +| word argexpr {check_parenthesized_first_argexpr (string_of_Ident (fst $1)) $2; (P_call_no_paren, call(Deref(I_func, fst $1), sndfst $2)), sp_pos_range $1 $2} /* foo $a, $b */ +| word_paren parenthesized {(P_tok, call(Deref(I_func, fst $1), sndfst $2)), sp_pos_range $1 $2} /* foo(@args) */ +| word BRACKET lines BRACKET_END listexpr %prec LSTOP {sp_n($2); check_block_sub $3 $4; ((if sndfst $5 = [] then P_tok else P_call_no_paren), call(Deref(I_func, fst $1), anonymous_sub(fst $3) :: sndfst $5)), sp_pos_range $1 $5} /* map { foo } @bar */ +| word BRACKET BRACKET expr BRACKET_END            BRACKET_END listexpr %prec LSTOP {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($6); ((if sndfst $7 = [] then P_tok else P_call_no_paren), call(Deref(I_func, fst $1), anonymous_sub [ Ref(I_hash, sndfst $4) ] :: sndfst $7)), sp_pos_range $1 $7} /* map { { foo } } @bar */ +| word BRACKET BRACKET expr BRACKET_END semi_colon BRACKET_END listexpr %prec LSTOP {sp_n($2); sp_p($3); sp_p($4); sp_p($5); sp_p($7); ((if sndfst $8 = [] then P_tok else P_call_no_paren), call(Deref(I_func, fst $1), anonymous_sub [ Ref(I_hash, sndfst $4); Semi_colon ] :: sndfst $8)), sp_pos_range $1 $8} /* map { { foo }; } @bar */ -| term ARROW word_or_scalar parenthesized {sp_0($2); sp_0($3); sp_0($4); (P_tok, Method_callP(sndfst $1, fst $3, sndfst $4)), pos_range $1 $4} /* $foo->bar(list) */ -| term ARROW word_or_scalar {sp_0($2); sp_0($3); (P_tok, Method_callP(sndfst $1, fst $3, [])), pos_range $1 $3} /* $foo->bar */ +| term ARROW word_or_scalar parenthesized {sp_0($2); sp_0($3); sp_0($4); (P_tok, Method_callP(sndfst $1, fst $3, sndfst $4)), sp_pos_range $1 $4} /* $foo->bar(list) */ +| term ARROW word_or_scalar {sp_0($2); sp_0($3); (P_tok, Method_callP(sndfst $1, fst $3, [])), sp_pos_range $1 $3} /* $foo->bar */ +| term ARROW MULT parenthesized {check_MULT_is_x $3; sp_0($2); sp_0($3); sp_0($4); (P_tok, Method_callP(sndfst $1, Ident(None, "x", get_pos $3), sndfst $4)), sp_pos_range $1 $4} /* $foo->bar(list) */ +| term ARROW MULT {check_MULT_is_x $3; sp_0($2); sp_0($3); (P_tok, Method_callP(sndfst $1, Ident(None, "x", get_pos $3), [])), sp_pos_range $1 $3} /* $foo->bar */ -| NEW word listexpr { (P_call_no_paren, Method_call(fst $2, Ident(None, "new", get_pos $1), sndfst $3)), pos_range $1 $3} /* new Class @args */ +| NEW word listexpr { (P_call_no_paren, Method_call(fst $2, Ident(None, "new", get_pos $1), sndfst $3)), sp_pos_range $1 $3} /* new Class @args */  | PRINT { (P_call_no_paren, Call_op("print", var_STDOUT :: [ var_dollar_ ])), snd $1} -| PRINT argexpr {check_parenthesized_first_argexpr (fst $1) $2; (P_call_no_paren, Call_op("print", var_STDOUT :: sndfst $2)), pos_range $1 $2} -| PRINT_TO_STAR { (P_call_no_paren, Call_op("print", Deref(I_star,   Ident(None, fst $1, get_pos $1)) :: [ var_dollar_ ])), snd $1} -| PRINT_TO_STAR argexpr { (P_call_no_paren, Call_op("print", Deref(I_star,   Ident(None, fst $1, get_pos $1)) :: sndfst $2)), pos_range $1 $2} -| PRINT_TO_SCALAR { (P_call_no_paren, Call_op("print", var_STDOUT :: [ Deref(I_scalar, Ident(None, fst $1, get_pos $1)) ])), snd $1} -| PRINT_TO_SCALAR argexpr { (P_call_no_paren, Call_op("print", Deref(I_scalar, Ident(None, fst $1, get_pos $1)) :: sndfst $2)), pos_range $1 $2} +| PRINT argexpr {check_parenthesized_first_argexpr (fst $1) $2; (P_call_no_paren, Call_op("print", var_STDOUT :: sndfst $2)), sp_pos_range $1 $2} +| PRINT_TO_SCALAR         { (P_call_no_paren, Call_op("print", var_STDOUT :: [ Deref(I_scalar, Ident(None, fst $1, get_pos $1)) ])), snd $1} +| PRINT_TO_SCALAR argexpr { (P_call_no_paren, Call_op("print", Deref(I_scalar, Ident(None, fst $1, get_pos $1)) :: sndfst $2)), sp_pos_range $1 $2} +| PRINT_TO_STAR           { (P_call_no_paren, Call_op("print", Deref(I_star, Ident(None, fst $1, get_pos $1)) :: [ var_dollar_ ])), snd $1} +| PRINT_TO_STAR argexpr   { (P_call_no_paren, Call_op("print", Deref(I_star, Ident(None, fst $1, get_pos $1)) :: sndfst $2)), sp_pos_range $1 $2} -| hash PKG_SCOPE {sp_0($2); (P_tok, Too_complex), pos_range $1 $2} /* %main:: */ +| hash PKG_SCOPE {sp_0($2); (P_tok, Too_complex), sp_pos_range $1 $2} /* %main:: */  | word {(P_tok, check_word_alone $1), snd $1} @@ -315,55 +321,66 @@ term:  | diamond {(P_expr, fst $1), snd $1}  diamond: -| LT GT {sp_0($2); Call_op("<>", []), pos_range $1 $2} -| LT term GT {sp_0($2); sp_0($3); Call_op("<>", [sndfst $2]), pos_range $1 $3} +| LT GT {sp_0($2); Call_op("<>", []), sp_pos_range $1 $2} +| LT term GT {sp_0($2); sp_0($3); Call_op("<>", [sndfst $2]), sp_pos_range $1 $3}  subscripted: /* Some kind of subscripted expression */ -| variable PKG_SCOPE bracket_subscript {sp_0($2); sp_0($3); Too_complex, pos_range $1 $3} /* $foo::{something} */ -| scalar bracket_subscript             {sp_0($2); Deref_with(I_hash , fst $1, fst      $2), pos_range $1 $2} /* $foo{bar} */ -| scalar arrayref                      {sp_0($2); Deref_with(I_array, fst $1, only_one $2), pos_range $1 $2} /* $array[$element] */ -| term ARROW bracket_subscript         {sp_0($2); sp_0($3); Deref_with(I_hash , sndfst $1, fst      $3), pos_range $1 $3} /* somehref->{bar} */ -| term ARROW arrayref                  {sp_0($2); sp_0($3); Deref_with(I_array, sndfst $1, only_one $3), pos_range $1 $3} /* somearef->[$element] */ -| term ARROW parenthesized             {sp_0($2); sp_0($3); Deref_with(I_func , sndfst $1, List(sndfst $3)), pos_range $1 $3} /* $subref->(@args) */ -| subscripted bracket_subscript        {sp_0($2); Deref_with(I_hash , fst $1, fst      $2), pos_range $1 $2} /* $foo->[bar]{baz} */ -| subscripted arrayref                 {sp_0($2); Deref_with(I_array, fst $1, only_one $2), pos_range $1 $2} /* $foo->[$bar][$baz] */ -| subscripted parenthesized            {sp_0($2); Deref_with(I_func , fst $1, List(sndfst $2)), pos_range $1 $2} /* $foo->{bar}(@args) */ +| variable PKG_SCOPE bracket_subscript {sp_0($2); sp_0($3); Too_complex, sp_pos_range $1 $3} /* $foo::{something} */ +| scalar bracket_subscript             {sp_0($2); Deref_with(I_hash , from_scalar $1, fst      $2), sp_pos_range $1 $2} /* $foo{bar} */ +| scalar arrayref                      {sp_0($2); Deref_with(I_array, from_scalar $1, only_one $2), sp_pos_range $1 $2} /* $array[$element] */ +| term ARROW bracket_subscript         {sp_0($2); sp_0($3); Deref_with(I_hash , sndfst $1, fst      $3), sp_pos_range $1 $3} /* somehref->{bar} */ +| term ARROW arrayref                  {sp_0($2); sp_0($3); Deref_with(I_array, sndfst $1, only_one $3), sp_pos_range $1 $3} /* somearef->[$element] */ +| term ARROW parenthesized             {sp_0($2); sp_0($3); Deref_with(I_func , sndfst $1, List(sndfst $3)), sp_pos_range $1 $3} /* $subref->(@args) */ +| subscripted bracket_subscript        {sp_0($2); Deref_with(I_hash , fst $1, fst      $2), sp_pos_range $1 $2} /* $foo->[bar]{baz} */ +| subscripted arrayref                 {sp_0($2); Deref_with(I_array, fst $1, only_one $2), sp_pos_range $1 $2} /* $foo->[$bar][$baz] */ +| subscripted parenthesized            {sp_0($2); Deref_with(I_func , fst $1, List(sndfst $2)), sp_pos_range $1 $2} /* $foo->{bar}(@args) */  restricted_subscripted: /* Some kind of subscripted expression */ -| scalar bracket_subscript             {sp_0($2); Deref_with(I_hash , fst $1, fst      $2), pos_range $1 $2} /* $foo{bar} */ -| scalar arrayref                      {sp_0($2); Deref_with(I_array, fst $1, only_one $2), pos_range $1 $2} /* $array[$element] */ -| restricted_subscripted bracket_subscript        {sp_0($2); Deref_with(I_hash , fst $1, fst      $2), pos_range $1 $2} /* $foo->[bar]{baz} */ -| restricted_subscripted arrayref                 {sp_0($2); Deref_with(I_array, fst $1, only_one $2), pos_range $1 $2} /* $foo->[$bar][$baz] */ -| restricted_subscripted parenthesized            {sp_0($2); Deref_with(I_func , fst $1, List(sndfst $2)), pos_range $1 $2} /* $foo->{bar}(@args) */ +| scalar bracket_subscript             {sp_0($2); Deref_with(I_hash , from_scalar $1, fst      $2), sp_pos_range $1 $2} /* $foo{bar} */ +| scalar arrayref                      {sp_0($2); Deref_with(I_array, from_scalar $1, only_one $2), sp_pos_range $1 $2} /* $array[$element] */ +| restricted_subscripted bracket_subscript        {sp_0($2); Deref_with(I_hash , fst $1, fst      $2), sp_pos_range $1 $2} /* $foo->[bar]{baz} */ +| restricted_subscripted arrayref                 {sp_0($2); Deref_with(I_array, fst $1, only_one $2), sp_pos_range $1 $2} /* $foo->[$bar][$baz] */ +| restricted_subscripted parenthesized            {sp_0($2); Deref_with(I_func , fst $1, List(sndfst $2)), sp_pos_range $1 $2} /* $foo->{bar}(@args) */  arrayref: -| arrayref_start ARRAYREF_END {sp_0($2); fst $1, pos_range $1 $2} -| arrayref_start expr ARRAYREF_END {sp_0($3); fst $1 @ [sndfst $2], pos_range $1 $3} -| arrayref_start BRACKET expr BRACKET_END ARRAYREF_END {sp_p($2); sp_p($4); sp_0($5); fst $1 @ [Ref(I_hash, sndfst $3)], pos_range $1 $5} +| arrayref_start ARRAYREF_END {sp_0($2); fst $1, sp_pos_range $1 $2} +| arrayref_start expr ARRAYREF_END {sp_0($3); fst $1 @ [sndfst $2], sp_pos_range $1 $3} +| arrayref_start BRACKET expr BRACKET_END ARRAYREF_END {sp_p($2); sp_p($4); sp_0($5); fst $1 @ [Ref(I_hash, sndfst $3)], sp_pos_range $1 $5}  parenthesized: -| parenthesized_start PAREN_END {sp_0_or_cr($2); ((if fst $1 = [] then P_tok else P_paren P_comma), fst $1), pos_range $1 $2} -| parenthesized_start expr PAREN_END {sp_0_or_cr($3); (P_paren(if fst $1 = [] then fstfst $2 else P_comma), fst $1 @ [(if fst $1 = [] then prio_lo P_loose else prio_lo_after P_comma) $2]), pos_range $1 $3} -| parenthesized_start BRACKET expr BRACKET_END PAREN_END {sp_p($4); sp_0_or_cr($5); (P_paren(if fst $1 = [] then P_expr else P_comma), fst $1 @ [Ref(I_hash, sndfst $3)]), pos_range $1 $5} +| parenthesized_start PAREN_END {sp_0_or_cr($2); ((if fst $1 = [] then P_tok else P_paren P_comma), fst $1), sp_pos_range $1 $2} +| parenthesized_start expr PAREN_END {sp_0_or_cr($3); (P_paren(if fst $1 = [] then fstfst $2 else P_comma), fst $1 @ [(if fst $1 = [] then prio_lo P_loose else prio_lo_after P_comma) $2]), sp_pos_range $1 $3} +| parenthesized_start BRACKET expr BRACKET_END PAREN_END {sp_p($4); sp_0_or_cr($5); (P_paren(if fst $1 = [] then P_expr else P_comma), fst $1 @ [Ref(I_hash, sndfst $3)]), sp_pos_range $1 $5}  arrayref_start:  | ARRAYREF {[], snd $1} -| arrayref_start BRACKET expr BRACKET_END comma {sp_p($2); sp_p($3); sp_p($4); fst $1 @ [Ref(I_hash, sndfst $3)], pos_range $1 $5} +| arrayref_start BRACKET expr BRACKET_END comma {sp_p($2); sp_p($3); sp_p($4); fst $1 @ [Ref(I_hash, sndfst $3)], sp_pos_range $1 $5}  parenthesized_start:  | PAREN {[], snd $1} -| parenthesized_start BRACKET expr BRACKET_END comma {(if fst $1 = [] then sp_0_or_cr else sp_p)($2); sp_p($3); sp_p($4); fst $1 @ [Ref(I_hash, sndfst $3)], pos_range $1 $5} - -my: /* Things that can be "my"'d */ -| MY parenthesized {List.map (fun e -> My e) (sndfst $2), pos_range $1 $2} -| MY scalar {[My(fst $2)], pos_range $1 $2} -| MY hash {[My(fst $2)], pos_range $1 $2} -| MY array {[My(fst $2)], pos_range $1 $2} +| parenthesized_start BRACKET expr BRACKET_END comma {(if fst $1 = [] then sp_0_or_cr else sp_p)($2); sp_p($3); sp_p($4); fst $1 @ [Ref(I_hash, sndfst $3)], sp_pos_range $1 $5} + +my_our: /* Things that can be "my"'d */ +| my_our_paren PAREN_END {sp_0($2); if sndfst $1 <> [] && fst (fstfst $1) then die_rule "syntax error";    My_our(snd (fstfst $1), sndfst $1, get_pos $1), sp_pos_range $1 $2} +| my_our_paren SCALAR_IDENT PAREN_END {(if sndfst $1 = [] then sp_0 else sp_1)($2); check_my_our_paren $1; My_our(snd (fstfst $1), sndfst $1 @ [I_scalar, sndfst $2], pos_range $1 $3), sp_pos_range $1 $3} +| my_our_paren HASH_IDENT   PAREN_END {(if sndfst $1 = [] then sp_0 else sp_1)($2); check_my_our_paren $1; My_our(snd (fstfst $1), sndfst $1 @ [I_hash,   sndfst $2], pos_range $1 $3), sp_pos_range $1 $3} +| my_our_paren ARRAY_IDENT  PAREN_END {(if sndfst $1 = [] then sp_0 else sp_1)($2); check_my_our_paren $1; My_our(snd (fstfst $1), sndfst $1 @ [I_array,  sndfst $2], pos_range $1 $3), sp_pos_range $1 $3} +| MY_OUR SCALAR_IDENT {My_our(fst $1, [I_scalar, sndfst $2], get_pos $2), sp_pos_range $1 $2} +| MY_OUR HASH_IDENT   {My_our(fst $1, [I_hash,   sndfst $2], get_pos $2), sp_pos_range $1 $2} +| MY_OUR ARRAY_IDENT  {My_our(fst $1, [I_array,  sndfst $2], get_pos $2), sp_pos_range $1 $2} + +my_our_paren: +| MY_OUR PAREN {sp_1($2); ((true, fst $1), []), sp_pos_range $1 $2} +| my_our_paren comma {if fst (fstfst $1) then die_rule "syntax error"; ((true, snd (fstfst $1)), sndfst $1), sp_pos_range $1 $2} +| my_our_paren BAREWORD {check_my_our_paren $1; if fst $2 <> "undef" then die_rule "scalar expected"; ((false, snd (fstfst $1)), sndfst $1 @ [I_raw, fst $2]), sp_pos_range $1 $2} +| my_our_paren SCALAR_IDENT {check_my_our_paren $1; ((false, snd (fstfst $1)), sndfst $1 @ [I_scalar, sndfst $2]), sp_pos_range $1 $2} +| my_our_paren HASH_IDENT   {check_my_our_paren $1; ((false, snd (fstfst $1)), sndfst $1 @ [I_hash,   sndfst $2]), sp_pos_range $1 $2} +| my_our_paren ARRAY_IDENT  {check_my_our_paren $1; ((false, snd (fstfst $1)), sndfst $1 @ [I_array,  sndfst $2]), sp_pos_range $1 $2}  termdo: /* Things called with "do" */  | DO term %prec UNIOP { die_rule "\"do EXPR\" not allowed" } /* do $filename */ -| DO BRACKET lines BRACKET_END %prec PREC_HIGH {sp_n($2); check_block_sub $3 $4; Block(fst $3), pos_range $1 $4} /* do { code */ +| DO BRACKET lines BRACKET_END %prec PREC_HIGH {sp_n($2); check_block_sub $3 $4; Block(fst $3), sp_pos_range $1 $4} /* do { code */  bracket_subscript: -| BRACKET expr BRACKET_END {sp_0($1); sp_same $2 $3; only_one_in_List $2, pos_range $1 $3} +| BRACKET expr BRACKET_END {sp_0($1); sp_same $2 $3; only_one_in_List $2, sp_pos_range $1 $3}  | COMPACT_HASH_SUBSCRIPT {sp_0($1); to_Raw_string $1, snd $1}  variable: @@ -396,17 +413,17 @@ word_paren:  | BAREWORD_PAREN { Ident(None, fst $1, get_pos $1), snd $1}  | RAW_IDENT_PAREN { to_Ident $1, snd $1} -arraylen: ARRAYLEN_IDENT {Deref(I_arraylen, to_Ident $1), snd $1} | ARRAYLEN  scalar {sp_0($2); Deref(I_arraylen, fst $2), snd $1} | ARRAYLEN  bracket_subscript {Deref(I_arraylen, fst $2), pos_range $1 $2} -scalar:   SCALAR_IDENT   {Deref(I_scalar  , to_Ident $1), snd $1} | DOLLAR    scalar {sp_0($2); Deref(I_scalar  , fst $2), snd $1} | DOLLAR    bracket_subscript {Deref(I_scalar  , fst $2), pos_range $1 $2} | DOLLAR BRACKET BRACKET expr BRACKET_END BRACKET_END {sp_0($2); sp_0($3); sp_p($5); sp_0($6); Deref(I_scalar, Ref(I_hash, sndfst $4)), pos_range $1 $6} -func:     FUNC_IDENT     {Deref(I_func    , to_Ident $1), snd $1} | AMPERSAND scalar {sp_0($2); Deref(I_func    , fst $2), snd $1} | AMPERSAND bracket_subscript {Deref(I_func    , fst $2), pos_range $1 $2} -array:    ARRAY_IDENT    {Deref(I_array   , to_Ident $1), snd $1} | AT        scalar {sp_0($2); Deref(I_array   , fst $2), snd $1} | AT        bracket_subscript {Deref(I_array   , fst $2), pos_range $1 $2} -hash:     HASH_IDENT     {Deref(I_hash    , to_Ident $1), snd $1} | PERCENT   scalar {sp_0($2); Deref(I_hash    , fst $2), snd $1} | PERCENT   bracket_subscript {Deref(I_hash    , fst $2), pos_range $1 $2} -star:     STAR_IDENT     {Deref(I_star    , to_Ident $1), snd $1} | STAR      scalar {sp_0($2); Deref(I_star    , fst $2), snd $1} | STAR      bracket_subscript {Deref(I_star    , fst $2), pos_range $1 $2} +arraylen: ARRAYLEN_IDENT {deref_arraylen (to_Ident $1), snd $1} | ARRAYLEN  scalar {sp_0($2); deref_arraylen (fst $2), snd $1} | ARRAYLEN  bracket_subscript {deref_arraylen (fst $2), sp_pos_range $1 $2} +scalar:   SCALAR_IDENT   {Deref(I_scalar, to_Ident $1), snd $1} | DOLLAR    scalar {sp_0($2); Deref(I_scalar, fst $2), snd $1} | DOLLAR    bracket_subscript {Deref(I_scalar, fst $2), sp_pos_range $1 $2} | DOLLAR BRACKET BRACKET expr BRACKET_END BRACKET_END {sp_0($2); sp_0($3); sp_p($5); sp_0($6); Deref(I_scalar, Ref(I_hash, sndfst $4)), sp_pos_range $1 $6} +func:     FUNC_IDENT     {Deref(I_func  , to_Ident $1), snd $1} | AMPERSAND scalar {sp_0($2); Deref(I_func  , fst $2), snd $1} | AMPERSAND bracket_subscript {Deref(I_func  , fst $2), sp_pos_range $1 $2} +array:    ARRAY_IDENT    {Deref(I_array , to_Ident $1), snd $1} | AT        scalar {sp_0($2); Deref(I_array , fst $2), snd $1} | AT        bracket_subscript {Deref(I_array , fst $2), sp_pos_range $1 $2} +hash:     HASH_IDENT     {Deref(I_hash  , to_Ident $1), snd $1} | PERCENT   scalar {sp_0($2); Deref(I_hash  , fst $2), snd $1} | PERCENT   bracket_subscript {Deref(I_hash  , fst $2), sp_pos_range $1 $2} +star:     STAR_IDENT     {Deref(I_star  , to_Ident $1), snd $1} | STAR      scalar {sp_0($2); Deref(I_star  , fst $2), snd $1} | STAR      bracket_subscript {Deref(I_star  , fst $2), sp_pos_range $1 $2}  expr_or_empty: {Block [], (Space_none, bpos)} | expr {sndfst $1, snd $1}  %%  ;; -prog_ref := Some inside +prog_ref := Some prog  ;; diff --git a/perl_checker.src/parser_helper.ml b/perl_checker.src/parser_helper.ml index c97c51d..ed89c8d 100644 --- a/perl_checker.src/parser_helper.ml +++ b/perl_checker.src/parser_helper.ml @@ -5,7 +5,8 @@ open Printf  let bpos = -1, -1  let raw_pos2pos(a, b) = !Info.current_file, a, b -let pos_range (_, (space, (a1, b1))) (_, (_, (a2, b2))) = space, ((if a1 = -1 then a2 else a1), (if b2 = -1 then b1 else b2)) +let pos_range (_, (_, (a1, b1))) (_, (_, (a2, b2))) = raw_pos2pos((if a1 = -1 then a2 else a1), (if b2 = -1 then b1 else b2)) +let sp_pos_range (_, (space, (a1, b1))) (_, (_, (a2, b2))) = space, ((if a1 = -1 then a2 else a1), (if b2 = -1 then b1 else b2))  let get_pos (_, (_, pos)) = raw_pos2pos pos  let var_dollar_ = Deref(I_scalar, Ident(None, "_", raw_pos2pos bpos))  let var_STDOUT = Deref(I_star, Ident(None, "STDOUT", raw_pos2pos bpos)) @@ -39,19 +40,29 @@ let string_of_Ident = function    | Ident(Some fq, s, _) -> fq ^ "::" ^ s    | _ -> internal_error "string_of_Ident" +let from_scalar (e, _) = +  match e with +  | Deref(I_scalar, ident) -> ident +  | _ -> internal_error "from_scalar" + +let from_array (e, _) = +  match e with +  | Deref(I_array, ident) -> ident +  | _ -> internal_error "from_array" -let msg_with_pos (start, end_) msg = Info.pos2sfull_current start end_ ^ msg -let die_with_pos raw_pos msg = failwith      (msg_with_pos raw_pos msg) -let warn         raw_pos msg = prerr_endline (msg_with_pos raw_pos msg) +let msg_with_rawpos (start, end_) msg = Info.pos2sfull_current start end_ ^ msg +let die_with_rawpos raw_pos msg = failwith      (msg_with_rawpos raw_pos msg) +let warn         raw_pos msg = prerr_endline (msg_with_rawpos raw_pos msg) -let die_rule msg = die_with_pos (Parsing.symbol_start(), Parsing.symbol_end()) msg +let die_rule msg = die_with_rawpos (Parsing.symbol_start(), Parsing.symbol_end()) msg  let warn_rule msg = warn (Parsing.symbol_start(), Parsing.symbol_end()) msg  let debug msg = if true then prerr_endline msg -let warn_too_many_space start = warn (start, start) "you should have only one space here" -let warn_no_space	start = warn (start, start) "you should have a space here" -let warn_cr		start = warn (start, start) "you should not have a carriage-return (\\n) here" -let warn_space		start = warn (start, start) "you should not have a space here" +let warn_verb pos msg = if not !Flags.quiet then warn (pos, pos) msg +let warn_too_many_space start = warn_verb start "you should have only one space here" +let warn_no_space	start = warn_verb start "you should have a space here" +let warn_cr		start = warn_verb start "you should not have a carriage-return (\\n) here" +let warn_space		start = warn_verb start "you should not have a space here"  let rec prio_less = function    | P_paren_wanted prio1, prio2 @@ -161,7 +172,7 @@ let sp_cr(_, (spaces, (start, _))) =    | Space_none -> ()    | Space_0    | Space_1 -  | Space_n -> warn (start, start) "you should have a carriage-return (\\n) here" +  | Space_n -> warn_verb start "you should have a carriage-return (\\n) here"    | Space_cr -> ()  let sp_same (_, (spaces1, _) as ter1) (_, (spaces2, _) as ter2) = @@ -169,8 +180,11 @@ let sp_same (_, (spaces1, _) as ter1) (_, (spaces2, _) as ter2) =    else if spaces2 <> Space_0 then sp_p ter1  let check_word_alone (word, _) = -  let s = string_of_Ident word in -  if s = "time" || s = "wantarray" then die_rule (sprintf "please use %s() instead of %s" s s); +  (match word with +  | Ident(None, ("time" as f), _) +  | Ident(None, ("wantarray" as f), _) -> +      die_rule (sprintf "please use %s() instead of %s" f f) +  | _ -> ());    word  let check_parenthesized_first_argexpr word ((_, e), (_, (start, _)) as ex) = @@ -181,26 +195,22 @@ let check_parenthesized_first_argexpr word ((_, e), (_, (start, _)) as ex) =    | e' :: l ->        if is_parenthesized e' then  	if want_space then -	  if l = [] then sp_n(ex) else die_with_pos (start, start) "can't handle this nicely" +	  if l = [] then sp_n(ex) else die_with_rawpos (start, start) "can't handle this nicely"  	else -	  if l = [] then sp_0(ex) else die_with_pos (start, start) "you must not have a space here" +	  if l = [] then sp_0(ex) else die_with_rawpos (start, start) "you must not have a space here"    | _ ->         if word = "time" then die_rule "please use time() instead of time";        sp_p(ex)  let check_foreach (s, (_, pos)) = if s = "for"     then warn pos "write \"foreach\" instead of \"for\""  let check_for     (s, (_, pos)) = if s = "foreach" then warn pos "write \"for\" instead of \"foreach\"" +let check_MULT_is_x (s, _) = if s <> "x" then die_rule "syntax error" +let check_my (s, _) = if s <> "my" then die_rule "syntax error" -let check_package t = -  if str_ends_with !Info.current_file ".pm" then -  match t with -  | Package _ :: _ -> () -  | _ -> warn (0, 0) (sprintf "module %s does not have \"package xxxx;\" on its first line" !Info.current_file) - -let check_my op para (_, pos) = +let check_my_our op para (_, pos) =    match op, para with -  | "=", [List [My _]; Ident(None, "undef", _)] -> warn pos "no need to initialize variable, it's done by default" -  | "=", [List [My _]; List[]] ->  +  | "=", [List [My_our _]; Ident(None, "undef", _)] -> warn pos "no need to initialize variable, it's done by default" +  | "=", [List [My_our _]; List[]] ->         if Info.is_on_same_line_current pos then warn pos "no need to initialize variables, it's done by default"    | _ -> () @@ -212,7 +222,7 @@ let check_block_sub (l, (_, (_, end_)) as ter_lines) (_, (space, _) as ter_BRACK      sp_p ter_BRACKET_END ;      if space <> Space_cr then -      (if l <> [] && last l = Semi_colon then warn (end_, end_) "spurious \";\" before closing block") +      (if l <> [] && last l = Semi_colon then warn_verb end_ "spurious \";\" before closing block")    )  let check_block_ref (l, (_, (_, end_)) as ter_lines) (_, (space, _) as ter_BRACKET_END) = @@ -221,42 +231,63 @@ let check_block_ref (l, (_, (_, end_)) as ter_lines) (_, (space, _) as ter_BRACK    else sp_same ter_lines ter_BRACKET_END ;    if space <> Space_cr then -    (if l <> [] && last l = Semi_colon then warn (end_, end_) "spurious \";\" before closing block") - +    (if l <> [] && last l = Semi_colon then warn_verb end_ "spurious \";\" before closing block") -let to_Ident ((fq, name), (_, pos)) = Ident(fq, name, raw_pos2pos pos) -let to_Raw_string (s, (_, pos)) = Raw_string(s, raw_pos2pos pos) - -let op prio s (_, both) = prio, (((), both), s) -let op_p prio s e = sp_p e ; op prio s e - -let call_op((prio, (prev_ter, op)), ter, para) =  -  sp_same prev_ter ter ; -  check_my op para (snd ter); -  prio, Call_op(op, para) +let check_my_our_paren (((comma_closed, _), _), _) = +  if not comma_closed then die_rule "syntax error"  let rec only_one (l, (spaces, pos)) =    match l with    | [List l'] -> only_one (l', (spaces, pos))    | [e] -> e -  | [] -> die_with_pos pos "you must give one argument" -  | _  -> die_with_pos pos "you must give only one argument" +  | [] -> die_with_rawpos pos "you must give one argument" +  | _  -> die_with_rawpos pos "you must give only one argument"  let only_one_in_List ((_, e), both) =    match e with    | List l -> only_one(l, both)    | _ -> e - -let array_ident_to_hash_ident (e, (_, pos)) = -  match e with -  | Deref(I_array, e) -> Deref(I_hash, e) -  | _ -> die_with_pos pos "internal error (array_ident_to_hash_ident)"  let to_List = function    | [e] -> e    | l -> List l -let sub_declaration (name, proto) body = Sub_declaration(name, proto, body) +let deref_arraylen e = Call(Ident(None, "int", raw_pos2pos bpos), [Deref(I_array, e)]) +let to_Ident ((fq, name), (_, pos)) = Ident(fq, name, raw_pos2pos pos) +let to_Raw_string (s, (_, pos)) = Raw_string(s, raw_pos2pos pos) +let to_Local ((_, e), (_, pos)) = +  let l =  +    match e with +    | List[List l] -> l +    | _ -> [e] +  in +  let local_vars, local_exprs = fpartition (function +    | Deref(I_star, Ident(None, ident, _)) -> +	Some(I_star, ident) +    | Deref(I_scalar, Ident _) +    | Deref(I_array, Ident _) +    | Deref(I_star, Ident _) +    | Deref_with(I_hash, Ident _, _) +    | Deref_with(I_hash, Deref(I_scalar, _), _) +    | Deref_with(I_hash, Deref_with(I_hash, Ident _, _), _) +    | Deref_with(I_hash, Deref_with(I_hash, Deref(I_scalar, Ident _), _), _) -> +	None +    | _ -> die_with_rawpos pos "bad argument to \"local\"" +  ) l in +  if local_vars = [] then Call_op("local", local_exprs) +  else if local_exprs = [] then My_our("local", local_vars, raw_pos2pos pos) +  else die_with_rawpos pos "bad argument to \"local\"" + +let op prio s (_, both) = prio, (((), both), s) +let op_p prio s e = sp_p e ; op prio s e + +let call_op((prio, (prev_ter, op)), ter, para) =  +  sp_same prev_ter ter ; +  check_my_our op para (snd ter); +  prio, Call_op(op, para) + +let sub_declaration (name, proto) body = Sub_declaration(name, proto, Block body) +let anonymous_sub body = Anonymous_sub (Block body)  let call(e, para) =     (match e with diff --git a/perl_checker.src/parser_helper.mli b/perl_checker.src/parser_helper.mli index 4655810..66395c1 100644 --- a/perl_checker.src/parser_helper.mli +++ b/perl_checker.src/parser_helper.mli @@ -1,6 +1,8 @@  val bpos : int * int  val raw_pos2pos : 'a * 'b -> string * 'a * 'b  val pos_range : +  'a * ('b * (int * int)) -> 'c * ('d * (int * int)) -> string * int * int +val sp_pos_range :    'a * ('b * (int * int)) -> 'c * ('d * (int * int)) -> 'b * (int * int)  val get_pos : 'a * ('b * ('c * 'd)) -> string * 'c * 'd  val var_dollar_ : Types.fromparser @@ -11,8 +13,10 @@ val un_parenthesize_full : Types.fromparser -> Types.fromparser  val not_complex : Types.fromparser -> bool  val not_simple : Types.fromparser -> bool  val string_of_Ident : Types.fromparser -> string -val msg_with_pos : int * int -> string -> string -val die_with_pos : int * int -> string -> 'a +val from_scalar : Types.fromparser * 'a -> Types.fromparser +val from_array : Types.fromparser * 'a -> Types.fromparser +val msg_with_rawpos : int * int -> string -> string +val die_with_rawpos : int * int -> string -> 'a  val warn : int * int -> string -> unit  val die_rule : string -> 'a  val warn_rule : string -> unit @@ -48,17 +52,27 @@ val check_parenthesized_first_argexpr :    ('a * Types.fromparser list) * (Types.spaces * (int * 'b)) -> unit  val check_foreach : string * ('a * (int * int)) -> unit  val check_for : string * ('a * (int * int)) -> unit -val check_package : Types.fromparser list -> unit -val check_my : string -> Types.fromparser list -> 'a * (int * int) -> unit +val check_MULT_is_x : string * 'a -> unit +val check_my : string * 'a -> unit +val check_my_our : +  string -> Types.fromparser list -> 'a * (int * int) -> unit  val check_block_sub :    Types.fromparser list * (Types.spaces * (int * int)) ->    'a * (Types.spaces * (int * 'b)) -> unit  val check_block_ref :    Types.fromparser list * (Types.spaces * (int * int)) ->    'a * (Types.spaces * (int * 'b)) -> unit +val check_my_our_paren : ((bool * 'a) * 'b) * 'c -> unit +val only_one : Types.fromparser list * ('a * (int * int)) -> Types.fromparser +val only_one_in_List : +  ('a * Types.fromparser) * ('b * (int * int)) -> Types.fromparser +val to_List : Types.fromparser list -> Types.fromparser +val deref_arraylen : Types.fromparser -> Types.fromparser  val to_Ident :    (string option * string) * ('a * (int * int)) -> Types.fromparser  val to_Raw_string : string * ('a * (int * int)) -> Types.fromparser +val to_Local : +  ('a * Types.fromparser) * ('b * (int * int)) -> Types.fromparser  val op : 'a -> 'b -> 'c * 'd -> 'a * ((unit * 'd) * 'b)  val op_p :    'a -> @@ -69,14 +83,9 @@ val call_op :    ('a * (('b * (Types.spaces * (int * 'c))) * string)) *    ('d * (Types.spaces * (int * int))) * Types.fromparser list ->    'a * Types.fromparser -val only_one : Types.fromparser list * ('a * (int * int)) -> Types.fromparser -val only_one_in_List : -  ('a * Types.fromparser) * ('b * (int * int)) -> Types.fromparser -val array_ident_to_hash_ident : -  Types.fromparser * ('a * (int * int)) -> Types.fromparser -val to_List : Types.fromparser list -> Types.fromparser  val sub_declaration :    Types.fromparser * string -> Types.fromparser list -> Types.fromparser +val anonymous_sub : Types.fromparser list -> Types.fromparser  val call : Types.fromparser * Types.fromparser list -> Types.fromparser  val call_one_scalar_para :    string * ('a * (int * int)) -> diff --git a/perl_checker.src/perl_checker.ml b/perl_checker.src/perl_checker.ml index 7e951a8..78dc2d5 100644 --- a/perl_checker.src/perl_checker.ml +++ b/perl_checker.src/perl_checker.ml @@ -1,19 +1,72 @@  open Types +open Common +open Tree -let _ =  -  let args = List.tl (Array.to_list Sys.argv) in -  let args = if args = [] then ["/tmp/t.pl"] else args in -  List.iter (fun file -> -    try -      let lexbuf = Lexing.from_channel (Unix.open_process_in (Printf.sprintf "expand \"%s\"" file)) in +let inc = +  let inc_ref = ref [] in +  let ignored_packages = ref [] in +  let rec updir dir nb = +    if nb = 0 then dir else +    match dir with +    | "." -> String.concat "/" (times ".." nb) +    | _ -> updir (Filename.dirname dir) (nb-1) +  in +  fun file_name package_name has_package_name -> +    if !inc_ref = [] then ( +      let reldir = if has_package_name then updir file_name (List.length(split_at2 ':'':' package_name)) else "." in +      let default = readlines (Unix.open_process_in "perl -le 'print foreach @INC'") in +      inc_ref := reldir :: default ; +              try -	Info.start_a_new_file file ; -	let tokens = Lexer.get_token Lexer.token lexbuf in -	let t = Parser_helper.parse_tokens Parser.prog tokens (Some lexbuf) in -	let _,_ = t, t in () -      with Failure s -> ( -	prerr_endline s ; -	exit 1 +	ignored_packages := readlines (open_in (reldir ^ "/.perl_checker")) +      with Sys_error _ -> () +    ); +    !inc_ref, !ignored_packages + +let findfile dirs f = List.find Sys.file_exists (List.map (fun dir -> dir ^ "/" ^ f) dirs) + +let rec parse_file state file = +  try +    if !Flags.verbose then prerr_endline ("checking " ^ file) ; +    let lexbuf = Lexing.from_channel (Unix.open_process_in (Printf.sprintf "expand \"%s\"" file)) in +    try +      Info.start_a_new_file file ; +      let tokens = Lexer.get_token Lexer.token lexbuf in +      let t = Parser_helper.parse_tokens Parser.prog tokens (Some lexbuf) in +      let package = get_global_info_from_package t in +      Tree.get_global_vars_declaration state package ; +      let state = { state with per_package = (package.package_name, package) :: state.per_package } in +      let state = List.fold_left parse_package_if_needed state package.uses in +      state +    with Failure s -> ( +      prerr_endline s ; +      exit 1       ) -    with _ -> prerr_endline ("bad file " ^ file) -  ) args +  with _ -> failwith ("bad file " ^ file) + +and parse_package_if_needed state (package_name, (_, pos)) = +  if List.mem_assoc package_name state.per_package then state else +  try +    let package = snd (List.hd state.per_package) in +    let inc, ignored_packages = inc package.file_name package.package_name package.has_package_name in +    if List.mem package_name ignored_packages then state  +    else +      let file = String.concat "/" (split_at2 ':'':' package_name) ^ ".pm" in +      parse_file state (findfile inc file) +  with Not_found ->  +    Tree.warn_with_pos pos (Printf.sprintf "can't find package %s" package_name) ; +    state + + +let parse_options = +  let args_r = ref [] in +  let options = [ +    "-v", Arg.Set Flags.verbose, "  be verbose" ; +    "-q", Arg.Set Flags.quiet, "  be quiet" ; +  ] in +  let usage = "Usage: perl_checker [-v] [-q] <files>\nOptions are:" in +  Arg.parse options (lpush args_r) usage; + +  let args = if !args_r = [] then ["../t.pl"] else !args_r in +  let state = List.fold_left parse_file default_state args in +  List.iter (check_tree state) (List.map snd state.per_package) diff --git a/perl_checker.src/tree.ml b/perl_checker.src/tree.ml new file mode 100644 index 0000000..7353629 --- /dev/null +++ b/perl_checker.src/tree.ml @@ -0,0 +1,424 @@ +open Types +open Common +open Printf + +type exports = {  +    export_ok : (context * string) list ; +    export_auto : (context * string) list ; +    export_tags : (string * (context * string) list) list ; +    re_export_all : bool ; +  } + +type uses = (string * ((context * string) list option * pos)) list + +type per_package = { +    file_name : string ; +    package_name : string ; has_package_name : bool ; +    exports : exports ; +    uses : uses ; +    body : fromparser list; +  } +type state = { +    per_package : (string * per_package) list ; +    global_vars_declared : (context * string * string, pos) Hashtbl.t ; +    global_vars_used : ((context * string * string) * pos) list ref ; +  } + +type vars = {  +    my_vars : (context * string) list list ; +    our_vars : (context * string) list list ; +    imported : ((context * string) * string) list ; +    current_package : string ; +    state : state ; +  } + +let anonymous_package_count = ref 0 +let default_state = { per_package = []; global_vars_declared = Hashtbl.create 256; global_vars_used = ref [] } +let empty_exports = { export_ok = []; export_auto = []; export_tags = []; re_export_all = false } + + +let die_with_pos pos msg = failwith (Info.pos2sfull pos ^ msg) +let warn_with_pos pos msg = prerr_endline (Info.pos2sfull pos ^ msg) + +let context2s = function +  | I_scalar -> "$" +  | I_hash -> "%" +  | I_array -> "@" +  | I_func -> "&" +  | I_raw -> "" +  | I_star -> "*" +let variable2s(context, ident) = context2s context ^ ident +let s2context s =  +  match s.[0] with +  | '$' -> I_scalar, skip_n_char 1 s +  | '%' -> I_hash  , skip_n_char 1 s +  | '@' -> I_array , skip_n_char 1 s +  | '&' -> I_func  , skip_n_char 1 s +  | '*' -> I_star  , skip_n_char 1 s +  | _ -> I_raw, s + + + +let get_current_package t = +  match t with +  | Package(Ident _ as ident) :: _ ->  +      Some (Parser_helper.string_of_Ident ident) +  | _ ->  +      if str_ends_with !Info.current_file ".pm" then warn_with_pos (!Info.current_file, 0, 0) (sprintf "module %s does not have \"package xxxx;\" on its first line" !Info.current_file) ; +      None + +let from_qw = function +  | Call_op("qw", [ Raw_string(s, pos)]) ->  +      List.map (fun s ->  +	let context, s' = s2context s in +	let context = +	  match context with +	  | I_raw -> if s'.[0] = ':' then I_raw else I_func +	  | I_func -> warn_with_pos pos "weird, exported name with a function context especially given"; I_func +	  | _ -> context  +	in context, s' +	       ) (words s) +  | String(_, pos) -> +      warn_with_pos pos "not recognised yet" ; +      [] +  | _ -> internal_error "get_exported" + +let get_exported t = +  List.fold_left (fun exports e -> +    match e with +    | List [ Call_op("=", [ Deref(I_array, Ident(None, "EXPORT", pos)); Call _ ]) ] +    | List [ Call_op("=", [ My_our("our", [(I_array, "EXPORT")], pos);  Call _ ]) ] -> +	if not exports.re_export_all then warn_with_pos pos "unrecognised @EXPORT" ; +	exports + +    | List [ Call_op("=", [ Deref(I_array, Ident(None, "EXPORT", pos)); v ])] +    | List [ Call_op("=", [ My_our("our", [(I_array, "EXPORT")], pos); v ])] -> +	if exports.export_auto <> [] then warn_with_pos pos "weird, @EXPORT set twice" ; +	{ exports with export_auto = from_qw v } + +    | Perl_checker_comment("RE-EXPORT-ALL", _) -> { exports with re_export_all = true } + +    | List [ Call_op("=", [ Deref(I_array, Ident(None, "EXPORT_OK", pos)); v ])] +    | List [ Call_op("=", [ My_our("our", [(I_array, "EXPORT_OK")], pos);  v ])] -> +	if exports.export_ok <> [] then warn_with_pos pos "weird, @EXPORT_OK set twice" ; +	(match v with +	| Call(Deref(I_func, Ident(None, "map", _)),  +	       [ Anonymous_sub(Block [List [Deref(I_array, Deref(I_scalar, Ident (None, "_", _)))]]); +		 Call(Deref(I_func, Ident(None, "values", _)), [ Deref(I_hash, Ident(None, "EXPORT_TAGS", _))])]) -> +		   { exports with export_ok = collect snd exports.export_tags } +	| _ -> { exports with export_ok = from_qw v }) + +    | List [ Call_op("=", [ Deref(I_hash, Ident(None, "EXPORT_TAGS", pos)); v ])] +    | List [ Call_op("=", [ My_our("our", [(I_hash, "EXPORT_TAGS")], pos);  v ])] -> +	(try +	  let export_tags = +	    match v with +	    | List [ List l ] -> +		List.map (function +		  | Ident(None, tag, _), Ref(I_array, List [List [v]]) -> +		      let para = +			match v with +			| Deref(I_array, Ident(None, "EXPORT_OK", _)) -> exports.export_ok +			| _ -> from_qw v +		      in +		      ":" ^ tag, para +		  | _ -> raise Not_found +	        ) (group_by_2 l) +	    | _ -> raise Not_found +	  in +	  if exports.export_tags <> [] then warn_with_pos pos "weird, %EXPORT_TAGS set twice" ; +	  { exports with export_tags = export_tags } +	with _ -> +	  warn_with_pos pos "unrecognised @EXPORT_TAGS" ; +	  exports) +    | List (My_our _ :: _) -> +	let _,_ = e,e in +	exports +    | _ -> exports +  ) empty_exports t + +let uses_external_package = function +  | "vars" | "Exporter" | "diagnostics" | "strict" | "lib" | "POSIX" | "Config" | "Socket" | "Net::FTP" | "IO::Socket" | "DynaLoader" -> true +  | _ -> false + +let get_uses t = +  List.fold_left (fun uses e -> +    match e with +    | Use(Ident _ as pkg, _) when uses_external_package (Parser_helper.string_of_Ident pkg) -> uses +    | Use(Ident(_, _, pos) as ident, l) -> +	let package = Parser_helper.string_of_Ident ident in +	let para = if l = [] then None else Some(from_qw (List.hd l)) in +	(package, (para, pos)) :: uses +    | _ -> uses +  ) [] t + +let get_global_info_from_package t = +  let exports = get_exported t in +  let uses = get_uses t in +  let current_package = get_current_package t in +  let package_name = +    match current_package with +    | None ->  +	if exports.export_ok <> [] || exports.export_auto <> [] || exports.export_tags <> [] then +	  die_with_pos (!Info.current_file, 0, 0) "file with no \"package\" wants to export!" +	else +	  (incr anonymous_package_count ; sprintf "anonymous%d" !anonymous_package_count) +    | Some name -> name +  in { file_name = !Info.current_file ; package_name = package_name; has_package_name = current_package <> None ; exports = exports ; uses = uses ; body = t } + +let get_global_vars_declaration state package =  +  List.iter (function +    | Sub_declaration(Ident(fq, name, pos), _proto, _) -> +	Hashtbl.add state.global_vars_declared (I_func, some_or fq package.package_name, name) pos + +    | List [ Call_op("=", [My_our("our", ours, pos); _]) ] +    | My_our("our", ours, pos) -> +	List.iter (fun (context, name) -> Hashtbl.add state.global_vars_declared (context, package.package_name, name) pos) ours +    | Use(Ident(None, "vars", pos), [ours]) ->  +	List.iter (fun (context, name) -> Hashtbl.add state.global_vars_declared (context, package.package_name, name) pos) (from_qw ours) +    | Use(Ident(None, "vars", pos), _) ->  +	die_with_pos pos "usage: \"use vars qw($var func)\"" +    | _ -> () +  ) package.body + +let get_imports state package = +  let rec get_one (package_name, (imports, pos)) = +    try +      let package_used = List.assoc package_name state.per_package in +      let exports = package_used.exports in +      let imports_vars = +	match imports with +	| None ->  +	    if exports.re_export_all then +	      collect (fun (package_name, _) -> (List.assoc package_name state.per_package).exports.export_ok) package_used.uses +	    else exports.export_auto +	| Some l ->  +	    collect (function +	      | I_raw, tag ->  +		  (try  +		    List.assoc tag exports.export_tags +		  with Not_found -> die_with_pos pos (sprintf "package %s doesn't export tag %s" package_name tag)) +	      | variable -> +		  if List.mem variable exports.export_ok then +		    [ variable ] +		  else +		    die_with_pos pos (sprintf "package %s doesn't export %s" package_name (variable2s variable)) +		    ) l +      in +      List.map (fun (context, name) -> (context, name), package.package_name) imports_vars +    with Not_found -> [] +  in +  collect get_one package.uses + +let rec fold_tree f env e =  +  match f env e with +  | Some env -> env +  | None -> +  match e with +  | Anonymous_sub(e') +  | Ref(_, e') +  | Deref(_, e') +  | Package(e') +      -> fold_tree f env e' + +  | Diamond(e') +       -> fold_tree_option f env e' + +  | Sub_declaration(e1, _, e2) +  | Deref_with(_, e1, e2) +  | Binop(_, e1, e2) +       ->  +	 let env = fold_tree f env e1 in +	 let env = fold_tree f env e2 in +	 env + +  | List l +  | Block l +  | Call_op(_, l) +      -> List.fold_left (fold_tree f) env l + +  | Call(e', l) +  | CallP(e', l) +  | Use(e', l) +    ->  +      let env = fold_tree f env e' in +      List.fold_left (fold_tree f) env l + +  | Method_call(e1, e2, l) +  | Method_callP(e1, e2, l) +    -> +      let env = fold_tree f env e1 in +      let env = fold_tree f env e2 in +      List.fold_left (fold_tree f) env l + +  | If_then_else(_, t_l, e')  +    ->  +      let env = fold_tree_option f env e' in +      List.fold_left (fun env (e1, e2) ->  +	let env = fold_tree f env e1 in +	let env = fold_tree f env e2 in +	env +      ) env t_l + +  | _ -> env + +and fold_tree_option f env = function +  | None -> env +  | Some e -> fold_tree f env e + + +let is_my_declared vars t = List.exists (List.exists ((=) t)) vars.my_vars +let is_our_declared vars t = List.exists (List.exists ((=) t)) vars.our_vars +let is_global_var_declared vars (context, fq, name) = +  let fq = some_or fq vars.current_package in +  Hashtbl.mem vars.state.global_vars_declared (context, fq, name) + +let is_global_var context ident =  +  match context with +  | I_scalar ->  +      (match ident with +      | "_" | "@" | "!" | ">" | "\\" | "$" | "^A" | "'" | "/" | "?" | "<" | "^W" | "|" | "^I" +      | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" -> true +      | _ -> false) +  | I_array ->  +      (match ident with +      | "_" | "ARGV" -> true +      | _ -> false) +  | I_hash -> +      (match ident with +      | "ENV" | "SIG" -> true +      | _ -> false) +  | I_star -> +      (match ident with +      | "STDIN" | "STDOUT" | "STDERR" -> true +      | _ -> false) +  | I_func -> +      (match ident with +      | "abs" | "alarm" | "basename" | "bless"  +      | "caller" | "chdir" | "chmod" | "chomp" | "chop" | "chown" | "chr" | "chroot" | "close" | "closedir" | "crypt" | "delete" | "die" +      | "each" | "eval" | "exec" | "exists" | "exit" | "fcntl" | "fileno" | "fork" +      | "gethostbyaddr" | "gethostbyname" | "getgrnam" | "getgrgid" | "getpwent" | "getpwnam" | "getpwuid" | "gmtime" | "goto" | "grep" | "hex" +      | "index" | "int" | "ioctl" | "join" | "keys" | "kill" +      | "last" | "lc" | "length" | "link" | "localtime" | "log" | "lstat" +      | "map" | "mkdir" | "next" | "oct" | "open" | "opendir" | "ord" +      | "pack" | "pipe" | "pop" | "print" | "printf" | "push" | "quotemeta"  +      | "rand" | "read" | "readdir" | "readlink" | "redo" | "ref" | "rename" | "require" | "return" | "reverse" | "rmdir" +      | "scalar" | "select" | "setpwent" | "shift" | "sleep" | "sort" | "splice" | "split" | "sprintf" | "stat" | "substr" +      | "symlink" | "sysopen" | "sysread" | "sysseek" | "system" | "syswrite" | "time" | "uc" | "umask" | "unpack" | "unshift" +      | "unlink" | "utime" | "values" | "vec" | "waitpid" | "wantarray" | "warn" | "write" +	  -> true + +      | _ -> false) +  | _ -> false + +let check_variable (context, var) vars =  +  match var with +  | Ident(None, ident, pos) when context <> I_func -> +      if is_my_declared vars (context, ident) || is_our_declared vars (context, ident) ||  +         List.mem_assoc (context, ident) vars.imported || is_global_var context ident || is_global_var_declared vars (context, None, ident) +      then ()  +      else warn_with_pos pos (sprintf "undeclared variable %s" (variable2s(context, ident))) +  | Ident(fq, name, pos) ->  +      if context = I_func && fq = None && is_global_var context name ||  +         is_global_var_declared vars (context, fq, name) +      then () +      else lpush vars.state.global_vars_used ((context, some_or fq vars.current_package, name), pos)  +  | _ -> () + +let declare_My vars (mys, pos) = +  let l_new = List.filter (fun (context, ident) -> +    if context = I_raw then +      if ident = "undef" then false else die_with_pos pos (sprintf "bad ident \"%s\" in my" ident) +    else true +  ) mys in +  let l_pre = List.hd vars.my_vars in +  List.iter (fun v -> +    if List.exists ((=) v) l_pre then warn_with_pos pos (sprintf "redeclared variable \"%s\"" (variable2s v)) +  ) l_new ; +  { vars with my_vars = (l_new @ l_pre) :: List.tl vars.my_vars } + +let declare_Our vars (ours, pos) = +  match vars.our_vars with +  | [] -> vars (* we're at the toplevel, already declared in global_vars_declared *) +  | l_pre :: other -> +      List.iter (fun v -> +	if List.exists ((=) v) l_pre then warn_with_pos pos (sprintf "redeclared variable \"%s\"" (variable2s v)) +      ) ours ; +      { vars with our_vars = (ours @ l_pre) :: other } + +let declare_My_our vars (my_or_our, l, pos) = +  match my_or_our with +  | "my"  -> declare_My  vars (l, pos) +  | "local" +  | "our" -> declare_Our vars (l, pos) +  | _ -> internal_error "declare_My_our" + + +let check_variables vars t =  +  let rec check_variables_ vars t = fold_tree check vars t +  and check vars = function +    | Block l -> +	let vars = { vars with my_vars = [] :: vars.my_vars ; our_vars = [] :: vars.our_vars } in +	let vars = List.fold_left check_variables_ vars l in +	let vars = { vars with my_vars = List.tl vars.my_vars ; our_vars = List.tl vars.our_vars } in +	Some vars +    | Call(Deref(I_func, Ident(None, "sort", _)), (Anonymous_sub(Block f) :: l)) -> +	let vars = List.fold_left check_variables_ vars l in +	let vars = { vars with my_vars = [ I_scalar, "a" ; I_scalar, "b" ] :: vars.my_vars ; our_vars = [] :: vars.our_vars } in +	let vars = List.fold_left check_variables_ vars f in +	let vars = { vars with my_vars = List.tl vars.my_vars ; our_vars = List.tl vars.our_vars } in +	Some vars + +    | Call_op("foreach my", [my; expr; Block block]) -> +	let vars = check_variables_ vars expr in +	let vars = check_variables_ vars (Block (my :: block)) in +	Some vars +    | Call_op(op, cond :: Block first_bl :: other) when op = "if" || op = "while" || op = "unless" || op = "until" -> +	let vars = { vars with my_vars = [] :: vars.my_vars ; our_vars = [] :: vars.our_vars } in +	let vars = check_variables_ vars cond in +	let vars = List.fold_left check_variables_ vars first_bl in +	let vars = { vars with my_vars = List.tl vars.my_vars ; our_vars = List.tl vars.our_vars } in +	let vars = List.fold_left check_variables_ vars other in +	Some vars + +    | Sub_declaration(Ident(fq, name, pos), _proto, body) -> +	let vars = declare_Our vars ([ I_func, (some_or fq vars.current_package) ^ "::" ^ name ], pos) in +	let vars = check_variables_ vars body in +	Some vars + +    | My_our(my_or_our, mys, pos) -> Some(declare_My_our vars (my_or_our, mys, pos)) +    | Deref(context, (Ident _ as var)) ->  +	check_variable (context, var) vars ; +	Some vars +    | Deref_with(context, (Ident _ as var), para) ->  +	let vars = check_variables_ vars para in +	check_variable (context, var) vars ; +	Some vars + +    | Call_op(op, [My_our(my_or_our, mys, pos); e]) -> +	if op = "=" then +	  (* check e first *) +	  let vars = check_variables_ vars e in +	  Some(declare_My_our vars (my_or_our, mys, pos)) +	else +	  (warn_with_pos pos "weird" ; None) + +    | _ -> None +  in +  let vars = List.fold_left check_variables_ { vars with my_vars = [[]] } t in +  vars + +(* +let check_vars vars = +  List.iter (function  +    | I_func, (f, pos) ->  +	if not (is_our_declared vars (I_func, f)) then warn_with_pos pos ("unknown function " ^ f) +    | _ -> () +  ) vars.global_vars_used +*) + +let check_tree state package = +  let imports = get_imports state package in +  let vars = { my_vars = [[]]; our_vars = []; imported = imports; current_package = package.package_name; state = state } in +  let _vars = check_variables vars package.body in +  () diff --git a/perl_checker.src/tree.mli b/perl_checker.src/tree.mli new file mode 100644 index 0000000..736c68e --- /dev/null +++ b/perl_checker.src/tree.mli @@ -0,0 +1,32 @@ +open Types + +type exports = { +  export_ok : (context * string) list; +  export_auto : (context * string) list; +  export_tags : (string * (context * string) list) list; +  re_export_all : bool; +}  + + +type uses = (string * ((context * string) list option * pos)) list + +type per_package = { +    file_name : string ; +    package_name : string ; has_package_name : bool ; +    exports : exports ; +    uses : uses ; +    body : fromparser list; +  } +type state = { +    per_package : (string * per_package) list; +    global_vars_declared : (context * string * string, pos) Hashtbl.t; +    global_vars_used : ((context * string * string) * pos) list ref; +  }  + +val default_state : state +val get_global_info_from_package : fromparser list -> per_package +val get_global_vars_declaration : state -> per_package -> unit +val check_tree : state -> per_package -> unit + +val die_with_pos : string * int * int -> string -> 'a +val warn_with_pos : string * int * int -> string -> unit diff --git a/perl_checker.src/types.mli b/perl_checker.src/types.mli index ceb5804..d11ff9a 100644 --- a/perl_checker.src/types.mli +++ b/perl_checker.src/types.mli @@ -11,9 +11,10 @@ type spaces =    | Space_cr    | Space_none -type context = I_scalar | I_hash | I_array | I_func | I_raw | I_star | I_arraylen +type context = I_scalar | I_hash | I_array | I_func | I_raw | I_star  type fromparser =  +   | Undef     | Ident of string option * string * pos     | Num of string * pos     | Raw_string of string * pos @@ -36,13 +37,13 @@ type fromparser =     | Method_call of fromparser * fromparser * fromparser list     | Method_callP of fromparser * fromparser * fromparser list -   | Anonymous_sub of fromparser list -   | My of fromparser -   | Local of fromparser +   | Anonymous_sub of fromparser +   | My_our of string * (context * string) list * pos     | Use of fromparser * fromparser list -   | Sub_declaration of fromparser * string * fromparser list (* name, prototype, body *) +   | Sub_declaration of fromparser * string * fromparser (* name, prototype, body *)     | Package of fromparser     | Label of string +   | Perl_checker_comment of string * pos     | Too_complex     | Semi_colon  | 
