From: 星外之神 Date: Fri, 28 Oct 2022 15:47:56 +0000 (+0800) Subject: vala: Add support for verbatim template string X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3c3ee793b499be959b3ed7f1f74014713382a438;p=thirdparty%2Fvala.git vala: Add support for verbatim template string Fixes https://gitlab.gnome.org/GNOME/vala/issues/1373 --- diff --git a/tests/Makefile.am b/tests/Makefile.am index 10ff6183f..b93d4963c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -908,6 +908,8 @@ TESTS = \ scanner/string-escape-x-digit-length.test \ scanner/string-escape-x-empty.test \ scanner/string-escape-x.vala \ + scanner/string-template.vala \ + scanner/string-verbatim-template.vala \ parser/argument-list-incomplete.test \ parser/array-creation-invalid.test \ parser/array-length.vala \ @@ -1006,6 +1008,7 @@ TESTS = \ parser/using-invalid-namespace.test \ parser/var-type-dynamic.vala \ parser/var-type-nullable.vala \ + parser/verbatim-template.vala \ parser/with-embedded.vala \ parser/with-empty.vala \ parser/with-invalid-declaration.test \ @@ -1403,6 +1406,7 @@ TESTS = \ genie/struct-after-class.gs \ genie/try-except-finally.gs \ genie/typeof.gs \ + genie/verbatim-template.gs \ genie/while.gs \ glib/conditional-glib-api.vala \ bindings/gio/memoryoutputstream.vala \ diff --git a/tests/genie/literal-template-string.c-expected b/tests/genie/literal-template-string.c-expected index 9dad5e432..0333b8546 100644 --- a/tests/genie/literal-template-string.c-expected +++ b/tests/genie/literal-template-string.c-expected @@ -5,15 +5,36 @@ #include #include +#if !defined(VALA_EXTERN) +#if defined(_MSC_VER) +#define VALA_EXTERN __declspec(dllexport) extern +#elif __GNUC__ >= 4 +#define VALA_EXTERN __attribute__((visibility("default"))) extern +#else +#define VALA_EXTERN extern +#endif +#endif + #define _g_free0(var) (var = (g_free (var), NULL)) #define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); #define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; } #define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; } #define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); +VALA_EXTERN gchar* m (void); static void _vala_main (gchar** args, gint args_length1); +gchar* +m (void) +{ + gchar* _tmp0_; + gchar* result; + _tmp0_ = g_strdup ("foo"); + result = _tmp0_; + return result; +} + static const gchar* string_to_string (const gchar* self) { @@ -27,28 +48,35 @@ static void _vala_main (gchar** args, gint args_length1) { - gchar* a = NULL; + gchar* _result_ = NULL; gchar* _tmp0_; - gint b = 0; - gchar* c = NULL; const gchar* _tmp1_; + gint i = 0; gchar* _tmp2_; gchar* _tmp3_; gchar* _tmp4_; gchar* _tmp5_; - _tmp0_ = g_strdup ("test"); - a = _tmp0_; - b = 100; - _tmp1_ = string_to_string (a); - _tmp2_ = g_strdup_printf ("%i", b); + const gchar* _tmp6_; + gchar* _tmp7_; + const gchar* _tmp8_; + _tmp0_ = g_strdup (""); + _result_ = _tmp0_; + _tmp1_ = _result_; + _vala_assert (g_strcmp0 (_tmp1_, "") == 0, "result == \"\""); + i = 42; + _tmp2_ = g_strdup_printf ("%i", i); _tmp3_ = _tmp2_; - _tmp4_ = g_strconcat (_tmp1_, _tmp3_, NULL); + _tmp4_ = m (); _tmp5_ = _tmp4_; + _tmp6_ = string_to_string (_tmp5_); + _tmp7_ = g_strconcat ("i=", _tmp3_, " m=", _tmp6_, " ", "$", NULL); + _g_free0 (_result_); + _result_ = _tmp7_; + _g_free0 (_tmp5_); _g_free0 (_tmp3_); - c = _tmp5_; - _vala_assert (g_strcmp0 (c, "test100") == 0, "c == \"test100\""); - _g_free0 (c); - _g_free0 (a); + _tmp8_ = _result_; + _vala_assert (g_strcmp0 (_tmp8_, "i=42 m=foo $") == 0, "result == \"i=42 m=foo $\""); + _g_free0 (_result_); } int diff --git a/tests/genie/literal-template-string.gs b/tests/genie/literal-template-string.gs index 87e75ae92..8c7dc617d 100644 --- a/tests/genie/literal-template-string.gs +++ b/tests/genie/literal-template-string.gs @@ -1,5 +1,10 @@ +def m():string + return "foo" + init - var a = "test" - var b = 100 - var c = @"$( a )$b" - assert( c == "test100" ) + result:string = @"" + assert( result == "" ) + + i:int = 42 + result = @"i=$i m=$( m() ) $$" + assert( result == "i=42 m=foo $" ) diff --git a/tests/genie/verbatim-template.c-expected b/tests/genie/verbatim-template.c-expected new file mode 100644 index 000000000..97531c04f --- /dev/null +++ b/tests/genie/verbatim-template.c-expected @@ -0,0 +1,103 @@ +/* genie_verbatim_template.c generated by valac, the Vala compiler + * generated from genie_verbatim_template.gs, do not modify */ + +#include +#include +#include + +#if !defined(VALA_EXTERN) +#if defined(_MSC_VER) +#define VALA_EXTERN __declspec(dllexport) extern +#elif __GNUC__ >= 4 +#define VALA_EXTERN __attribute__((visibility("default"))) extern +#else +#define VALA_EXTERN extern +#endif +#endif + +#define _g_free0(var) (var = (g_free (var), NULL)) +#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); +#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; } +#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; } +#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); + +VALA_EXTERN gchar* m (void); +static void _vala_main (gchar** args, + gint args_length1); + +gchar* +m (void) +{ + gchar* _tmp0_; + gchar* result; + _tmp0_ = g_strdup ("foo"); + result = _tmp0_; + return result; +} + +static const gchar* +string_to_string (const gchar* self) +{ + const gchar* result; + g_return_val_if_fail (self != NULL, NULL); + result = self; + return result; +} + +static void +_vala_main (gchar** args, + gint args_length1) +{ + gchar* _result_ = NULL; + gchar* _tmp0_; + const gchar* _tmp1_; + gint i = 0; + gchar* _tmp2_; + gchar* _tmp3_; + gchar* _tmp4_; + gchar* _tmp5_; + const gchar* _tmp6_; + gchar* _tmp7_; + const gchar* _tmp8_; + gint a = 0; + gchar* _tmp9_; + gchar* _tmp10_; + gchar* _tmp11_; + const gchar* _tmp12_; + _tmp0_ = g_strdup (""); + _result_ = _tmp0_; + _tmp1_ = _result_; + _vala_assert (g_strcmp0 (_tmp1_, "") == 0, "result == \"\""); + i = 42; + _tmp2_ = g_strdup_printf ("%i", i); + _tmp3_ = _tmp2_; + _tmp4_ = m (); + _tmp5_ = _tmp4_; + _tmp6_ = string_to_string (_tmp5_); + _tmp7_ = g_strconcat ("i=", _tmp3_, " \nm=", _tmp6_, " \n", "$", NULL); + _g_free0 (_result_); + _result_ = _tmp7_; + _g_free0 (_tmp5_); + _g_free0 (_tmp3_); + _tmp8_ = _result_; + _vala_assert (g_strcmp0 (_tmp8_, "i=42 \nm=foo \n$") == 0, "result == \"i=42 \\nm=foo \\n$\""); + a = 4711; + _tmp9_ = g_strdup_printf ("%i", a); + _tmp10_ = _tmp9_; + _tmp11_ = g_strconcat ("\"", _tmp10_, "\"", "\"", NULL); + _g_free0 (_result_); + _result_ = _tmp11_; + _g_free0 (_tmp10_); + _tmp12_ = _result_; + _vala_assert (g_strcmp0 (_tmp12_, "\"4711\"\"") == 0, "result == \"\\\"4711\\\"\\\"\""); + _g_free0 (_result_); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (argv, argc); + return 0; +} + diff --git a/tests/genie/verbatim-template.gs b/tests/genie/verbatim-template.gs new file mode 100644 index 000000000..92f9a5c80 --- /dev/null +++ b/tests/genie/verbatim-template.gs @@ -0,0 +1,16 @@ +def m():string + return "foo" + +init + result:string = @"""""" + assert( result == "" ) + + i:int = 42 + result = @"""i=$i +m=$( m() ) +$$""" + assert( result == "i=42 \nm=foo \n$" ) + + a:int = 4711 + result = @""""$a""""" + assert( result == "\"4711\"\"" ) diff --git a/tests/parser/template.c-expected b/tests/parser/template.c-expected index 933aa14f5..093acfe15 100644 --- a/tests/parser/template.c-expected +++ b/tests/parser/template.c-expected @@ -46,27 +46,30 @@ _vala_main (void) { gchar* _result_ = NULL; gchar* _tmp0_; + const gchar* _tmp1_; gint i = 0; - gchar* _tmp1_; gchar* _tmp2_; - const gchar* _tmp3_; + gchar* _tmp3_; const gchar* _tmp4_; - gchar* _tmp5_; - const gchar* _tmp6_; + const gchar* _tmp5_; + gchar* _tmp6_; + const gchar* _tmp7_; _tmp0_ = g_strdup (""); _g_free0 (_result_); _result_ = _tmp0_; + _tmp1_ = _result_; + _vala_assert (g_strcmp0 (_tmp1_, "") == 0, "result == \"\""); i = 42; - _tmp1_ = g_strdup_printf ("%i", i); - _tmp2_ = _tmp1_; - _tmp3_ = m (); - _tmp4_ = string_to_string (_tmp3_); - _tmp5_ = g_strconcat ("i=", _tmp2_, " m=", _tmp4_, " ", "$", NULL); + _tmp2_ = g_strdup_printf ("%i", i); + _tmp3_ = _tmp2_; + _tmp4_ = m (); + _tmp5_ = string_to_string (_tmp4_); + _tmp6_ = g_strconcat ("i=", _tmp3_, " m=", _tmp5_, " ", "$", NULL); _g_free0 (_result_); - _result_ = _tmp5_; - _g_free0 (_tmp2_); - _tmp6_ = _result_; - _vala_assert (g_strcmp0 (_tmp6_, "i=42 m=foo $") == 0, "result == \"i=42 m=foo $\""); + _result_ = _tmp6_; + _g_free0 (_tmp3_); + _tmp7_ = _result_; + _vala_assert (g_strcmp0 (_tmp7_, "i=42 m=foo $") == 0, "result == \"i=42 m=foo $\""); _g_free0 (_result_); } diff --git a/tests/parser/template.vala b/tests/parser/template.vala index b4a073676..f1fd76cfe 100644 --- a/tests/parser/template.vala +++ b/tests/parser/template.vala @@ -5,6 +5,7 @@ unowned string m () { void main () { string result; result = @""; + assert (result == ""); int i = 42; result = @"i=$i m=$(m ()) $$"; diff --git a/tests/parser/verbatim-template.c-expected b/tests/parser/verbatim-template.c-expected new file mode 100644 index 000000000..108cae7c5 --- /dev/null +++ b/tests/parser/verbatim-template.c-expected @@ -0,0 +1,83 @@ +/* parser_verbatim_template.c generated by valac, the Vala compiler + * generated from parser_verbatim_template.vala, do not modify */ + +#include +#include +#include + +#if !defined(VALA_EXTERN) +#if defined(_MSC_VER) +#define VALA_EXTERN __declspec(dllexport) extern +#elif __GNUC__ >= 4 +#define VALA_EXTERN __attribute__((visibility("default"))) extern +#else +#define VALA_EXTERN extern +#endif +#endif + +#define _g_free0(var) (var = (g_free (var), NULL)) +#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); +#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; } +#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; } +#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); + +VALA_EXTERN const gchar* m (void); +static void _vala_main (void); + +const gchar* +m (void) +{ + const gchar* result; + result = "foo"; + return result; +} + +static const gchar* +string_to_string (const gchar* self) +{ + const gchar* result; + g_return_val_if_fail (self != NULL, NULL); + result = self; + return result; +} + +static void +_vala_main (void) +{ + gchar* _result_ = NULL; + gchar* _tmp0_; + const gchar* _tmp1_; + gint i = 0; + gchar* _tmp2_; + gchar* _tmp3_; + const gchar* _tmp4_; + const gchar* _tmp5_; + gchar* _tmp6_; + const gchar* _tmp7_; + _tmp0_ = g_strdup (""); + _g_free0 (_result_); + _result_ = _tmp0_; + _tmp1_ = _result_; + _vala_assert (g_strcmp0 (_tmp1_, "") == 0, "result == \"\""); + i = 42; + _tmp2_ = g_strdup_printf ("%i", i); + _tmp3_ = _tmp2_; + _tmp4_ = m (); + _tmp5_ = string_to_string (_tmp4_); + _tmp6_ = g_strconcat ("i=", _tmp3_, " \nm=", _tmp5_, " \n", "$", NULL); + _g_free0 (_result_); + _result_ = _tmp6_; + _g_free0 (_tmp3_); + _tmp7_ = _result_; + _vala_assert (g_strcmp0 (_tmp7_, "i=42 \nm=foo \n$") == 0, "result == \"i=42 \\nm=foo \\n$\""); + _g_free0 (_result_); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/parser/verbatim-template.vala b/tests/parser/verbatim-template.vala new file mode 100644 index 000000000..4a3db1fa5 --- /dev/null +++ b/tests/parser/verbatim-template.vala @@ -0,0 +1,15 @@ +unowned string m () { + return "foo"; +} + +void main () { + string result; + result = @""""""; + assert (result == ""); + + int i = 42; + result = @"""i=$i +m=$(m ()) +$$"""; + assert (result == "i=42 \nm=foo \n$"); +} diff --git a/tests/scanner/string-template.c-expected b/tests/scanner/string-template.c-expected new file mode 100644 index 000000000..eb6821b76 --- /dev/null +++ b/tests/scanner/string-template.c-expected @@ -0,0 +1,57 @@ +/* scanner_string_template.c generated by valac, the Vala compiler + * generated from scanner_string_template.vala, do not modify */ + +#include +#include +#include + +#define _g_free0(var) (var = (g_free (var), NULL)) + +static void _vala_main (void); + +static const gchar* +string_to_string (const gchar* self) +{ + const gchar* result; + g_return_val_if_fail (self != NULL, NULL); + result = self; + return result; +} + +static void +_vala_main (void) +{ + gchar* foo = NULL; + gchar* _tmp0_; + gchar* bar = NULL; + gchar* _tmp1_; + gchar* manam = NULL; + const gchar* _tmp2_; + const gchar* _tmp3_; + gchar* _tmp4_; + gchar* minim = NULL; + gchar* _tmp5_; + _tmp0_ = g_strdup ("Hello"); + foo = _tmp0_; + _tmp1_ = g_strdup ("world"); + bar = _tmp1_; + _tmp2_ = string_to_string (foo); + _tmp3_ = string_to_string (bar); + _tmp4_ = g_strconcat (_tmp2_, " ", _tmp3_, "!", NULL); + manam = _tmp4_; + _tmp5_ = g_strdup (""); + minim = _tmp5_; + _g_free0 (minim); + _g_free0 (manam); + _g_free0 (bar); + _g_free0 (foo); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/scanner/string-template.vala b/tests/scanner/string-template.vala new file mode 100644 index 000000000..3d4a00d4f --- /dev/null +++ b/tests/scanner/string-template.vala @@ -0,0 +1,8 @@ +void main () { + var foo = "Hello"; + var bar = "world"; + + var manam = @"$foo $bar!"; + var minim = @""; +} + diff --git a/tests/scanner/string-verbatim-template.c-expected b/tests/scanner/string-verbatim-template.c-expected new file mode 100644 index 000000000..6f7b0d214 --- /dev/null +++ b/tests/scanner/string-verbatim-template.c-expected @@ -0,0 +1,57 @@ +/* scanner_string_verbatim_template.c generated by valac, the Vala compiler + * generated from scanner_string_verbatim_template.vala, do not modify */ + +#include +#include +#include + +#define _g_free0(var) (var = (g_free (var), NULL)) + +static void _vala_main (void); + +static const gchar* +string_to_string (const gchar* self) +{ + const gchar* result; + g_return_val_if_fail (self != NULL, NULL); + result = self; + return result; +} + +static void +_vala_main (void) +{ + gchar* foo = NULL; + gchar* _tmp0_; + gchar* bar = NULL; + gchar* _tmp1_; + gchar* manam = NULL; + const gchar* _tmp2_; + const gchar* _tmp3_; + gchar* _tmp4_; + gchar* minim = NULL; + gchar* _tmp5_; + _tmp0_ = g_strdup ("Hello"); + foo = _tmp0_; + _tmp1_ = g_strdup ("world"); + bar = _tmp1_; + _tmp2_ = string_to_string (foo); + _tmp3_ = string_to_string (bar); + _tmp4_ = g_strconcat (_tmp2_, " \n", _tmp3_, "!", NULL); + manam = _tmp4_; + _tmp5_ = g_strdup (""); + minim = _tmp5_; + _g_free0 (minim); + _g_free0 (manam); + _g_free0 (bar); + _g_free0 (foo); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/scanner/string-verbatim-template.vala b/tests/scanner/string-verbatim-template.vala new file mode 100644 index 000000000..32ff85755 --- /dev/null +++ b/tests/scanner/string-verbatim-template.vala @@ -0,0 +1,8 @@ +void main () { + var foo = "Hello"; + var bar = "world"; + + var manam = @"""$foo +$bar!"""; + var minim = @""""""; +} diff --git a/vala/valagenieparser.vala b/vala/valagenieparser.vala index 7c7c48309..9781cd339 100644 --- a/vala/valagenieparser.vala +++ b/vala/valagenieparser.vala @@ -394,6 +394,11 @@ public class Vala.Genie.Parser : CodeVisitor { string raw_string = get_last_string (); string escaped_string = raw_string.substring (3, raw_string.length - 6).escape (""); return new StringLiteral ("\"%s\"".printf (escaped_string), get_src (begin)); + case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: + next (); + string raw_string = get_last_string (); + string escaped_string = raw_string.escape (""); + return new StringLiteral ("\"%s\"".printf (escaped_string), get_src (begin)); case TokenType.NULL: next (); return new NullLiteral (get_src (begin)); @@ -701,6 +706,7 @@ public class Vala.Genie.Parser : CodeVisitor { case TokenType.STRING_LITERAL: case TokenType.TEMPLATE_STRING_LITERAL: case TokenType.VERBATIM_STRING_LITERAL: + case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: case TokenType.NULL: expr = parse_literal (); break; @@ -1285,6 +1291,7 @@ public class Vala.Genie.Parser : CodeVisitor { case TokenType.STRING_LITERAL: case TokenType.TEMPLATE_STRING_LITERAL: case TokenType.VERBATIM_STRING_LITERAL: + case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: case TokenType.NULL: case TokenType.SELF: case TokenType.SUPER: diff --git a/vala/valageniescanner.vala b/vala/valageniescanner.vala index ef54ae7d3..286992206 100644 --- a/vala/valageniescanner.vala +++ b/vala/valageniescanner.vala @@ -67,7 +67,8 @@ public class Vala.Genie.Scanner { BRACKET, REGEX_LITERAL, TEMPLATE, - TEMPLATE_PART + TEMPLATE_PART, + VERBATIM_TEMPLATE } public Scanner (SourceFile source_file) { @@ -97,6 +98,10 @@ public class Vala.Genie.Scanner { return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.TEMPLATE); } + bool in_verbatim_template () { + return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.VERBATIM_TEMPLATE); + } + bool in_template_part () { return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.TEMPLATE_PART); } @@ -695,6 +700,7 @@ public class Vala.Genie.Scanner { public TokenType read_template_token (out SourceLocation token_begin, out SourceLocation token_end) { + bool is_verbatim = in_verbatim_template (); TokenType type; char* begin = current; token_begin = SourceLocation (begin, line, column); @@ -706,9 +712,22 @@ public class Vala.Genie.Scanner { } else { switch (current[0]) { case '"': - type = TokenType.CLOSE_TEMPLATE; - current++; - state_stack.length--; + if (is_verbatim) { + if (current < end -2 && current[1] == '"' && current[2] == '"' && current[3] != '"') { + type = TokenType.CLOSE_TEMPLATE; + current += 3; + state_stack.length--; + } else { + type = TokenType.VERBATIM_TEMPLATE_STRING_LITERAL; + current++; + token_length_in_chars++; + state_stack += State.TEMPLATE_PART; + } + } else { + type = TokenType.CLOSE_TEMPLATE; + current++; + state_stack.length--; + } break; case '$': token_begin.pos++; // $ is not part of following token @@ -727,7 +746,7 @@ public class Vala.Genie.Scanner { state_stack += State.PARENS; return read_token (out token_begin, out token_end); } else if (current[0] == '$') { - type = TokenType.TEMPLATE_STRING_LITERAL; + type = is_verbatim ? TokenType.VERBATIM_TEMPLATE_STRING_LITERAL : TokenType.TEMPLATE_STRING_LITERAL; current++; state_stack += State.TEMPLATE_PART; } else { @@ -736,10 +755,10 @@ public class Vala.Genie.Scanner { } break; default: - type = TokenType.TEMPLATE_STRING_LITERAL; + type = is_verbatim ? TokenType.VERBATIM_TEMPLATE_STRING_LITERAL : TokenType.TEMPLATE_STRING_LITERAL; token_length_in_chars = 0; while (current < end && current[0] != '"' && current[0] != '$') { - if (current[0] == '\\') { + if (current[0] == '\\' && !is_verbatim) { current++; token_length_in_chars++; if (current >= end) { @@ -842,7 +861,7 @@ public class Vala.Genie.Scanner { return TokenType.EOF; } - if (in_template ()) { + if (in_template () || in_verbatim_template ()) { return read_template_token (out token_begin, out token_end); } else if (in_template_part ()) { state_stack.length--; @@ -960,9 +979,15 @@ public class Vala.Genie.Scanner { type = get_identifier_or_keyword (begin, len); } else if (current[0] == '@') { if (current < end - 1 && current[1] == '"') { + current += 1; + if (current < end - 5 && current[1] == '"' && current[2] == '"') { + current += 3; + state_stack += State.VERBATIM_TEMPLATE; + } else { + current += 1; + state_stack += State.TEMPLATE; + } type = TokenType.OPEN_TEMPLATE; - current += 2; - state_stack += State.TEMPLATE; } else { token_begin.pos++; // @ is not part of the identifier current++; @@ -1053,7 +1078,7 @@ public class Vala.Genie.Scanner { if (state_stack.length > 0) { state_stack.length--; } - if (in_template ()) { + if (in_template () || in_verbatim_template ()) { type = TokenType.COMMA; } break; diff --git a/vala/valagenietokentype.vala b/vala/valagenietokentype.vala index 08bef53fd..fc46724b4 100644 --- a/vala/valagenietokentype.vala +++ b/vala/valagenietokentype.vala @@ -163,6 +163,7 @@ public enum Vala.Genie.TokenType { USES, VAR, VERBATIM_STRING_LITERAL, + VERBATIM_TEMPLATE_STRING_LITERAL, VIRTUAL, VOID, VOLATILE, @@ -311,6 +312,7 @@ public enum Vala.Genie.TokenType { case USES: return "`uses'"; case VAR: return "`var'"; case VERBATIM_STRING_LITERAL: return "verbatim string literal"; + case VERBATIM_TEMPLATE_STRING_LITERAL: return "verbatim template string literal"; case VIRTUAL: return "`virtual'"; case VOID: return "`void'"; case VOLATILE: return "`volatile'"; diff --git a/vala/valaparser.vala b/vala/valaparser.vala index f703e9c86..23b1e7db4 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -384,6 +384,11 @@ public class Vala.Parser : CodeVisitor { string raw_string = get_last_string (); string escaped_string = raw_string.substring (3, raw_string.length - 6).escape (""); return new StringLiteral ("\"%s\"".printf (escaped_string), get_src (begin)); + case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: + next (); + string raw_string = get_last_string (); + string escaped_string = raw_string.escape (""); + return new StringLiteral ("\"%s\"".printf (escaped_string), get_src (begin)); case TokenType.NULL: next (); return new NullLiteral (get_src (begin)); @@ -719,6 +724,7 @@ public class Vala.Parser : CodeVisitor { case TokenType.REGEX_LITERAL: case TokenType.TEMPLATE_STRING_LITERAL: case TokenType.VERBATIM_STRING_LITERAL: + case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: case TokenType.NULL: expr = parse_literal (); break; @@ -1269,6 +1275,7 @@ public class Vala.Parser : CodeVisitor { case TokenType.STRING_LITERAL: case TokenType.TEMPLATE_STRING_LITERAL: case TokenType.VERBATIM_STRING_LITERAL: + case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: case TokenType.REGEX_LITERAL: case TokenType.NULL: case TokenType.THIS: diff --git a/vala/valascanner.vala b/vala/valascanner.vala index daeb97feb..13a4698b6 100644 --- a/vala/valascanner.vala +++ b/vala/valascanner.vala @@ -54,7 +54,8 @@ public class Vala.Scanner { BRACKET, TEMPLATE, TEMPLATE_PART, - REGEX_LITERAL + REGEX_LITERAL, + VERBATIM_TEMPLATE } public Scanner (SourceFile source_file) { @@ -82,6 +83,10 @@ public class Vala.Scanner { return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.TEMPLATE); } + bool in_verbatim_template () { + return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.VERBATIM_TEMPLATE); + } + bool in_template_part () { return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.TEMPLATE_PART); } @@ -686,6 +691,7 @@ public class Vala.Scanner { } public TokenType read_template_token (out SourceLocation token_begin, out SourceLocation token_end) { + bool is_verbatim = in_verbatim_template (); TokenType type; char* begin = current; token_begin = SourceLocation (begin, line, column); @@ -697,9 +703,22 @@ public class Vala.Scanner { } else { switch (current[0]) { case '"': - type = TokenType.CLOSE_TEMPLATE; - current++; - state_stack.length--; + if (is_verbatim) { + if (current < end -2 && current[1] == '"' && current[2] == '"' && current[3] != '"') { + type = TokenType.CLOSE_TEMPLATE; + current += 3; + state_stack.length--; + } else { + type = TokenType.VERBATIM_TEMPLATE_STRING_LITERAL; + current++; + token_length_in_chars++; + state_stack += State.TEMPLATE_PART; + } + } else { + type = TokenType.CLOSE_TEMPLATE; + current++; + state_stack.length--; + } break; case '$': token_begin.pos++; // $ is not part of following token @@ -718,7 +737,7 @@ public class Vala.Scanner { state_stack += State.PARENS; return read_token (out token_begin, out token_end); } else if (current[0] == '$') { - type = TokenType.TEMPLATE_STRING_LITERAL; + type = is_verbatim ? TokenType.VERBATIM_TEMPLATE_STRING_LITERAL : TokenType.TEMPLATE_STRING_LITERAL; current++; state_stack += State.TEMPLATE_PART; } else { @@ -727,10 +746,10 @@ public class Vala.Scanner { } break; default: - type = TokenType.TEMPLATE_STRING_LITERAL; + type = is_verbatim ? TokenType.VERBATIM_TEMPLATE_STRING_LITERAL : TokenType.TEMPLATE_STRING_LITERAL; token_length_in_chars = 0; while (current < end && current[0] != '"' && current[0] != '$') { - if (current[0] == '\\') { + if (current[0] == '\\' && !is_verbatim) { current++; token_length_in_chars++; if (current >= end) { @@ -829,7 +848,7 @@ public class Vala.Scanner { } public TokenType read_token (out SourceLocation token_begin, out SourceLocation token_end) { - if (in_template ()) { + if (in_template () || in_verbatim_template ()) { return read_template_token (out token_begin, out token_end); } else if (in_template_part ()) { state_stack.length--; @@ -861,9 +880,15 @@ public class Vala.Scanner { type = get_identifier_or_keyword (begin, len); } else if (current[0] == '@') { if (current < end - 1 && current[1] == '"') { + current += 1; + if (current < end - 5 && current[1] == '"' && current[2] == '"') { + current += 3; + state_stack += State.VERBATIM_TEMPLATE; + } else { + current += 1; + state_stack += State.TEMPLATE; + } type = TokenType.OPEN_TEMPLATE; - current += 2; - state_stack += State.TEMPLATE; } else { token_begin.pos++; // @ is not part of the identifier current++; @@ -901,7 +926,7 @@ public class Vala.Scanner { if (state_stack.length > 0) { state_stack.length--; } - if (in_template ()) { + if (in_template () || in_verbatim_template ()) { type = TokenType.COMMA; } break; diff --git a/vala/valatokentype.vala b/vala/valatokentype.vala index 2c64ec1b0..6097beaff 100644 --- a/vala/valatokentype.vala +++ b/vala/valatokentype.vala @@ -149,6 +149,7 @@ public enum Vala.TokenType { USING, VAR, VERBATIM_STRING_LITERAL, + VERBATIM_TEMPLATE_STRING_LITERAL, VIRTUAL, VOID, VOLATILE, @@ -284,6 +285,7 @@ public enum Vala.TokenType { case USING: return "`using'"; case VAR: return "`var'"; case VERBATIM_STRING_LITERAL: return "verbatim string literal"; + case VERBATIM_TEMPLATE_STRING_LITERAL: return "verbatim template string literal"; case VIRTUAL: return "`virtual'"; case VOID: return "`void'"; case VOLATILE: return "`volatile'";