Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Json output with escaped quote #75

Closed
susuhahnml opened this issue Mar 9, 2022 · 7 comments
Closed

Json output with escaped quote #75

susuhahnml opened this issue Mar 9, 2022 · 7 comments

Comments

@susuhahnml
Copy link

susuhahnml commented Mar 9, 2022

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\")".

@susuhahnml susuhahnml changed the title Json output with scaped quote Json output with escaped quote Mar 10, 2022
@rkaminsk
Copy link
Member

rkaminsk commented Mar 10, 2022

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.

@susuhahnml
Copy link
Author

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

@rkaminsk
Copy link
Member

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.

@BenKaufmann
Copy link
Contributor

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

BenKaufmann pushed a commit that referenced this issue Mar 14, 2022
@BenKaufmann
Copy link
Contributor

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

@rkaminsk
Copy link
Member

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

Looks good!

@susuhahnml
Copy link
Author

Thank you! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants