Browse Source

[cs] Implement cs.Syntax.code() (#10051)

* Implement cs.Syntax.code()

* Use haxe.Rest instead of haxe.extern.Rest

* Fix Syntax.code example
Rudy Ges 4 years ago
parent
commit
cd359a2f04
3 changed files with 99 additions and 4 deletions
  1. 36 1
      src/generators/gencs.ml
  2. 56 0
      std/cs/Syntax.hx
  3. 7 3
      tests/unit/src/unit/TestSyntaxModule.hx

+ 36 - 1
src/generators/gencs.ml

@@ -1507,6 +1507,8 @@ let generate con =
 						write w " as ";
 						write w " as ";
 						write w (t_s e.etype);
 						write w (t_s e.etype);
 						write w " )";
 						write w " )";
+					| TCall({ eexpr = TField (_, FStatic ({ cl_path = ["cs"],"Syntax" }, { cf_name = meth })) }, args) ->
+						gen_syntax meth args e.epos
 					| TCall ({ eexpr = TIdent "__cs__" }, [ { eexpr = TConst(TString(s)) } ] ) ->
 					| TCall ({ eexpr = TIdent "__cs__" }, [ { eexpr = TConst(TString(s)) } ] ) ->
 						write w s
 						write w s
 					| TCall ({ eexpr = TIdent "__cs__" }, { eexpr = TConst(TString(s)) } :: tl ) ->
 					| TCall ({ eexpr = TIdent "__cs__" }, { eexpr = TConst(TString(s)) } :: tl ) ->
@@ -1789,7 +1791,40 @@ let generate con =
 					| TEnumParameter _ -> write w "[ enum parameter not supported ]"; if !strict_mode then die "" __LOC__
 					| TEnumParameter _ -> write w "[ enum parameter not supported ]"; if !strict_mode then die "" __LOC__
 					| TEnumIndex _ -> write w "[ enum index not supported ]"; if !strict_mode then die "" __LOC__
 					| TEnumIndex _ -> write w "[ enum index not supported ]"; if !strict_mode then die "" __LOC__
 					| TIdent s -> write w "[ ident not supported ]"; if !strict_mode then die "" __LOC__
 					| TIdent s -> write w "[ ident not supported ]"; if !strict_mode then die "" __LOC__
-			)
+				)
+			and gen_syntax meth args pos =
+				match meth, args with
+				| "code", code :: args ->
+					let code, code_pos =
+						match code.eexpr with
+						| TConst (TString s) -> s, code.epos
+						| _ -> abort "The `code` argument for cs.Syntax.code must be a string constant" code.epos
+					in
+					begin
+						let rec reveal_expr expr =
+							match expr.eexpr with
+								| TCast (e, _) | TMeta (_, e) -> reveal_expr e
+								| _ -> expr
+						in
+						let args = List.map
+							(fun arg ->
+								match (reveal_expr arg).eexpr with
+									| TIf _ | TBinop _ | TUnop _ -> { arg with eexpr = TParenthesis arg }
+									| _ -> arg
+							)
+							args
+						in
+						Codegen.interpolate_code gen.gcon code args (write w) (expr_s w) code_pos
+					end
+				| "plainCode", [code] ->
+					let code =
+						match code.eexpr with
+						| TConst (TString s) -> s
+						| _ -> abort "The `code` argument for cs.Syntax.plainCode must be a string constant" code.epos
+					in
+					write w (String.concat "\n" (ExtString.String.nsplit code "\r\n"))
+				| _ ->
+					abort (Printf.sprintf "Unknown cs.Syntax method `%s` with %d arguments" meth (List.length args)) pos
 			and do_call w e el =
 			and do_call w e el =
 				let params, el = extract_tparams [] el in
 				let params, el = extract_tparams [] el in
 				let params = List.rev params in
 				let params = List.rev params in

+ 56 - 0
std/cs/Syntax.hx

@@ -0,0 +1,56 @@
+/*
+ * Copyright (C)2005-2021 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package cs;
+
+import haxe.Rest;
+
+/**
+	Generate C# syntax not directly supported by Haxe.
+	Use only at low-level when specific target-specific code-generation is required.
+**/
+@:noClosure
+extern class Syntax {
+	/**
+		Inject `code` directly into generated source.
+
+		`code` must be a string constant.
+
+		Additional `args` are supported to provide code interpolation, for example:
+		```haxe
+		Syntax.code("System.Console.WriteLine({0} + {1})", "hi", 42);
+		```
+		will generate
+		```haxe
+		System.Console.WriteLine("hi" + 42);
+		```
+
+		Emits a compilation error if the count of `args` does not match the count of placeholders in `code`.
+	**/
+	static function code(code:String, args:Rest<Dynamic>):Dynamic;
+
+	/**
+		Inject `code` directly into generated source.
+		The same as `cs.Syntax.code` except this one does not provide code interpolation.
+	**/
+	static function plainCode(code:String):Dynamic;
+}

+ 7 - 3
tests/unit/src/unit/TestSyntaxModule.hx

@@ -6,17 +6,21 @@ import js.Syntax;
 import php.Syntax;
 import php.Syntax;
 #elseif python
 #elseif python
 import python.Syntax;
 import python.Syntax;
+#elseif cs
+import cs.Syntax;
 #end
 #end
 
 
 class TestSyntaxModule extends Test {
 class TestSyntaxModule extends Test {
-#if (php || js || python)
+#if (php || js || python || cs)
 	function testCode() {
 	function testCode() {
 		var i1 = 1;
 		var i1 = 1;
 		var i2 = 2;
 		var i2 = 2;
 		var result = Syntax.code('{0} + {1}', i1, i2);
 		var result = Syntax.code('{0} + {1}', i1, i2);
 		eq(i1 + i2, result);
 		eq(i1 + i2, result);
 	}
 	}
+#end
 
 
+#if (php || js || python)
 	function testField() {
 	function testField() {
 		var o = {field:'hello'};
 		var o = {field:'hello'};
 		var value = Syntax.field(o, 'field');
 		var value = Syntax.field(o, 'field');
@@ -49,7 +53,7 @@ class TestSyntaxModule extends Test {
 	#end
 	#end
 #end
 #end
 
 
-#if js
+#if (js || cs)
 	function testPlainCode() {
 	function testPlainCode() {
 		var s = Syntax.plainCode('"{0}"');
 		var s = Syntax.plainCode('"{0}"');
 		eq('{0}', s);
 		eq('{0}', s);
@@ -60,4 +64,4 @@ class TestSyntaxModule extends Test {
 private class Construct {
 private class Construct {
 	public var value:Int;
 	public var value:Int;
 	public function new(arg:Int) value = arg;
 	public function new(arg:Int) value = arg;
-}
+}