Просмотр исходного кода

[php] better native `foreach` generation at the cost of the uglier hack

Alexander Kuzmenko 6 лет назад
Родитель
Сommit
0e38b5a2f2
2 измененных файлов с 31 добавлено и 17 удалено
  1. 17 15
      src/generators/genphp7.ml
  2. 14 2
      std/php/Syntax.hx

+ 17 - 15
src/generators/genphp7.ml

@@ -1657,7 +1657,13 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| TBlock exprs -> self#write_expr_block expr
 				| TFor (var, iterator, body) -> fail self#pos __POS__
 				| TIf (condition, if_expr, else_expr) -> self#write_expr_if condition if_expr else_expr
-				| TWhile (condition, expr, do_while) -> self#write_expr_while condition expr do_while
+				| TWhile (condition, expr, do_while) ->
+					(match (reveal_expr_with_parenthesis condition).eexpr with
+						| TField (_, FStatic ({ cl_path = path }, { cf_name = "foreachCondition" })) when path = syntax_type_path  ->
+							self#write_expr_syntax_foreach expr
+						| _ ->
+							self#write_expr_while condition expr do_while
+					)
 				| TSwitch (switch, cases, default ) -> self#write_expr_switch switch cases default
 				| TTry (try_expr, catches) -> self#write_expr_try_catch try_expr catches
 				| TReturn expr -> self#write_expr_return expr
@@ -2401,7 +2407,6 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| "coalesce" -> self#write_expr_syntax_coalesce args
 				| "instanceof" -> self#write_expr_syntax_instanceof args
 				| "nativeClassName" -> self#write_expr_syntax_native_class_name args
-				| "foreach" -> self#write_expr_syntax_foreach args
 				| "construct" -> self#write_expr_syntax_construct args
 				| "field" | "getField" -> self#write_expr_syntax_get_field args
 				| "setField" -> self#write_expr_syntax_set_field args
@@ -2595,25 +2600,22 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		(**
 			Writes `foreach` expression to output buffer (for `php.Syntax.foreach()`)
 		*)
-		method write_expr_syntax_foreach args =
-			match args with
-				| collection_expr :: { eexpr = TFunction fn } :: [] ->
-					let (key_name, value_name) = match fn.tf_args with
-						| ({ v_name = key_name }, _) :: ({ v_name = value_name }, _) :: [] -> (key_name, value_name)
-						| _ -> fail self#pos __POS__
-					and add_parentheses =
-						match collection_expr.eexpr with
-							| TLocal _ -> false
+		method write_expr_syntax_foreach body =
+			match body.eexpr with
+				| TBlock ({ eexpr = TCall (_, [collection]) } :: { eexpr = TVar (key, _) } :: { eexpr = TVar (value, _) } :: _ :: body_exprs) ->
+					let add_parentheses =
+						match collection.eexpr with
+							| TLocal _ | TField _ | TArray _ -> false
 							| _ -> true
 					in
 					self#write "foreach (";
 					if add_parentheses then self#write "(";
-					self#write_expr collection_expr;
+					self#write_expr collection;
 					if add_parentheses then self#write ")";
-					self#write (" as $" ^ key_name ^ " => $" ^ value_name ^ ") ");
-					self#write_as_block fn.tf_expr
+					self#write (" as $" ^ key.v_name ^ " => $" ^ value.v_name ^ ") ");
+					self#write_as_block ~unset_locals:true { body with eexpr = TBlock body_exprs };
 				| _ ->
-					ctx.error "php.Syntax.foreach() only accepts anonymous function declaration for second argument." self#pos
+					fail self#pos __POS__
 		(**
 			Writes TCall to output buffer
 		*)

+ 14 - 2
std/php/Syntax.hx

@@ -189,7 +189,19 @@ extern class Syntax {
         }
         ```
     **/
-    static function foreach<TCollection,TKey,TValue>( collection:TCollection, body:TKey->TValue->Void ) : Void;
+    static inline function foreach<TCollection,TKey,TValue>(collection:TCollection, body:TKey->TValue->Void) : Void {
+        while(Syntax.foreachCondition) {
+            Syntax.foreachCollection(collection);
+            var key:TKey = Syntax.foreachKey();
+            var value:TValue = Syntax.foreachValue();
+            Syntax.keepVar(key, value, key, value);
+            body(key, value);
+        }
+    }
+    static private var foreachCondition:Bool;
+    static private function foreachCollection<T>(collection:T):Void;
+    static private function foreachKey<T>():T;
+    static private function foreachValue<T>():T;
 
     /**
         Generates `new $className($arg1, ...$argN)`
@@ -261,7 +273,7 @@ extern class Syntax {
     /**
         Don't let compiler to optimize away local var passed to this method.
     **/
-    static function keepVar( localVar:Dynamic ) : Void;
+    static function keepVar( localVar:Rest<Dynamic> ) : Void;
 
     /**
         Adds `...` operator before `args`