diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index fcb07729356..33025f25895 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -33,6 +33,7 @@ type 'value compiler_api = { after_generate : (unit -> unit) -> unit; on_type_not_found : (string -> 'value) -> unit; parse_string : string -> Globals.pos -> bool -> Ast.expr; + load_lexer_lines : string -> string -> unit; parse : 'a . ((Ast.token * Globals.pos) Stream.t -> 'a) -> string -> 'a; type_expr : Ast.expr -> Type.texpr; resolve_type : Ast.complex_type -> Globals.pos -> t; @@ -1908,6 +1909,12 @@ let macro_api ccom get_api = if s = "" then (get_api()).exc_string "Invalid expression"; encode_expr ((get_api()).parse_string s (decode_pos p) (decode_bool b)) ); + "load_lexer_lines", vfun2 (fun f c -> + let f = decode_string f in + let content = decode_string c in + (get_api()).load_lexer_lines f content; + vnull + ); "make_expr", vfun2 (fun v p -> encode_expr (value_to_expr v (decode_pos p)) ); diff --git a/src/syntax/lexer.ml b/src/syntax/lexer.ml index c3deee69db9..6a462e2bc57 100644 --- a/src/syntax/lexer.ml +++ b/src/syntax/lexer.ml @@ -210,27 +210,19 @@ let find_line p f = loop 0 (Array.length f.lalines) (* resolve a position within a non-haxe file by counting newlines *) -let resolve_pos file = - let ch = open_in_bin file in - let f = make_file file in +let resolve_pos f next skip = let rec loop p = let inc i () = f.lline <- f.lline + 1; f.llines <- (p + i,f.lline) :: f.llines; i in - let i = match input_char ch with + let i = match next() with | '\n' -> inc 1 | '\r' -> - ignore(input_char ch); + skip 1; inc 2 | c -> (fun () -> - let rec skip n = - if n > 0 then begin - ignore(input_char ch); - skip (n - 1) - end - in let code = int_of_char c in if code < 0xC0 then () else if code < 0xE0 then skip 1 @@ -241,8 +233,35 @@ let resolve_pos file = in loop (p + i()) in + loop 0 + +let resolve_file_content_pos file content = + let f = make_file file in + let i = ref 0 in + let next () = + try + let ret = String.get content !i in + incr i; + ret + with Invalid_argument _ -> raise End_of_file + in + let skip n = + i := !i + n + in + try resolve_pos f next skip with End_of_file -> f + +let resolve_file_pos file = + let ch = open_in_bin file in + let f = make_file file in + let next () = input_char ch in + let rec skip n = + if n > 0 then begin + ignore(next ()); + skip (n - 1) + end + in try - loop 0 + resolve_pos f next skip with End_of_file -> close_in ch; f @@ -252,7 +271,7 @@ let find_file file = Hashtbl.find all_files file with Not_found -> try - let f = resolve_pos file in + let f = resolve_file_pos file in Hashtbl.add all_files file f; f with Sys_error _ -> diff --git a/src/typing/macroContext.ml b/src/typing/macroContext.ml index 7efe2499139..fc152c0e3f2 100644 --- a/src/typing/macroContext.ml +++ b/src/typing/macroContext.ml @@ -196,6 +196,10 @@ let make_macro_com_api com mcom p = | ParseSuccess(r,_,_) -> r | ParseError(_,(msg,p),_) -> Parser.error msg p ); + load_lexer_lines = (fun file content -> + let f = Lexer.resolve_file_content_pos file content in + Hashtbl.add Lexer.all_files file f; + ); type_expr = (fun e -> Interp.exc_string "unsupported" ); diff --git a/std/haxe/macro/Context.hx b/std/haxe/macro/Context.hx index eb2d963c04a..7925337c587 100644 --- a/std/haxe/macro/Context.hx +++ b/std/haxe/macro/Context.hx @@ -395,6 +395,15 @@ class Context { return load("do_parse", 3)(expr, pos, true); } + /** + Parse file content for newlines, allowing positions to be resolved + properly inside that file later on (using `Context.parseInlineString` + for example). Works with both real and virtual files. + **/ + public static function loadLexerLines(file:String, content:String):Void { + load("load_lexer_lines", 2)(file, content); + } + /** Builds an expression from `v`. diff --git a/tests/unit/src/unit/issues/Issue3387.hx b/tests/unit/src/unit/issues/Issue3387.hx index 7d57a4c7166..5dfb2712c16 100644 --- a/tests/unit/src/unit/issues/Issue3387.hx +++ b/tests/unit/src/unit/issues/Issue3387.hx @@ -6,7 +6,8 @@ class Issue3387 extends unit.Test { } static macro function getPos(source:String, file:String) { + haxe.macro.Context.loadLexerLines(file, source); var e = haxe.macro.Context.parseInlineString(source, haxe.macro.Context.makePosition({min: 0, max: 0, file: file})); return macro $v{Std.string(e.pos)}; } -} \ No newline at end of file +}