Bläddra i källkod

improve f-string

blueloveTH 2 år sedan
förälder
incheckning
2698040f52
2 ändrade filer med 88 tillägg och 26 borttagningar
  1. 63 26
      src/expr.cpp
  2. 25 0
      tests/25_rawstring.py

+ 63 - 26
src/expr.cpp

@@ -379,7 +379,7 @@ namespace pkpy{
             }
         }
         // name or name.name
-        std::regex pattern(R"(^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*){0,1}$)");
+        PK_LOCAL_STATIC const std::regex pattern(R"(^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*){0,1}$)");
         if(std::regex_match(expr.str(), pattern)){
             int dot = expr.index(".");
             if(dot < 0){
@@ -402,36 +402,73 @@ namespace pkpy{
 
     void FStringExpr::emit_(CodeEmitContext* ctx){
         VM* vm = ctx->vm;
-        PK_LOCAL_STATIC const std::regex pattern(R"(\{(.*?)\})");
-        std::cregex_iterator begin(src.begin(), src.end(), pattern);
-        std::cregex_iterator end;
-        int size = 0;
-        int i = 0;
-        for(auto it = begin; it != end; it++) {
-            std::cmatch m = *it;
-            if (i < m.position()) {
-                Str literal = src.substr(i, m.position() - i);
-                ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
-                size++;
-            }
-            Str expr = m[1].str();
-            int conon = expr.index(":");
-            if(conon >= 0){
-                _load_simple_expr(ctx, expr.substr(0, conon));
-                Str spec = expr.substr(conon+1);
-                ctx->emit_(OP_FORMAT_STRING, ctx->add_const(VAR(spec)), line);
+        int i = 0;              // left index
+        int j = 0;              // right index
+        int count = 0;          // how many string parts
+        bool flag = false;      // true if we are in a expression
+
+        while(j < src.size){
+            if(flag){
+                if(src[j] == '}'){
+                    // add expression
+                    Str expr = src.substr(i, j-i);
+                    int conon = expr.index(":");
+                    if(conon >= 0){
+                        _load_simple_expr(ctx, expr.substr(0, conon));
+                        Str spec = expr.substr(conon+1);
+                        ctx->emit_(OP_FORMAT_STRING, ctx->add_const(VAR(spec)), line);
+                    }else{
+                        _load_simple_expr(ctx, expr);
+                    }
+                    flag = false;
+                    count++;
+                }
             }else{
-                _load_simple_expr(ctx, expr);
+                if(src[j] == '{'){
+                    // look at next char
+                    if(j+1 < src.size && src[j+1] == '{'){
+                        // {{ -> {
+                        j++;
+                        ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR("{")), line);
+                        count++;
+                    }else{
+                        // { -> }
+                        flag = true;
+                        i = j+1;
+                    }
+                }else if(src[j] == '}'){
+                    // look at next char
+                    if(j+1 < src.size && src[j+1] == '}'){
+                        // }} -> }
+                        j++;
+                        ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR("}")), line);
+                        count++;
+                    }else{
+                        // } -> error
+                        // throw std::runtime_error("f-string: unexpected }");
+                        // just ignore
+                    }
+                }else{
+                    // literal
+                    i = j;
+                    while(j < src.size && src[j] != '{' && src[j] != '}') j++;
+                    Str literal = src.substr(i, j-i);
+                    ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
+                    count++;
+                    continue;   // skip j++
+                }
             }
-            size++;
-            i = (int)(m.position() + m.length());
+            j++;
         }
-        if (i < src.length()) {
-            Str literal = src.substr(i, src.length() - i);
+
+        if(flag){
+            // literal
+            Str literal = src.substr(i, src.size-i);
             ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
-            size++;
+            count++;
         }
-        ctx->emit_(OP_BUILD_STRING, size, line);
+
+        ctx->emit_(OP_BUILD_STRING, count, line);
     }
 
 

+ 25 - 0
tests/25_rawstring.py

@@ -27,6 +27,10 @@ s = f'''->->{s}<-<-
 
 assert s == '->->asdasd\nasds1321321321测试\\测试<-<-\n123\n'
 
+x = 1, 2, 3
+assert f"""
+a = {{{x[0]}, {x[1]}, {x[2]}}}""" == '\na = {1, 2, 3}'
+
 assert r''' ' ''' == " ' "
 
 a = 10
@@ -85,6 +89,27 @@ assert f'{"test"!r:*^10}' == "**'test'**"
 assert f'{"test"!r:*^11}' == "**'test'***"
 assert f'{12345!r:0>10}' == "0000012345"
 
+# test {{ and }}
+assert f'' == ''
+assert f'{{}}' == '{}'
+assert f'{{{{}}}}' == '{{}}'
+assert f'{{' == '{'
+assert f'}}' == '}'
+assert f'{{{{' == '{{'
+assert f'}}}}' == '}}'
+a = 123
+assert f'={a}' == '=123'
+assert f'{a}=' == '123='
+assert f'--{a}--' == '--123--'
+assert f'{{a}}' == '{a}'
+assert f'{{{a}}}' == '{123}'
+
+assert f'{{=}}{a}' == '{=}123'
+assert f'{a}{{=}}' == '123{=}'
+
+# assert f'}123' == '123'     # ignore '}'
+# assert f'{{{' == '{'        # ignore '{'
+
 class A:
     def __repr__(self):
         return 'A()'