potassco / clasp

⚙️ A conflict-driven nogood learning answer set solver

Home Page:https://potassco.org/clasp/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Json output with escaped quote

susuhahnml opened this issue · comments

When using the json output where the stable models have strings with escaped quotes the output is not as expected:

echo 'p("xyz\\"abc").' | clingo --outf=2
{
  "Solver": "clingo version 5.5.1",
  "Input": [
    "stdin"
  ],
  "Call": [
    {
      "Witnesses": [
        {
          "Value": [
            "p(\"xyz\"abc\")"
          ]
        }
      ]
    }
  ],
  "Result": "SATISFIABLE",
  "Models": {
    "Number": 1,
    "More": "no"
  },
  "Calls": 1,
  "Time": {
    "Total": 0.007,
    "Solve": 0.000,
    "Model": 0.000,
    "Unsat": 0.000,
    "CPU": 0.002
  }
}

The output gives: "p(\"xyz\"abc\")" wich can't distiguish the escaped quotes. I would expect "p(\"xyz\\\"abc\")".

Something like this should do the trick:

--- a/src/clasp_output.cpp
+++ b/src/clasp_output.cpp
@@ -532,9 +532,13 @@ void JsonOutput::printString(const char* v, const char* sep) {
 	uint32 n = 0;
 	buf[n++] = '"';
 	while (*v) {
-		if      (*v != '\\' && *v != '"')                       { buf[n++] = *v++; }
-		else if (*v == '"' || !strchr("\"\\/\b\f\n\r\t", v[1])) { buf[n++] = '\\'; buf[n++] = *v++; }
-		else                                                    { buf[n++] = v[0]; buf[n++] = v[1]; v += 2; }
+		if      (*v == '"' || *v == '\\') { buf[n++] = '\\'; buf[n++] = *v++; }
+		else if (*v == '\b')              { buf[n++] = '\\'; buf[n++] = 'b'; v++; }
+		else if (*v == '\f')              { buf[n++] = '\\'; buf[n++] = 'f'; v++; }
+		else if (*v == '\n')              { buf[n++] = '\\'; buf[n++] = 'n'; v++; }
+		else if (*v == '\r')              { buf[n++] = '\\'; buf[n++] = 'r'; v++; }
+		else if (*v == '\t')              { buf[n++] = '\\'; buf[n++] = 't'; v++; }
+		else                              { buf[n++] = *v++; }
 		if (n > BUF_SIZE - 2) { buf[n] = 0; printf("%s%s", sep, buf); n = 0; sep = ""; }
 	}
 	buf[n] = 0;

Here is also a variant with a loop. I am a bit curious whether the compiler unrolls it. It's probably not better than the above:

--- a/src/clasp_output.cpp
+++ b/src/clasp_output.cpp
@@ -532,9 +532,18 @@ void JsonOutput::printString(const char* v, const char* sep) {
 	uint32 n = 0;
 	buf[n++] = '"';
 	while (*v) {
-		if      (*v != '\\' && *v != '"')                       { buf[n++] = *v++; }
-		else if (*v == '"' || !strchr("\"\\/\b\f\n\r\t", v[1])) { buf[n++] = '\\'; buf[n++] = *v++; }
-		else                                                    { buf[n++] = v[0]; buf[n++] = v[1]; v += 2; }
+		for (const char *c = "\"\"\\\\\bb\ff\nn\rr\tt";; c += 2) {
+			if (!*c) {
+				buf[n++] = *v++;
+				break;
+			}
+			if (*c == *v) {
+				buf[n++] = '\\';
+				buf[n++] = *(c+1);
+				++v;
+				break;
+			}
+		}
 		if (n > BUF_SIZE - 2) { buf[n] = 0; printf("%s%s", sep, buf); n = 0; sep = ""; }
 	}
 	buf[n] = 0;

With this you can run echo 'p("xyz\\"abc").' | clingo --outf=2 to obtain

{
  "Solver": "clingo version 5.5.2",
  "Input": [
    "stdin"
  ],
  "Call": [
    {
      "Witnesses": [
        {
          "Value": [
            "p(\"xyz\\\"abc\")"
          ]
        }
      ]
    }
  ],
  "Result": "SATISFIABLE",
  "Models": {
    "Number": 1,
    "More": "no"
  },
  "Calls": 1,
  "Time": {
    "Total": 0.004,
    "Solve": 0.000,
    "Model": 0.000,
    "Unsat": 0.000,
    "CPU": 0.004
  }
}

The values should then be parsable by clingo's term parser again. Also note that clingo never outputs a symbol string with a newline in it. It will always be "\\n" instead.

This looks very nice! How should we proceed to integrate it into clingo so that it can be used in other projects (clingraph)?

This looks very nice! How should we proceed to integrate it into clingo so that it can be used in other projects (clingraph)?

Let's see what Benny says. He usually replies on weekends. Maybe it is too much code duplication for him and he rather wants to have a loop. 😄 I also hope that I am not missing anything.

@rkaminsk Thanks for the code. Can‘t look at it this weekend but I try to do so early next week.

@rkaminsk Used your loop approach but with hiding the loop in strchr.

@rkaminsk Used your loop approach but with hiding the loop in strchr.

Looks good!

Thank you! :)