Jelajahi Sumber

make ParserEntry.parse/parse_string methods accept grammar entry (#9487)

* make ParserEntry.parse/parse_string methods accept grammar entry

so we don't need to concat fake types to the thing we want to parse

* ParserEntry.parse_expr_string is not raising Exit anymore

* make #3531 error and provide more helpful error message for empty interpolated expressions

* adjust test
Dan Korostelev 5 tahun lalu
induk
melakukan
8bb1fb0247

+ 14 - 0
src/syntax/lexer.ml

@@ -283,6 +283,20 @@ let sharp_ident = [%sedlex.regexp?
 	)
 ]
 
+let is_whitespace = function
+	| ' ' | '\n' | '\r' | '\t' -> true
+	| _ -> false
+
+let string_is_whitespace s =
+	try
+		for i = 0 to String.length s - 1 do
+			if not (is_whitespace (String.unsafe_get s i)) then
+				raise Exit
+		done;
+		true
+	with Exit ->
+		false
+
 let idtype = [%sedlex.regexp? Star '_', 'A'..'Z', Star ('_' | 'a'..'z' | 'A'..'Z' | '0'..'9')]
 
 let integer = [%sedlex.regexp? ('1'..'9', Star ('0'..'9')) | '0']

+ 17 - 14
src/syntax/parserEntry.ml

@@ -204,7 +204,7 @@ class dead_block_collector conds = object(self)
 end
 
 (* parse main *)
-let parse ctx code file =
+let parse entry ctx code file =
 	let old = Lexer.save() in
 	let restore_cache = TokenCache.clear () in
 	let was_display = !in_display in
@@ -360,7 +360,7 @@ let parse ctx code file =
 		Some t
 	) in
 	try
-		let l = parse_file s in
+		let l = entry s in
 		(match !mstack with p :: _ -> syntax_error Unclosed_conditional ~pos:(Some p) sraw () | _ -> ());
 		let was_display_file = !in_display_file in
 		restore();
@@ -384,7 +384,7 @@ let parse ctx code file =
 			restore();
 			raise e
 
-let parse_string com s p error inlined =
+let parse_string entry com s p error inlined =
 	let old = Lexer.save() in
 	let old_file = (try Some (Hashtbl.find Lexer.all_files p.pfile) with Not_found -> None) in
 	let old_display = display_position#get in
@@ -409,7 +409,7 @@ let parse_string com s p error inlined =
 		in_display_file := false;
 	end;
 	let result = try
-		parse com (Sedlexing.Utf8.from_string s) p.pfile
+		parse entry com (Sedlexing.Utf8.from_string s) p.pfile
 	with Error (e,pe) ->
 		restore();
 		error (error_msg e) (if inlined then pe else p)
@@ -421,13 +421,16 @@ let parse_string com s p error inlined =
 	result
 
 let parse_expr_string com s p error inl =
-	let head = "class X{static function main() " in
-	let head = (if p.pmin > String.length head then head ^ String.make (p.pmin - String.length head) ' ' else head) in
-	let rec loop e = let e = Ast.map_expr loop e in (fst e,p) in
-	let extract_expr (_,decls) = match decls with
-		| [EClass { d_data = [{ cff_name = "main",null_pos; cff_kind = FFun { f_expr = Some e } }]},_] -> (if inl then e else loop e)
-		| _ -> raise Exit
-	in
-	match parse_string com (head ^ s ^ ";}") p error inl with
-	| ParseSuccess(data,is_display_file,pdi) -> ParseSuccess(extract_expr data,is_display_file,pdi)
-	| ParseError(data,error,errors) -> ParseError(extract_expr data,error,errors)
+	let s = if p.pmin > 0 then (String.make p.pmin ' ') ^ s else s in
+	let result = parse_string expr com s p error inl in
+	if inl then
+		result
+	else begin
+		let rec loop e =
+			let e = map_expr loop e in
+			(fst e,p)
+		in
+		match result with
+		| ParseSuccess(data,is_display_file,pdi) -> ParseSuccess(loop data,is_display_file,pdi)
+		| ParseError(data,error,errors) -> ParseError(loop data,error,errors)
+	end

+ 9 - 15
src/typing/macroContext.ml

