1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-02 08:59:28 +02:00

Line breaks allowed in strings (or lists) in $ lines; \n means n ...

... because we must match how xgettext parses strings, and unlike XLisp, it
does it strictly according to this reference:

http://www.lispworks.com/documentation/lw70/CLHS/Body/02_de.htm
This commit is contained in:
Paul Licameli 2018-03-02 13:57:56 -05:00
parent ad4843621b
commit ff3a869e83
2 changed files with 75 additions and 49 deletions

View File

@ -1471,26 +1471,27 @@ wxString NyquistEffect::EscapeString(const wxString & inStr)
wxArrayString NyquistEffect::ParseChoice(const wxString & text) wxArrayString NyquistEffect::ParseChoice(const wxString & text)
{ {
wxArrayString choices;
if (text[0] == wxT('(')) { if (text[0] == wxT('(')) {
// New style: expecting a Lisp-like list of strings // New style: expecting a Lisp-like list of strings
Tokenize(text, choices, 1, 1); Tokenizer tzer;
tzer.Tokenize(text, true, 1, 1);
auto &choices = tzer.tokens;
for (auto &choice : choices) for (auto &choice : choices)
choice = UnQuote(choice); choice = UnQuote(choice);
return choices;
} }
else { else {
// Old style: expecting a comma-separated list of // Old style: expecting a comma-separated list of
// un-internationalized names, ignoring leading and trailing spaces // un-internationalized names, ignoring leading and trailing spaces
// on each; and the whole may be quoted // on each; and the whole may be quoted
choices = wxStringTokenize( auto choices = wxStringTokenize(
text[0] == wxT('"') ? text.Mid(1, text.Length() - 2) : text, text[0] == wxT('"') ? text.Mid(1, text.Length() - 2) : text,
wxT(",") wxT(",")
); );
for (auto &choice : choices) for (auto &choice : choices)
choice = choice.Trim(true).Trim(false); choice = choice.Trim(true).Trim(false);
return choices;
} }
return choices;
} }
void NyquistEffect::RedirectOutput() void NyquistEffect::RedirectOutput()
@ -1530,8 +1531,9 @@ wxString NyquistEffect::UnQuote(
} }
else if (allowParens && else if (allowParens &&
len >= 2 && s[0] == wxT('(') && s[len - 1] == wxT(')')) { len >= 2 && s[0] == wxT('(') && s[len - 1] == wxT(')')) {
wxArrayString tokens; Tokenizer tzer;
Tokenize(s, tokens, 1, 1); tzer.Tokenize(s, true, 1, 1);
auto &tokens = tzer.tokens;
if (tokens.size() > 1) if (tokens.size() > 1)
// Assume the first token was _ -- we don't check that // Assume the first token was _ -- we don't check that
// And the second is the string, which is internationalized // And the second is the string, which is internationalized
@ -1564,15 +1566,10 @@ double NyquistEffect::GetCtrlValue(const wxString &s)
return Internat::CompatibleToDouble(s); return Internat::CompatibleToDouble(s);
} }
void NyquistEffect::Tokenize( bool NyquistEffect::Tokenizer::Tokenize(
const wxString &line, wxArrayString &tokens, const wxString &line, bool eof,
size_t trimStart, size_t trimEnd) size_t trimStart, size_t trimEnd)
{ {
bool sl = false;
bool q = false;
wxString tok = wxT("");
int paren = 0;
auto endToken = [&]{ auto endToken = [&]{
if (!tok.empty()) { if (!tok.empty()) {
tokens.push_back(tok); tokens.push_back(tok);
@ -1587,8 +1584,6 @@ void NyquistEffect::Tokenize(
sl = true; sl = true;
continue; continue;
} }
else if (sl && c == wxT('n'))
c = wxT('\n');
if (!sl && !paren && c == wxT('"')) { if (!sl && !paren && c == wxT('"')) {
if (!q) if (!q)
@ -1626,18 +1621,29 @@ void NyquistEffect::Tokenize(
sl = false; sl = false;
} }
// Finish -- this just forgives unbalanced open quotes or left parens if (eof || (!q && !paren)) {
endToken(); endToken();
return true;
}
else {
// End of line but not of file, and a string or list is yet unclosed
// If a string, accumulate a newline character
if (q)
tok += wxT('\n');
return false;
}
} }
void NyquistEffect::Parse(const wxString &line) bool NyquistEffect::Parse(
Tokenizer &tzer, const wxString &line, bool eof, bool first)
{ {
wxArrayString tokens; if ( !tzer.Tokenize(line, eof, first ? 1 : 0, 0) )
Tokenize(line, tokens, 1, 0); return false;
const auto &tokens = tzer.tokens;
int len = tokens.size(); int len = tokens.size();
if (len < 1) { if (len < 1) {
return; return true;
} }
// Consistency decision is for "plug-in" as the correct spelling // Consistency decision is for "plug-in" as the correct spelling
@ -1645,7 +1651,7 @@ void NyquistEffect::Parse(const wxString &line)
if (len == 2 && tokens[0] == wxT("nyquist") && if (len == 2 && tokens[0] == wxT("nyquist") &&
(tokens[1] == wxT("plug-in") || tokens[1] == wxT("plugin"))) { (tokens[1] == wxT("plug-in") || tokens[1] == wxT("plugin"))) {
mOK = true; mOK = true;
return; return true;
} }
if (len >= 2 && tokens[0] == wxT("type")) { if (len >= 2 && tokens[0] == wxT("type")) {
@ -1661,7 +1667,7 @@ void NyquistEffect::Parse(const wxString &line)
if (len >= 3 && tokens[2] == wxT("spectral")) {; if (len >= 3 && tokens[2] == wxT("spectral")) {;
mIsSpectral = true; mIsSpectral = true;
} }
return; return true;
} }
if (len == 2 && tokens[0] == wxT("codetype")) { if (len == 2 && tokens[0] == wxT("codetype")) {
@ -1674,7 +1680,7 @@ void NyquistEffect::Parse(const wxString &line)
mIsSal = true; mIsSal = true;
mFoundType = true; mFoundType = true;
} }
return; return true;
} }
// TODO: Update documentation. // TODO: Update documentation.
@ -1696,7 +1702,7 @@ void NyquistEffect::Parse(const wxString &line)
mCompiler = false; mCompiler = false;
} }
} }
return; return true;
} }
// We support versions 1, 2 and 3 // We support versions 1, 2 and 3
@ -1713,7 +1719,7 @@ void NyquistEffect::Parse(const wxString &line)
_("This version of Audacity does not support Nyquist plug-in version %ld"), _("This version of Audacity does not support Nyquist plug-in version %ld"),
v v
); );
return; return true;
} }
mVersion = (int) v; mVersion = (int) v;
} }
@ -1722,17 +1728,17 @@ void NyquistEffect::Parse(const wxString &line)
mName = UnQuote(tokens[1]); mName = UnQuote(tokens[1]);
if (mName.EndsWith(wxT("..."))) if (mName.EndsWith(wxT("...")))
mName = mName.RemoveLast(3); mName = mName.RemoveLast(3);
return; return true;
} }
if (len >= 2 && tokens[0] == wxT("action")) { if (len >= 2 && tokens[0] == wxT("action")) {
mAction = UnQuote(tokens[1]); mAction = UnQuote(tokens[1]);
return; return true;
} }
if (len >= 2 && tokens[0] == wxT("info")) { if (len >= 2 && tokens[0] == wxT("info")) {
mInfo = UnQuote(tokens[1]); mInfo = UnQuote(tokens[1]);
return; return true;
} }
if (len >= 2 && tokens[0] == wxT("preview")) { if (len >= 2 && tokens[0] == wxT("preview")) {
@ -1751,7 +1757,7 @@ void NyquistEffect::Parse(const wxString &line)
else if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) { else if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) {
mEnablePreview = false; mEnablePreview = false;
} }
return; return true;
} }
// Maximum number of samples to be processed. This can help the // Maximum number of samples to be processed. This can help the
@ -1768,7 +1774,7 @@ void NyquistEffect::Parse(const wxString &line)
// -1 = auto (default), 0 = don't merge clips, 1 = do merge clips // -1 = auto (default), 0 = don't merge clips, 1 = do merge clips
tokens[1].ToLong(&v); tokens[1].ToLong(&v);
mMergeClips = v; mMergeClips = v;
return; return true;
} }
if (len >= 2 && tokens[0] == wxT("restoresplits")) { if (len >= 2 && tokens[0] == wxT("restoresplits")) {
@ -1776,18 +1782,18 @@ void NyquistEffect::Parse(const wxString &line)
// Splits are restored by default. Set to 0 to prevent. // Splits are restored by default. Set to 0 to prevent.
tokens[1].ToLong(&v); tokens[1].ToLong(&v);
mRestoreSplits = !!v; mRestoreSplits = !!v;
return; return true;
} }
#endif #endif
if (len >= 2 && tokens[0] == wxT("author")) { if (len >= 2 && tokens[0] == wxT("author")) {
mAuthor = UnQuote(tokens[1]); mAuthor = UnQuote(tokens[1]);
return; return true;
} }
if (len >= 2 && tokens[0] == wxT("copyright")) { if (len >= 2 && tokens[0] == wxT("copyright")) {
mCopyright = UnQuote(tokens[1]); mCopyright = UnQuote(tokens[1]);
return; return true;
} }
// TODO: Document. // TODO: Document.
@ -1795,7 +1801,7 @@ void NyquistEffect::Parse(const wxString &line)
if (len >= 2 && tokens[0] == wxT("manpage")) { if (len >= 2 && tokens[0] == wxT("manpage")) {
// do not translate // do not translate
mManPage = UnQuote(tokens[1], false, false); mManPage = UnQuote(tokens[1], false, false);
return; return true;
} }
// TODO: Document. // TODO: Document.
@ -1803,7 +1809,7 @@ void NyquistEffect::Parse(const wxString &line)
if (len >= 2 && tokens[0] == wxT("helpfile")) { if (len >= 2 && tokens[0] == wxT("helpfile")) {
// do not translate // do not translate
mHelpFile = UnQuote(tokens[1], false, false); mHelpFile = UnQuote(tokens[1], false, false);
return; return true;
} }
// TODO: Document. // TODO: Document.
@ -1812,7 +1818,7 @@ void NyquistEffect::Parse(const wxString &line)
if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) { if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) {
mDebugButton = false; mDebugButton = false;
} }
return; return true;
} }
if (len >= 6 && tokens[0] == wxT("control")) { if (len >= 6 && tokens[0] == wxT("control")) {
@ -1845,7 +1851,7 @@ void NyquistEffect::Parse(const wxString &line)
ctrl.label = UnQuote( ctrl.label ); ctrl.label = UnQuote( ctrl.label );
if (len < 8) { if (len < 8) {
return; return true;
} }
if ((tokens[3] == wxT("float")) || if ((tokens[3] == wxT("float")) ||
@ -1869,7 +1875,7 @@ void NyquistEffect::Parse(const wxString &line)
// Note that the AudacityApp's mLogger has not yet been created, // Note that the AudacityApp's mLogger has not yet been created,
// so this brings up an alert box, but after the Audacity frame is up. // so this brings up an alert box, but after the Audacity frame is up.
wxLogWarning(str); wxLogWarning(str);
return; return true;
} }
ctrl.lowStr = tokens[6]; ctrl.lowStr = tokens[6];
@ -1924,6 +1930,7 @@ void NyquistEffect::Parse(const wxString &line)
mCategories.Add(tokens[i]); mCategories.Add(tokens[i]);
} }
} }
return true;
} }
bool NyquistEffect::ParseProgram(wxInputStream & stream) bool NyquistEffect::ParseProgram(wxInputStream & stream)
@ -1952,18 +1959,29 @@ bool NyquistEffect::ParseProgram(wxInputStream & stream)
mFoundType = false; mFoundType = false;
while (!stream.Eof() && stream.IsOk()) while (!stream.Eof() && stream.IsOk())
{ {
bool dollar = false;
wxString line = pgm.ReadLine().Trim(false); wxString line = pgm.ReadLine().Trim(false);
if (line.Length() > 1 && if (line.Length() > 1 &&
// New in 2.3.0: allow magic comment lines to start with $ // New in 2.3.0: allow magic comment lines to start with $
// The trick is that xgettext will not consider such lines comments // The trick is that xgettext will not consider such lines comments
// and will extract the strings they contain // and will extract the strings they contain
(line[0] == wxT(';') || line[0] == wxT('$'))) (line[0] == wxT(';') ||
(dollar = (line[0] == wxT('$')))))
{ {
Parse(line); Tokenizer tzer;
// Don't pass this line to the interpreter, so it doesn't get confused unsigned nLines = 1;
// by $, but pass a blank, bool done;
do
// Allow run-ons only for new $ format header lines
done = Parse(tzer, line, !dollar || stream.Eof(), nLines == 1);
while(!done &&
(line = pgm.ReadLine().Trim(false), ++nLines, true));
// Don't pass these lines to the interpreter, so it doesn't get confused
// by $, but pass blanks,
// so that SAL effects compile with proper line numbers // so that SAL effects compile with proper line numbers
mCmd += wxT('\n'); while (nLines --)
mCmd += wxT('\n');
} }
else else
{ {

View File

@ -163,10 +163,18 @@ private:
void ParseFile(); void ParseFile();
bool ParseCommand(const wxString & cmd); bool ParseCommand(const wxString & cmd);
bool ParseProgram(wxInputStream & stream); bool ParseProgram(wxInputStream & stream);
static void Tokenize( struct Tokenizer {
const wxString &line, wxArrayString &tokens, bool sl { false };
size_t trimStart, size_t trimEnd); bool q { false };
void Parse(const wxString &line); int paren{ 0 };
wxString tok;
wxArrayString tokens;
bool Tokenize(
const wxString &line, bool eof,
size_t trimStart, size_t trimEnd);
};
bool Parse(Tokenizer &tokenizer, const wxString &line, bool eof, bool first);
static wxString UnQuote(const wxString &s, static wxString UnQuote(const wxString &s,
bool allowParens = true, bool translate = true); bool allowParens = true, bool translate = true);