Deduplicate string literal rendering, fix 4909

This commit is contained in:
Robert Hensing 2023-04-09 22:42:20 +02:00
parent 8f0ec323ea
commit 4e0804c920
7 changed files with 86 additions and 39 deletions

View file

@ -40,6 +40,7 @@ extern "C" {
#include "markdown.hh" #include "markdown.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
#include "progress-bar.hh" #include "progress-bar.hh"
#include "value/print.hh"
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
#define GC_INCLUDE_NEW #define GC_INCLUDE_NEW
@ -894,17 +895,6 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
} }
std::ostream & printStringValue(std::ostream & str, const char * string) {
str << "\"";
for (const char * i = string; *i; i++)
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
else if (*i == '\t') str << "\\t";
else str << *i;
str << "\"";
return str;
}
// FIXME: lot of cut&paste from Nix's eval.cc. // FIXME: lot of cut&paste from Nix's eval.cc.
@ -922,12 +912,14 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break; break;
case nBool: case nBool:
str << ANSI_CYAN << (v.boolean ? "true" : "false") << ANSI_NORMAL; str << ANSI_CYAN;
printLiteral(str, v.boolean);
str << ANSI_NORMAL;
break; break;
case nString: case nString:
str << ANSI_WARNING; str << ANSI_WARNING;
printStringValue(str, v.string.s); printLiteral(str, v.string.s);
str << ANSI_NORMAL; str << ANSI_NORMAL;
break; break;
@ -967,7 +959,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
if (isVarName(i.first)) if (isVarName(i.first))
str << i.first; str << i.first;
else else
printStringValue(str, i.first.c_str()); printLiteral(str, i.first);
str << " = "; str << " = ";
if (seen.count(i.second)) if (seen.count(i.second))
str << "«repeated»"; str << "«repeated»";

View file

@ -9,6 +9,7 @@
#include "filetransfer.hh" #include "filetransfer.hh"
#include "function-trace.hh" #include "function-trace.hh"
#include "profiles.hh" #include "profiles.hh"
#include "value/print.hh"
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
@ -104,18 +105,10 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << integer; str << integer;
break; break;
case tBool: case tBool:
str << (boolean ? "true" : "false"); printLiteral(str, boolean);
break; break;
case tString: case tString:
str << "\""; printLiteral(str, string.s);
for (const char * i = string.s; *i; i++)
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
else if (*i == '\t') str << "\\t";
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
else str << *i;
str << "\"";
break; break;
case tPath: case tPath:
str << path; // !!! escaping? str << path; // !!! escaping?

View file

@ -3,6 +3,7 @@
#include "eval.hh" #include "eval.hh"
#include "symbol-table.hh" #include "symbol-table.hh"
#include "util.hh" #include "util.hh"
#include "value/print.hh"
#include <cstdlib> #include <cstdlib>
@ -62,18 +63,6 @@ Pos::operator std::shared_ptr<AbstractPos>() const
/* Displaying abstract syntax trees. */ /* Displaying abstract syntax trees. */
static void showString(std::ostream & str, std::string_view s)
{
str << '"';
for (auto c : s)
if (c == '"' || c == '\\' || c == '$') str << "\\" << c;
else if (c == '\n') str << "\\n";
else if (c == '\r') str << "\\r";
else if (c == '\t') str << "\\t";
else str << c;
str << '"';
}
std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol) std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
{ {
std::string_view s = symbol; std::string_view s = symbol;
@ -85,7 +74,7 @@ std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
else { else {
char c = s[0]; char c = s[0];
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) { if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
showString(str, s); printLiteral(str, s);
return str; return str;
} }
for (auto c : s) for (auto c : s)
@ -93,7 +82,7 @@ std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
(c >= 'A' && c <= 'Z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || (c >= '0' && c <= '9') ||
c == '_' || c == '\'' || c == '-')) { c == '_' || c == '\'' || c == '-')) {
showString(str, s); printLiteral(str, s);
return str; return str;
} }
str << s; str << s;
@ -118,7 +107,7 @@ void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
{ {
showString(str, s); printLiteral(str, s);
} }
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const

View file

@ -0,0 +1,26 @@
#include "value/print.hh"
namespace nix {
std::ostream &
printLiteral(std::ostream & str, const std::string_view string) {
str << "\"";
for (auto i = string.begin(); i != string.end(); ++i) {
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
else if (*i == '\t') str << "\\t";
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
else str << *i;
}
str << "\"";
return str;
}
std::ostream &
printLiteral(std::ostream & str, bool boolean) {
str << (boolean ? "true" : "false");
return str;
}
}

View file

@ -0,0 +1,30 @@
#pragma once
/**
* @file
* @brief Common printing functions for the Nix language
*
* While most types come with their own methods for printing, they share some
* functions that are placed here.
*/
#include <iostream>
namespace nix {
/**
* Print a string as a Nix string literal.
*
* Quotes and fairly minimal escaping are added.
*
* @param s The logical string
*/
std::ostream & printLiteral(std::ostream & o, std::string_view s);
inline std::ostream & printLiteral(std::ostream & o, const char * s) {
return printLiteral(o, std::string_view(s));
}
inline std::ostream & printLiteral(std::ostream & o, const std::string & s) {
return printLiteral(o, std::string_view(s));
}
/** Print `true` or `false`. */
std::ostream & printLiteral(std::ostream & o, bool b);
}

View file

@ -313,6 +313,15 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
} }
/**
* Print a derivation string literal to an std::string.
*
* This syntax does not generalize to the expression language, which needs to
* escape `$`.
*
* @param res Where to print to
* @param s Which logical string to print
*/
static void printString(std::string & res, std::string_view s) static void printString(std::string & res, std::string_view s)
{ {
boost::container::small_vector<char, 64 * 1024> buffer; boost::container::small_vector<char, 64 * 1024> buffer;

View file

@ -79,6 +79,14 @@ testReplResponse '
"result: ${a}" "result: ${a}"
' "result: 2" ' "result: 2"
# check dollar escaping https://github.com/NixOS/nix/issues/4909
# note the escaped \,
# \\
# because the second argument is a regex
testReplResponse '
"$" + "{hi}"
' '"\\${hi}"'
testReplResponse ' testReplResponse '
drvPath drvPath
' '".*-simple.drv"' \ ' '".*-simple.drv"' \