@@ -126,21 +126,16 @@ let load_macro_ref : (typer -> bool -> path -> string -> pos -> (typer * ((strin
 let make_macro_api ctx p =
 	let parse_expr_string s p inl =
 		typing_timer ctx false (fun() ->
-			try
-				begin match ParserEntry.parse_expr_string ctx.com.defines s p error inl with
-					| ParseSuccess(data,true,_) when inl -> data (* ignore errors when inline-parsing in display file *)
-					| ParseSuccess(data,_,_) -> data
-					| ParseError _ -> raise MacroApi.Invalid_expr
-				end
-			with Exit ->
-				raise MacroApi.Invalid_expr)
+			match ParserEntry.parse_expr_string ctx.com.defines s p error inl with
+				| ParseSuccess(data,true,_) when inl -> data (* ignore errors when inline-parsing in display file *)
+				| ParseSuccess(data,_,_) -> data
+				| ParseError _ -> raise MacroApi.Invalid_expr)
 	in
 	let parse_metadata s p =
 		try
-			match ParserEntry.parse_string ctx.com.defines (s ^ " typedef T = T") null_pos error false with
-			| ParseSuccess((_,[ETypedef t,_]),_,_) -> t.d_meta
+			match ParserEntry.parse_string Grammar.parse_meta ctx.com.defines s null_pos error false with
+			| ParseSuccess(meta,_,_) -> meta
 			| ParseError(_,_,_) -> error "Malformed metadata string" p
-			| _ -> die "" __LOC__
 		with _ ->
 			error "Malformed metadata string" p
 	in
@@ -231,15 +226,14 @@ let make_macro_api ctx p =
 		MacroApi.type_patch = (fun t f s v ->
 			typing_timer ctx false (fun() ->
 				let v = (match v with None -> None | Some s ->
-					match ParserEntry.parse_string ctx.com.defines ("typedef T = " ^ s) null_pos error false with
-					| ParseSuccess((_,[ETypedef { d_data = ct },_]),_,_) -> Some ct
+					match ParserEntry.parse_string Grammar.parse_complex_type ctx.com.defines s null_pos error false with
+					| ParseSuccess((ct,_),_,_) -> Some ct
 					| ParseError(_,(msg,p),_) -> Parser.error msg p (* p is null_pos, but we don't have anything else here... *)
-					| _ -> die "" __LOC__
 				) in
 				let tp = get_type_patch ctx t (Some (f,s)) in
 				match v with
 				| None -> tp.tp_remove <- true
-				| Some _ -> tp.tp_type <- Option.map fst v
+				| Some t -> tp.tp_type <- Some t
 			);
 		);
 		MacroApi.meta_patch = (fun m t f s p ->

+ 1 - 1
src/typing/typeloadParse.ml

@@ -36,7 +36,7 @@ let parse_file_from_lexbuf com file p lexbuf =
 	Lexer.init file;
 	incr stats.s_files_parsed;
 	let parse_result = try
-		ParserEntry.parse com.defines lexbuf file
+		ParserEntry.parse Grammar.parse_file com.defines lexbuf file
 	with
 		| Sedlexing.MalFormed ->
 			t();

+ 8 - 8
src/typing/typer.ml

@@ -1551,16 +1551,16 @@ and format_string ctx s p =
 		let slen = send - pos - 1 in
 		let scode = String.sub s (pos + 1) slen in
 		min := !min + 2;
-		if slen > 0 then begin
+		begin
 			let e =
 				let ep = { p with pmin = !pmin + pos + 2; pmax = !pmin + send + 1 } in
-				try
-					begin match ParserEntry.parse_expr_string ctx.com.defines scode ep error true with
-						| ParseSuccess(data,_,_) -> data
-						| ParseError(_,(msg,p),_) -> error (Parser.error_msg msg) p
-					end
-				with Exit ->
-					error "Invalid interpolated expression" ep
+				let error msg pos =
+					if Lexer.string_is_whitespace scode then error "Expression cannot be empty" ep
+					else error msg pos
+				in
+				match ParserEntry.parse_expr_string ctx.com.defines scode ep error true with
+					| ParseSuccess(data,_,_) -> data
+					| ParseError(_,(msg,p),_) -> error (Parser.error_msg msg) p
 			in
 			add_expr e slen
 		end;

+ 1 - 1
tests/misc/projects/Issue6826/compile-fail.hxml.stderr

@@ -1,2 +1,2 @@
-Main.hx:5: characters 20-21 : Invalid interpolated expression
+Main.hx:5: characters 20-21 : Expression cannot be empty
 Main.hx:5: characters 20-21 : For function argument 'v'

+ 0 - 7
tests/unit/src/unit/issues/Issue3531.hx

@@ -1,7 +0,0 @@
-package unit.issues;
-
-class Issue3531 extends Test {
-	function test() {
-		eq("ab", 'a${}b');
-	}
-}