#! /bin/sh # (* exec ocaml "$0" "$@" *) directory ".";; let rec neg_index_from s start c = let l = String.length s in if start = l then raise Not_found else if start > l then invalid_arg "neg_index_from" else if s.[ start ] = c then neg_index_from s (start+1) c else start ;; type directive = Ifdef of string | Ifndef of string | Endif | No_directive let get_directive s = let l_s = String.length s in let is literal = let l_literal = String.length literal in (l_s >= l_literal) && (String.sub s 0 l_literal = literal) && (if l_s > l_literal then s.[l_literal] = ' ' else true) in let get_arg() = try let k1 = String.index s ' ' in let k2 = neg_index_from s k1 ' ' in let k3 = try String.index_from s k2 ' ' with Not_found -> l_s in String.sub s k2 (k3-k2) with Not_found -> failwith "Argument expected" in match s with _ when is "IFDEF" -> Ifdef (get_arg()) | _ when is "IFNDEF" -> Ifndef (get_arg()) | _ when is "ENDIF" -> Endif | _ -> No_directive ;; let rec filter infile inch outch vars linenr skip = (* Copy lines from [inch] to [outch]. The next line read from [inch] will * have line number [linenr]. [vars] contains the list of defined variables. * [skip] indicates that output is to be suppressed. * The function stops at the next ENDIF directive for which no matching * IFDEF or IFNDEF has been found before, or at the end of [inch]. * The function returns the line number of the ENDIF directive. *) output_string outch ("# " ^ string_of_int linenr ^ " \"" ^ infile ^ "\"\n"); try let current_linenr = ref linenr in let current_line = ref (input_line inch) in let dir = ref (get_directive !current_line) in while !dir <> Endif do begin match !dir with | Ifdef v -> let skip' = not (List.mem v vars) in let linenr' = filter infile inch outch vars (!current_linenr+1) (skip || skip') in current_linenr := linenr'; output_string outch ("# " ^ string_of_int (!current_linenr+1) ^ " \"" ^ infile ^ "\"\n"); | Ifndef v -> let skip' = List.mem v vars in let linenr' = filter infile inch outch vars (!current_linenr+1) (skip || skip') in current_linenr := linenr'; output_string outch ("# " ^ string_of_int (!current_linenr+1) ^ " \"" ^ infile ^ "\"\n"); | No_directive -> if not skip then output_string outch (!current_line ^ "\n"); | Endif -> assert false end; (* Continue with next line: *) incr current_linenr; current_line := input_line inch; dir := get_directive !current_line; done; !current_linenr with End_of_file -> 0 ;; let main () = let vars = ref [] in let infile = ref "" in Arg.parse [ "-D", Arg.String (fun s -> vars := s :: !vars), "var define this variable"; ] (fun s -> infile := s) "usage: ifdef [ options ] filename.mlp"; if !infile = "" then failwith "No input file!"; let basename = Filename.chop_extension !infile in let outfile = basename ^ ".ml" in if !infile = outfile then failwith "Input filename ends with .ml, please avoid that"; let inch = open_in !infile in let outch = open_out outfile in try let _ = filter !infile inch outch !vars 1 false in close_out outch; close_in inch; with error -> close_out outch; close_in inch; Sys.remove (outfile) ;; main();;