123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466 |
- /* Interpreter.cpp
- *
- * Copyright (C) 1993-2018 Paul Boersma
- *
- * This code is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This code is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this work. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <ctype.h>
- #include "Interpreter.h"
- #include "praatP.h"
- extern structMelderDir praatDir;
- #include "praat_script.h"
- #include "Formula.h"
- #include "praat_version.h"
- #include "../kar/UnicodeData.h"
- #include "../fon/Vector.h"
- #define Interpreter_WORD 1
- #define Interpreter_REAL 2
- #define Interpreter_POSITIVE 3
- #define Interpreter_INTEGER 4
- #define Interpreter_NATURAL 5
- #define Interpreter_BOOLEAN 6
- #define Interpreter_SENTENCE 7
- #define Interpreter_TEXT 8
- #define Interpreter_CHOICE 9
- #define Interpreter_OPTIONMENU 10
- #define Interpreter_BUTTON 11
- #define Interpreter_OPTION 12
- #define Interpreter_COMMENT 13
- autoVEC theInterpreterNumvec;
- autoMAT theInterpreterNummat;
- Thing_implement (InterpreterVariable, SimpleString, 0);
- static autoInterpreterVariable InterpreterVariable_create (conststring32 key) {
- try {
- if (key [0] == U'e' && key [1] == U'\0')
- Melder_throw (U"You cannot use 'e' as the name of a variable (e is the constant 2.71...).");
- if (key [0] == U'p' && key [1] == U'i' && key [2] == U'\0')
- Melder_throw (U"You cannot use 'pi' as the name of a variable (pi is the constant 3.14...).");
- if (key [0] == U'u' && key [1] == U'n' && key [2] == U'd' && key [3] == U'e' && key [4] == U'f' && key [5] == U'i' &&
- key [6] == U'n' && key [7] == U'e' && key [8] == U'd' && key [9] == U'\0')
- Melder_throw (U"You cannot use 'undefined' as the name of a variable.");
- autoInterpreterVariable me = Thing_new (InterpreterVariable);
- my string = Melder_dup (key);
- return me;
- } catch (MelderError) {
- Melder_throw (U"Interpreter variable not created.");
- }
- }
- Thing_implement (Interpreter, Thing, 0);
- autoInterpreter Interpreter_create (conststring32 environmentName, ClassInfo editorClass) {
- try {
- autoInterpreter me = Thing_new (Interpreter);
- my variablesMap. max_load_factor (0.65f);
- my environmentName = Melder_dup (environmentName);
- my editorClass = editorClass;
- return me;
- } catch (MelderError) {
- Melder_throw (U"Interpreter not created.");
- }
- }
- autoInterpreter Interpreter_createFromEnvironment (Editor editor) {
- if (! editor) return Interpreter_create (nullptr, nullptr);
- return Interpreter_create (editor -> name.get(), editor -> classInfo);
- }
- void Melder_includeIncludeFiles (autostring32 *inout_text) {
- for (int depth = 0; ; depth ++) {
- char32 *head = inout_text->get();
- integer numberOfIncludes = 0;
- if (depth > 10)
- Melder_throw (U"Include files nested too deep. Probably cyclic.");
- for (;;) {
- char32 *includeLocation, *includeFileName, *tail;
- integer headLength, includeTextLength, newLength;
- /*
- Look for an include statement. If not found, we have finished.
- */
- includeLocation = ( str32nequ (head, U"include ", 8) ? head : str32str (head, U"\ninclude ") );
- if (! includeLocation)
- break;
- if (includeLocation != head)
- includeLocation += 1;
- numberOfIncludes += 1;
- /*
- Separate out the head.
- */
- *includeLocation = U'\0';
- /*
- Separate out the name of the include file.
- */
- includeFileName = includeLocation + 8;
- while (Melder_isHorizontalSpace (*includeFileName)) includeFileName ++;
- tail = includeFileName;
- while (Melder_staysWithinLine (*tail)) tail ++;
- if (*tail != U'\0') {
- *tail = U'\0';
- tail += 1;
- }
- /*
- Get the contents of the include file.
- */
- structMelderFile includeFile { };
- Melder_relativePathToFile (includeFileName, & includeFile);
- autostring32 includeText;
- try {
- includeText = MelderFile_readText (& includeFile);
- } catch (MelderError) {
- Melder_throw (U"Include file ", & includeFile, U" not read.");
- }
- /*
- Construct the new text.
- */
- headLength = (head - inout_text->get()) + str32len (head);
- includeTextLength = str32len (includeText.get());
- newLength = headLength + includeTextLength + 1 + str32len (tail);
- autostring32 newText (newLength);
- str32cpy (newText.get(), inout_text->get());
- str32cpy (newText.get() + headLength, includeText.get());
- str32cpy (newText.get() + headLength + includeTextLength, U"\n");
- str32cpy (newText.get() + headLength + includeTextLength + 1, tail);
- /*
- Replace the old text with the new. This will work even within an autostring.
- */
- *inout_text = newText.move();
- /*
- Cycle.
- */
- head = inout_text->get() + headLength + includeTextLength + 1;
- }
- if (numberOfIncludes == 0) break;
- }
- }
- integer Interpreter_readParameters (Interpreter me, mutablestring32 text) {
- char32 *formLocation = nullptr;
- integer npar = 0;
- my dialogTitle [0] = U'\0';
- /*
- Look for a "form" line.
- */
- {// scope
- char32 *p = & text [0];
- for (;;) {
- /*
- Invariant here: we are at the beginning of a line.
- */
- Melder_skipHorizontalSpace (& p);
- if (str32nequ (p, U"form", 4) && Melder_isEndOfInk (p [4])) {
- formLocation = p;
- break;
- }
- Melder_skipToEndOfLine (& p);
- if (*p == U'\0') break;
- p ++; // skip newline symbol
- }
- }
- /*
- If there is no "form" line, there are no parameters.
- */
- if (formLocation) {
- char32 *dialogTitle = Melder_findEndOfHorizontalSpace (formLocation + 4);
- char32 *endOfLine = Melder_findEndOfLine (dialogTitle);
- if (*endOfLine == U'\0')
- Melder_throw (U"Unfinished form (only a \"form\" line).");
- *endOfLine = U'\0'; // destroy input temporarily in order to limit copying of dialog title
- str32ncpy (my dialogTitle, dialogTitle, Interpreter_MAX_DIALOG_TITLE_LENGTH);
- *endOfLine = U'\n'; // restore input
- my numberOfParameters = 0;
- while (true) {
- int type = 0;
- char32 *startOfLine = Melder_findEndOfHorizontalSpace (endOfLine + 1);
- /*
- Skip empty lines and lines with comments.
- */
- while (*startOfLine == U'#' || *startOfLine == U';' || *startOfLine == U'!' || Melder_isEndOfLine (*startOfLine)) {
- endOfLine = Melder_findEndOfLine (startOfLine);
- if (Melder_isEndOfText (*endOfLine))
- Melder_throw (U"Unfinished form (missing \"endform\").");
- startOfLine = Melder_findEndOfHorizontalSpace (endOfLine + 1);
- }
- if (str32nequ (startOfLine, U"endform", 7) && Melder_isEndOfInk (startOfLine [7])) break;
- char32 *parameterLocation;
- if (str32nequ (startOfLine, U"word", 4) && Melder_isEndOfInk (startOfLine [4]))
- { type = Interpreter_WORD; parameterLocation = startOfLine + 4; }
- else if (str32nequ (startOfLine, U"real", 4) && Melder_isEndOfInk (startOfLine [4]))
- { type = Interpreter_REAL; parameterLocation = startOfLine + 4; }
- else if (str32nequ (startOfLine, U"positive", 8) && Melder_isEndOfInk (startOfLine [8]))
- { type = Interpreter_POSITIVE; parameterLocation = startOfLine + 8; }
- else if (str32nequ (startOfLine, U"integer", 7) && Melder_isEndOfInk (startOfLine [7]))
- { type = Interpreter_INTEGER; parameterLocation = startOfLine + 7; }
- else if (str32nequ (startOfLine, U"natural", 7) && Melder_isEndOfInk (startOfLine [7]))
- { type = Interpreter_NATURAL; parameterLocation = startOfLine + 7; }
- else if (str32nequ (startOfLine, U"boolean", 7) && Melder_isEndOfInk (startOfLine [7]))
- { type = Interpreter_BOOLEAN; parameterLocation = startOfLine + 7; }
- else if (str32nequ (startOfLine, U"sentence", 8) && Melder_isEndOfInk (startOfLine [8]))
- { type = Interpreter_SENTENCE; parameterLocation = startOfLine + 8; }
- else if (str32nequ (startOfLine, U"text", 4) && Melder_isEndOfInk (startOfLine [4]))
- { type = Interpreter_TEXT; parameterLocation = startOfLine + 4; }
- else if (str32nequ (startOfLine, U"choice", 6) && Melder_isEndOfInk (startOfLine [6]))
- { type = Interpreter_CHOICE; parameterLocation = startOfLine + 6; }
- else if (str32nequ (startOfLine, U"optionmenu", 10) && Melder_isEndOfInk (startOfLine [10]))
- { type = Interpreter_OPTIONMENU; parameterLocation = startOfLine + 10; }
- else if (str32nequ (startOfLine, U"button", 6) && Melder_isEndOfInk (startOfLine [6]))
- { type = Interpreter_BUTTON; parameterLocation = startOfLine + 6; }
- else if (str32nequ (startOfLine, U"option", 6) && Melder_isEndOfInk (startOfLine [6]))
- { type = Interpreter_OPTION; parameterLocation = startOfLine + 6; }
- else if (str32nequ (startOfLine, U"comment", 7) && Melder_isEndOfInk (startOfLine [7]))
- { type = Interpreter_COMMENT; parameterLocation = startOfLine + 7; }
- else {
- endOfLine = Melder_findEndOfLine (startOfLine);
- *endOfLine = U'\0'; // destroy input in order to limit printing of parameter type
- Melder_throw (U"Unknown parameter type:\n\"", startOfLine, U"\".");
- }
- /*
- Example:
- form Something
- real Time_(s) 3.14 (= pi)
- choice Colour 2
- button Red
- button Green
- button Blue
- endform
- my parameters [1] := "Time_(s)"
- my parameters [2] := "Colour"
- my parameters [3] := ""
- my parameters [4] := ""
- my parameters [5] := ""
- my arguments [1] := "3.14 (= pi)"
- my arguments [2] := "2"
- my arguments [3] := "Red" (funny, but needed in Interpreter_getArgumentsFromString)
- my arguments [4] := "Green"
- my arguments [5] := "Blue"
- */
- if (type <= Interpreter_OPTIONMENU) {
- Melder_skipHorizontalSpace (& parameterLocation);
- if (Melder_isEndOfLine (*parameterLocation)) {
- *parameterLocation = U'\0'; // destroy input in order to limit printing of line
- Melder_throw (U"Missing parameter:\n\"", startOfLine, U"\".");
- }
- char32 *q = my parameters [++ my numberOfParameters];
- while (Melder_staysWithinInk (*parameterLocation)) * (q ++) = * (parameterLocation ++);
- *q = U'\0';
- npar ++;
- } else {
- my parameters [++ my numberOfParameters] [0] = U'\0';
- }
- char32 *argumentLocation = Melder_findEndOfHorizontalSpace (parameterLocation);
- endOfLine = Melder_findEndOfLine (argumentLocation);
- if (Melder_isEndOfText (*endOfLine))
- Melder_throw (U"Unfinished form (missing \"endform\").");
- *endOfLine = U'\0'; // destroy input temporarily in order to limit copying of argument
- my arguments [my numberOfParameters] = Melder_dup_f (argumentLocation);
- *endOfLine = U'\n'; // restore input
- my types [my numberOfParameters] = type;
- }
- } else {
- npar = my numberOfParameters = 0;
- }
- return npar;
- }
- autoUiForm Interpreter_createForm (Interpreter me, GuiWindow parent, conststring32 path,
- void (*okCallback) (UiForm, integer, Stackel, conststring32, Interpreter, conststring32, bool, void *), void *okClosure,
- bool selectionOnly)
- {
- autoUiForm form = UiForm_create (parent,
- Melder_cat (selectionOnly ? U"Run script (selection only): " : U"Run script: ", my dialogTitle),
- okCallback, okClosure, nullptr, nullptr);
- UiField radio = nullptr;
- if (path)
- UiForm_addText (form.get(), nullptr, nullptr, U"$file", path);
- for (int ipar = 1; ipar <= my numberOfParameters; ipar ++) {
- /*
- * Convert underscores to spaces.
- */
- char32 parameter [100], *p = & parameter [0];
- str32cpy (parameter, my parameters [ipar]);
- while (*p) { if (*p == U'_') *p = U' '; p ++; }
- switch (my types [ipar]) {
- case Interpreter_WORD:
- UiForm_addWord (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
- case Interpreter_REAL:
- UiForm_addReal (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break; // TODO: an address of a real variable
- case Interpreter_POSITIVE:
- UiForm_addPositive (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
- case Interpreter_INTEGER:
- UiForm_addInteger (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
- case Interpreter_NATURAL:
- UiForm_addNatural (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
- case Interpreter_BOOLEAN:
- UiForm_addBoolean (form.get(), nullptr, nullptr, parameter, my arguments [ipar] [0] == U'1' ||
- my arguments [ipar] [0] == U'y' || my arguments [ipar] [0] == U'Y' ||
- (my arguments [ipar] [0] == U'o' && my arguments [ipar] [1] == U'n')); break;
- case Interpreter_SENTENCE:
- UiForm_addSentence (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
- case Interpreter_TEXT:
- UiForm_addText (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
- case Interpreter_CHOICE:
- radio = UiForm_addRadio (form.get(), nullptr, nullptr, nullptr, parameter, Melder_atoi (my arguments [ipar].get()), 1); break;
- case Interpreter_OPTIONMENU:
- radio = UiForm_addOptionMenu (form.get(), nullptr, nullptr, nullptr, parameter, Melder_atoi (my arguments [ipar].get()), 1); break;
- case Interpreter_BUTTON:
- if (radio) UiRadio_addButton (radio, my arguments [ipar].get()); break;
- case Interpreter_OPTION:
- if (radio) UiOptionMenu_addButton (radio, my arguments [ipar].get()); break;
- case Interpreter_COMMENT:
- UiForm_addLabel (form.get(), nullptr, my arguments [ipar].get()); break;
- default:
- UiForm_addWord (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
- }
- /*
- * Strip parentheses and colon off parameter name.
- */
- if ((p = str32chr (my parameters [ipar], U'(')) != nullptr) {
- *p = U'\0';
- if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
- }
- p = my parameters [ipar];
- if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
- }
- UiForm_finish (form.get());
- return form;
- }
- void Interpreter_getArgumentsFromDialog (Interpreter me, UiForm dialog) {
- for (int ipar = 1; ipar <= my numberOfParameters; ipar ++) {
- char32 parameter [100], *p;
- /*
- * Strip parentheses and colon off parameter name.
- */
- if ((p = str32chr (my parameters [ipar], U'(')) != nullptr) {
- *p = U'\0';
- if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
- }
- p = my parameters [ipar];
- if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
- /*
- * Convert underscores to spaces.
- */
- str32cpy (parameter, my parameters [ipar]);
- p = & parameter [0]; while (*p) { if (*p == U'_') *p = U' '; p ++; }
- switch (my types [ipar]) {
- case Interpreter_REAL:
- case Interpreter_POSITIVE: {
- double value = UiForm_getReal_check (dialog, parameter);
- my arguments [ipar] = autostring32 (40, true);
- Melder_sprint (my arguments [ipar].get(),40+1, value);
- break;
- }
- case Interpreter_INTEGER:
- case Interpreter_NATURAL:
- case Interpreter_BOOLEAN: {
- integer value = UiForm_getInteger (dialog, parameter);
- my arguments [ipar] = autostring32 (40, true);
- Melder_sprint (my arguments [ipar].get(),40+1, value);
- break;
- }
- case Interpreter_CHOICE:
- case Interpreter_OPTIONMENU: {
- integer integerValue = 0;
- integerValue = UiForm_getInteger (dialog, parameter);
- conststring32 stringValue = UiForm_getString (dialog, parameter);
- my arguments [ipar] = autostring32 (40, true);
- Melder_sprint (my arguments [ipar].get(),40+1, integerValue);
- Melder_sprint (my choiceArguments [ipar],100, stringValue);
- break;
- }
- case Interpreter_BUTTON:
- case Interpreter_OPTION:
- case Interpreter_COMMENT:
- break;
- default: {
- conststring32 value = UiForm_getString (dialog, parameter);
- my arguments [ipar] = Melder_dup_f (value);
- break;
- }
- }
- }
- }
- void Interpreter_getArgumentsFromString (Interpreter me, conststring32 arguments) {
- int size = my numberOfParameters;
- integer length = str32len (arguments);
- while (size >= 1 && my parameters [size] [0] == U'\0')
- size --; /* Ignore fields without a variable name (button, comment). */
- for (int ipar = 1; ipar <= size; ipar ++) {
- char32 *p = my parameters [ipar];
- /*
- * Ignore buttons and comments again.
- */
- if (! *p) continue;
- /*
- * Strip parentheses and colon off parameter name.
- */
- if ((p = str32chr (p, U'(')) != nullptr) {
- *p = U'\0';
- if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
- }
- p = my parameters [ipar];
- if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
- }
- for (int ipar = 1; ipar < size; ipar ++) {
- int ichar = 0;
- /*
- * Ignore buttons and comments again. The buttons will keep their labels as "arguments".
- */
- if (my parameters [ipar] [0] == U'\0') continue;
- /*
- Erase the current values, probably the default values,
- and replace with the actual arguments.
- */
- my arguments [ipar] = autostring32 (length);
- /*
- Skip spaces until next argument.
- */
- while (Melder_isHorizontalSpace (*arguments)) arguments ++;
- /*
- The argument is everything up to the next space, or, if it starts with a double quote,
- everything between this quote and the matching double quote;
- in this case, the argument can represent a double quote by a sequence of two double quotes.
- Example: the string
- "I said ""hello"""
- will be passed to the dialog as a single argument containing the text
- I said "hello"
- */
- if (*arguments == U'\"') {
- arguments ++; // do not include leading double quote
- for (;;) {
- if (*arguments == U'\0')
- Melder_throw (U"Missing matching quote.");
- if (*arguments == U'\"' && * ++ arguments != U'\"') break; // remember second quote
- my arguments [ipar] [ichar ++] = *arguments ++;
- }
- } else {
- while (Melder_staysWithinInk (*arguments))
- my arguments [ipar] [ichar ++] = *arguments ++;
- }
- my arguments [ipar] [ichar] = U'\0'; // trailing null byte
- }
- /*
- The last item is handled separately, because it consists of the rest of the line.
- Leading spaces are skipped, but trailing spaces are included.
- */
- if (size > 0) {
- while (Melder_isHorizontalSpace (*arguments)) arguments ++;
- my arguments [size] = Melder_dup_f (arguments);
- }
- /*
- Convert booleans and choices to numbers.
- */
- for (int ipar = 1; ipar <= size; ipar ++) {
- if (my types [ipar] == Interpreter_BOOLEAN) {
- mutablestring32 arg = & my arguments [ipar] [0];
- if (str32equ (arg, U"1") || str32equ (arg, U"yes") || str32equ (arg, U"on") ||
- str32equ (arg, U"Yes") || str32equ (arg, U"On") || str32equ (arg, U"YES") || str32equ (arg, U"ON"))
- {
- str32cpy (arg, U"1");
- } else if (str32equ (arg, U"0") || str32equ (arg, U"no") || str32equ (arg, U"off") ||
- str32equ (arg, U"No") || str32equ (arg, U"Off") || str32equ (arg, U"NO") || str32equ (arg, U"OFF"))
- {
- str32cpy (arg, U"0");
- } else {
- Melder_throw (U"Unknown value \"", arg, U"\" for boolean \"", my parameters [ipar], U"\".");
- }
- } else if (my types [ipar] == Interpreter_CHOICE) {
- int jpar;
- mutablestring32 arg = & my arguments [ipar] [0];
- for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
- if (my types [jpar] != Interpreter_BUTTON && my types [jpar] != Interpreter_OPTION)
- Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
- if (str32equ (my arguments [jpar].get(), arg)) { // the button labels are in the arguments; see Interpreter_readParameters
- str32cpy (arg, Melder_integer (jpar - ipar));
- str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
- break;
- }
- }
- if (jpar > my numberOfParameters)
- Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
- } else if (my types [ipar] == Interpreter_OPTIONMENU) {
- int jpar;
- mutablestring32 arg = & my arguments [ipar] [0];
- for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
- if (my types [jpar] != Interpreter_OPTION && my types [jpar] != Interpreter_BUTTON)
- Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
- if (str32equ (my arguments [jpar].get(), arg)) {
- str32cpy (arg, Melder_integer (jpar - ipar));
- str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
- break;
- }
- }
- if (jpar > my numberOfParameters)
- Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
- }
- }
- }
- void Interpreter_getArgumentsFromArgs (Interpreter me, int narg, Stackel args) {
- trace (narg, U" arguments");
- int size = my numberOfParameters;
- while (size >= 1 && my parameters [size] [0] == U'\0')
- size --; // ignore trailing fields without a variable name (button, comment)
- for (int ipar = 1; ipar <= size; ipar ++) {
- mutablestring32 p = my parameters [ipar];
- /*
- * Ignore buttons and comments again.
- */
- if (! *p) continue;
- /*
- * Strip parentheses and colon off parameter name.
- */
- if ((p = str32chr (p, U'(')) != nullptr) {
- *p = U'\0';
- if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
- }
- p = my parameters [ipar];
- if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
- }
- int iarg = 0;
- for (int ipar = 1; ipar <= size; ipar ++) {
- /*
- * Ignore buttons and comments again. The buttons will keep their labels as "arguments".
- */
- if (my parameters [ipar] [0] == U'\0') continue;
- /*
- Erase the current values, probably the default values...
- */
- my arguments [ipar]. reset(); //
- if (iarg == narg)
- Melder_throw (U"Found ", narg, U" arguments but expected more.");
- Stackel arg = & args [++ iarg];
- /*
- ... and replace with the actual arguments.
- */
- my arguments [ipar] =
- arg -> which == Stackel_NUMBER ? Melder_dup (Melder_double (arg -> number)) :
- arg -> which == Stackel_STRING ? Melder_dup (arg -> getString()) : autostring32();
- Melder_assert (my arguments [ipar]);
- }
- if (iarg < narg)
- Melder_throw (U"Found ", narg, U" arguments but expected only ", iarg, U".");
- /*
- * Convert booleans and choices to numbers.
- */
- for (int ipar = 1; ipar <= size; ipar ++) {
- if (my types [ipar] == Interpreter_BOOLEAN) {
- mutablestring32 arg = & my arguments [ipar] [0];
- if (str32equ (arg, U"1") || str32equ (arg, U"yes") || str32equ (arg, U"on") ||
- str32equ (arg, U"Yes") || str32equ (arg, U"On") || str32equ (arg, U"YES") || str32equ (arg, U"ON"))
- {
- str32cpy (arg, U"1");
- } else if (str32equ (arg, U"0") || str32equ (arg, U"no") || str32equ (arg, U"off") ||
- str32equ (arg, U"No") || str32equ (arg, U"Off") || str32equ (arg, U"NO") || str32equ (arg, U"OFF"))
- {
- str32cpy (arg, U"0");
- } else {
- Melder_throw (U"Unknown value \"", arg, U"\" for boolean \"", my parameters [ipar], U"\".");
- }
- } else if (my types [ipar] == Interpreter_CHOICE) {
- int jpar;
- mutablestring32 arg = & my arguments [ipar] [0];
- for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
- if (my types [jpar] != Interpreter_BUTTON && my types [jpar] != Interpreter_OPTION)
- Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
- if (str32equ (my arguments [jpar].get(), arg)) { // the button labels are in the arguments; see Interpreter_readParameters
- str32cpy (arg, Melder_integer (jpar - ipar));
- str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
- break;
- }
- }
- if (jpar > my numberOfParameters)
- Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
- } else if (my types [ipar] == Interpreter_OPTIONMENU) {
- int jpar;
- mutablestring32 arg = & my arguments [ipar] [0];
- for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
- if (my types [jpar] != Interpreter_OPTION && my types [jpar] != Interpreter_BUTTON)
- Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
- if (str32equ (my arguments [jpar].get(), arg)) {
- str32cpy (arg, Melder_integer (jpar - ipar));
- str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
- break;
- }
- }
- if (jpar > my numberOfParameters)
- Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
- }
- }
- }
- static void Interpreter_addNumericVariable (Interpreter me, conststring32 key, double value) {
- autoInterpreterVariable variable = InterpreterVariable_create (key);
- variable -> numericValue = value;
- my variablesMap [key] = variable.move();
- variable.releaseToAmbiguousOwner();
- }
- static void Interpreter_addStringVariable (Interpreter me, conststring32 key, conststring32 value) {
- autoInterpreterVariable variable = InterpreterVariable_create (key);
- variable -> stringValue = Melder_dup (value);
- my variablesMap [key] = variable.move();
- variable.releaseToAmbiguousOwner();
- }
- InterpreterVariable Interpreter_hasVariable (Interpreter me, conststring32 key) {
- Melder_assert (key);
- auto it = my variablesMap. find (key [0] == U'.' ? Melder_cat (my procedureNames [my callDepth], key) : key);
- if (it != my variablesMap. end()) {
- return it -> second.get();
- } else {
- return nullptr;
- }
- }
- InterpreterVariable Interpreter_lookUpVariable (Interpreter me, conststring32 key) {
- Melder_assert (key);
- conststring32 variableNameIncludingProcedureName =
- key [0] == U'.' ? Melder_cat (my procedureNames [my callDepth], key) : key;
- auto it = my variablesMap. find (variableNameIncludingProcedureName);
- if (it != my variablesMap. end()) {
- return it -> second.get();
- }
- /*
- * The variable doesn't yet exist: create a new one.
- */
- autoInterpreterVariable variable = InterpreterVariable_create (variableNameIncludingProcedureName);
- InterpreterVariable variable_ref = variable.get();
- my variablesMap [variableNameIncludingProcedureName] = variable.move();
- return variable_ref;
- }
- static integer lookupLabel (Interpreter me, conststring32 labelName) {
- for (integer ilabel = 1; ilabel <= my numberOfLabels; ilabel ++)
- if (str32equ (labelName, my labelNames [ilabel]))
- return ilabel;
- Melder_throw (U"Unknown label \"", labelName, U"\".");
- }
- static bool isCommand (conststring32 string) {
- const char32 *p = & string [0];
- /*
- * Things that start with "nowarn", "noprogress", or "nocheck" are commands.
- */
- if (p [0] == U'n' && p [1] == U'o' &&
- (str32nequ (p + 2, U"warn ", 5) || str32nequ (p + 2, U"progress ", 9) || str32nequ (p + 2, U"check ", 6))) return true;
- if (str32nequ (p, U"demo ", 5)) return true;
- /*
- * Otherwise, things that start with lower case are formulas.
- */
- if (! Melder_isUpperCaseLetter (*p)) return false;
- /*
- * The remaining possibility is things that start with upper case.
- * If they contain an underscore, they are object names, hence we must have a formula.
- * Otherwise, we have a command.
- */
- while (Melder_isAlphanumeric (*p)) p ++;
- return *p != '_';
- }
- static void parameterToVariable (Interpreter me, int type, conststring32 in_parameter, int ipar) {
- char32 parameter [200];
- Melder_assert (type != 0);
- str32cpy (parameter, in_parameter);
- if (type >= Interpreter_REAL && type <= Interpreter_BOOLEAN) {
- Interpreter_addNumericVariable (me, parameter, Melder_atof (my arguments [ipar].get()));
- } else if (type == Interpreter_CHOICE || type == Interpreter_OPTIONMENU) {
- Interpreter_addNumericVariable (me, parameter, Melder_atof (my arguments [ipar].get()));
- str32cat (parameter, U"$");
- Interpreter_addStringVariable (me, parameter, my choiceArguments [ipar]);
- } else if (type == Interpreter_BUTTON || type == Interpreter_OPTION || type == Interpreter_COMMENT) {
- /* Do not add a variable. */
- } else {
- str32cat (parameter, U"$");
- Interpreter_addStringVariable (me, parameter, my arguments [ipar].get());
- }
- }
- inline static void NumericVectorVariable_move (InterpreterVariable variable, VEC movedVector, bool rightHandSideOwned) {
- if (rightHandSideOwned) {
- /*
- Statement like: a# = b# + c#
- */
- variable -> numericVectorValue. adoptFromAmbiguousOwner (movedVector);
- } else if (variable -> numericVectorValue.size == movedVector.size) {
- if (variable -> numericVectorValue.at == movedVector.at) {
- /*
- Statement like: a# = a#
- */
- (void) 0; // assigning a variable to itself: do nothing
- } else {
- /*
- Statement like: a# = b# // with matching sizes
- */
- vectorcopy_preallocated (variable -> numericVectorValue.get(), movedVector);
- }
- } else {
- /*
- Statement like: a# = b# // with non-matching sizes
- */
- variable -> numericVectorValue = VECcopy (movedVector);
- }
- }
- inline static void NumericMatrixVariable_move (InterpreterVariable variable, MAT movedMatrix, bool rightHandSideOwned) {
- if (rightHandSideOwned) {
- /*
- Statement like: a## = b## + c##
- */
- variable -> numericMatrixValue. adoptFromAmbiguousOwner (movedMatrix);
- } else if (variable -> numericMatrixValue.nrow == movedMatrix.nrow && variable -> numericMatrixValue.ncol == movedMatrix.ncol) {
- if (variable -> numericMatrixValue.at == movedMatrix.at) {
- /*
- Statement like: a## = a##
- */
- (void) 0; // assigning a variable to itself: do nothing
- } else {
- /*
- Statement like: a## = b## // with matching sizes
- */
- matrixcopy_preallocated (variable -> numericMatrixValue.get(), movedMatrix);
- }
- } else {
- /*
- Statement like: a## = b## // with non-matching sizes
- */
- variable -> numericMatrixValue = matrixcopy (movedMatrix);
- }
- }
- inline static void NumericVectorVariable_add (InterpreterVariable variable, double scalar) {
- variable -> numericVectorValue += scalar;
- }
- inline static void NumericVectorVariable_add (InterpreterVariable variable, constVEC vector) {
- VEC variableVector = variable -> numericVectorValue.get();
- Melder_require (vector.size == variableVector.size,
- U"You cannot add a vector with size ", vector.size,
- U" to a vector with a different size (", variableVector.size, U")."
- );
- variable -> numericVectorValue += vector;
- }
- inline static void NumericVectorVariable_subtract (InterpreterVariable variable, double scalar) {
- VEC variableVector = variable -> numericVectorValue.get();
- for (integer i = 1; i <= variable -> numericVectorValue.size; i ++)
- variable -> numericVectorValue [i] -= scalar;
- }
- inline static void NumericVectorVariable_subtract (InterpreterVariable variable, constVEC vector) {
- VEC variableVector = variable -> numericVectorValue.get();
- if (vector.size != variable -> numericVectorValue.size)
- Melder_throw (U"You cannot subtract a vector with size ", vector.size,
- U" from a vector with a different size (", variable -> numericVectorValue.size, U").");
- for (integer i = 1; i <= variable -> numericVectorValue.size; i ++)
- variable -> numericVectorValue [i] -= vector [i];
- }
- inline static void NumericVectorVariable_multiply (InterpreterVariable variable, double scalar) {
- VEC variableVector = variable -> numericVectorValue.get();
- for (integer i = 1; i <= variableVector.size; i ++)
- variableVector [i] *= scalar;
- }
- inline static void NumericVectorVariable_multiply (InterpreterVariable variable, constVEC vector) {
- VEC variableVector = variable -> numericVectorValue.get();
- if (vector.size != variableVector.size)
- Melder_throw (U"You cannot multiply a vector with size ", variableVector.size,
- U" with a vector with a different size (", vector.size, U").");
- for (integer i = 1; i <= variableVector.size; i ++)
- variableVector [i] *= vector [i];
- }
- inline static void NumericVectorVariable_divide (InterpreterVariable variable, double scalar) {
- VEC variableVector = variable -> numericVectorValue.get();
- for (integer i = 1; i <= variableVector.size; i ++)
- variableVector [i] /= scalar;
- }
- inline static void NumericVectorVariable_divide (InterpreterVariable variable, constVEC vector) {
- VEC variableVector = variable -> numericVectorValue.get();
- if (vector.size != variableVector.size)
- Melder_throw (U"You cannot divide a vector with size ", variableVector.size,
- U" by a vector with a different size (", vector.size, U").");
- for (integer i = 1; i <= variableVector.size; i ++)
- variableVector [i] /= vector [i];
- }
- inline static void NumericMatrixVariable_add (InterpreterVariable variable, double scalar) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] += scalar;
- }
- inline static void NumericMatrixVariable_add (InterpreterVariable variable, constMAT matrix) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
- Melder_throw (U"You cannot add a matrix with size ", matrix.nrow, U"x", matrix.ncol,
- U" to a matrix with a different size (", variableMatrix.nrow, U"x", variableMatrix.ncol, U").");
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] += matrix [irow] [icol];
- }
- inline static void NumericMatrixVariable_subtract (InterpreterVariable variable, double scalar) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] -= scalar;
- }
- inline static void NumericMatrixVariable_subtract (InterpreterVariable variable, constMAT matrix) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
- Melder_throw (U"You cannot subtract a matrix with size ", matrix.nrow, U"x", matrix.ncol,
- U" from a matrix with a different size (", variableMatrix.nrow, U"x", variableMatrix.ncol, U").");
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] -= matrix [irow] [icol];
- }
- inline static void NumericMatrixVariable_multiply (InterpreterVariable variable, double scalar) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] *= scalar;
- }
- inline static void NumericMatrixVariable_multiply (InterpreterVariable variable, constMAT matrix) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
- Melder_throw (U"You cannot multiply a matrix with size ", variableMatrix.nrow, U"x", variableMatrix.ncol,
- U" from a matrix with a different size (", matrix.nrow, U"x", matrix.ncol, U").");
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] *= matrix [irow] [icol];
- }
- inline static void NumericMatrixVariable_divide (InterpreterVariable variable, double scalar) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] /= scalar;
- }
- inline static void NumericMatrixVariable_divide (InterpreterVariable variable, constMAT matrix) {
- MAT variableMatrix = variable -> numericMatrixValue.get();
- if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
- Melder_throw (U"You cannot divide a matrix with size ", variableMatrix.nrow, U"x", variableMatrix.ncol,
- U" by a matrix with a different size (", matrix.nrow, U"x", matrix.ncol, U").");
- for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
- for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
- variableMatrix [irow] [icol] /= matrix [irow] [icol];
- }
- static void Interpreter_do_procedureCall (Interpreter me, char32 *command,
- char32 * const *lines, integer numberOfLines, integer& lineNumber, integer callStack [], int& callDepth)
- {
- /*
- Modern type of procedure calls, with comma separation, quoted strings, and array support.
- We just passed the `@` sign, so we continue by looking for a procedure name at the call site.
- */
- char32 *p = command;
- while (Melder_isHorizontalSpace (*p)) p ++; // skip whitespace
- char32 *callName = p;
- while (Melder_staysWithinInk (*p) && *p != U'(' && *p != U':') p ++;
- if (p == callName) Melder_throw (U"Missing procedure name after \"@\".");
- bool hasArguments = ( *p != U'\0' );
- if (hasArguments) {
- bool parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
- *p = U'\0'; // close procedure name
- if (! parenthesisOrColonFound) {
- p ++; // step over first white space
- while (Melder_isHorizontalSpace (*p)) p ++; // skip more whitespace
- hasArguments = ( *p != U'\0' );
- parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
- if (hasArguments && ! parenthesisOrColonFound)
- Melder_throw (U"Missing parenthesis or colon after procedure name \"", callName, U"\".");
- }
- p ++; // step over parenthesis or colon
- }
- integer callLength = str32len (callName);
- integer iline = 1;
- for (; iline <= numberOfLines; iline ++) {
- if (! str32nequ (lines [iline], U"procedure ", 10)) continue;
- char32 *q = lines [iline] + 10;
- while (Melder_isHorizontalSpace (*q)) q ++; // skip whitespace before procedure name
- char32 *procName = q;
- while (Melder_staysWithinInk (*q) && *q != U'(' && *q != U':') q ++;
- if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
- if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
- /*
- * We found the procedure definition.
- */
- if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
- Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
- str32cpy (my procedureNames [my callDepth], callName);
- bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
- if (*q) q ++; // step over parenthesis or colon or first white space
- if (! parenthesisOrColonFound) {
- while (Melder_isHorizontalSpace (*q)) q ++; // skip more whitespace
- if (*q == U'(' || *q == U':') q ++; // step over parenthesis or colon
- }
- while (*q && *q != U')') {
- static MelderString argument { };
- MelderString_empty (& argument);
- while (Melder_isHorizontalSpace (*p)) p ++;
- while (Melder_isHorizontalSpace (*q)) q ++;
- conststring32 parameterName = q;
- while (Melder_staysWithinInk (*q) && *q != U',' && *q != U')') q ++; // collect parameter name
- int expressionDepth = 0;
- for (; *p; p ++) {
- if (*p == U',') {
- if (expressionDepth == 0) break; // depth-0 comma ends expression
- MelderString_appendCharacter (& argument, U',');
- } else if (*p == U')') {
- if (expressionDepth == 0) break; // depth-0 closing parenthesis ends expression
- expressionDepth --;
- MelderString_appendCharacter (& argument, U')');
- } else if (*p == U'(') {
- expressionDepth ++;
- MelderString_appendCharacter (& argument, U'(');
- } else if (*p == U'\"') {
- /*
- * Enter a string literal.
- */
- MelderString_appendCharacter (& argument, U'\"');
- p ++;
- for (;; p ++) {
- if (*p == U'\0') {
- Melder_throw (U"Incomplete string literal: the quotes don't match.");
- } else if (*p == U'\"') {
- MelderString_appendCharacter (& argument, U'\"');
- if (p [1] == '\"') {
- p ++; // stay in the string literal
- MelderString_appendCharacter (& argument, U'\"');
- } else {
- break;
- }
- } else {
- MelderString_appendCharacter (& argument, *p);
- }
- }
- } else {
- MelderString_appendCharacter (& argument, *p);
- }
- }
- if (q == parameterName)
- break;
- if (*p) {
- *p = U'\0';
- p ++;
- }
- if (q [-1] == U'$') {
- my callDepth --;
- autostring32 value = Interpreter_stringExpression (me, argument.string);
- my callDepth ++;
- char32 save = *q;
- *q = U'\0';
- InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
- *q = save;
- var -> stringValue = value.move();
- } else if (q [-1] == U'#') {
- if (q [-2] == U'#') {
- MAT value;
- bool owned;
- my callDepth --;
- Interpreter_numericMatrixExpression (me, argument.string, & value, & owned);
- my callDepth ++;
- char32 save = *q;
- *q = U'\0';
- InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
- *q = save;
- NumericMatrixVariable_move (var, value, owned);
- } else {
- VEC value;
- bool owned;
- my callDepth --;
- Interpreter_numericVectorExpression (me, argument.string, & value, & owned);
- my callDepth ++;
- char32 save = *q;
- *q = U'\0';
- InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
- *q = save;
- NumericVectorVariable_move (var, value, owned);
- }
- } else {
- double value;
- my callDepth --;
- Interpreter_numericExpression (me, argument.string, & value);
- my callDepth ++;
- char32 save = *q;
- *q = U'\0';
- InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
- *q = save;
- var -> numericValue = value;
- }
- if (*q)
- q ++; // skip comma
- }
- if (callDepth == Interpreter_MAX_CALL_DEPTH)
- Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
- callStack [++ callDepth] = lineNumber;
- lineNumber = iline;
- break;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
- }
- static void Interpreter_do_oldProcedureCall (Interpreter me, char32 *command,
- char32 * const *lines, integer numberOfLines, integer& lineNumber, integer callStack [], int& callDepth)
- {
- /*
- Old type of procedure calls, with space separation, unquoted strings, and no array support.
- */
- char32 *p = command;
- while (Melder_isHorizontalSpace (*p)) p ++; // skip whitespace
- char32 *callName = p;
- while (*p != U'\0' && ! Melder_isHorizontalSpace (*p) && *p != U'(' && *p != U':') p ++;
- if (p == callName) Melder_throw (U"Missing procedure name after 'call'.");
- bool hasArguments = *p != U'\0';
- *p = U'\0'; // close procedure name
- integer callLength = str32len (callName);
- integer iline = 1;
- for (; iline <= numberOfLines; iline ++) {
- if (! str32nequ (lines [iline], U"procedure ", 10)) continue;
- char32 *q = lines [iline] + 10;
- while (Melder_isHorizontalSpace (*q)) q ++;
- char32 *procName = q;
- while (*q != U'\0' && ! Melder_isHorizontalSpace (*q) && *q != U'(' && *q != U':') q ++;
- if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
- bool hasParameters = ( *q != U'\0' );
- if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
- if (hasArguments && ! hasParameters)
- Melder_throw (U"Call to procedure \"", callName, U"\" has too many arguments.");
- if (hasParameters && ! hasArguments)
- Melder_throw (U"Call to procedure \"", callName, U"\" has too few arguments.");
- if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
- Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
- str32cpy (my procedureNames [my callDepth], callName);
- if (hasParameters) {
- bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
- q ++; // step over parenthesis or colon or first white space
- if (! parenthesisOrColonFound) {
- while (Melder_isHorizontalSpace (*q)) q ++; // skip more whitespace
- if (*q == U'(' || *q == U':') q ++; // step over parenthesis or colon
- }
- ++ p; // first argument
- while (*q && *q != ')') {
- char32 *par, save;
- static MelderString arg { };
- MelderString_empty (& arg);
- while (Melder_isHorizontalSpace (*p)) p ++;
- while (Melder_isHorizontalSpace (*q) || *q == U',' || *q == U')') q ++;
- par = q;
- while (*q != U'\0' && ! Melder_isHorizontalSpace (*q) && *q != U',' && *q != U')') q ++; // collect parameter name
- if (*q) { // does anything follow the parameter name?
- if (*p == U'\"') {
- p ++; // skip initial quote
- while (*p != U'\0') {
- if (*p == U'\"') { // quote signals end-of-string or string-internal quote
- if (p [1] == U'\"') { // double quote signals string-internal quote
- MelderString_appendCharacter (& arg, U'\"');
- p += 2; // skip second quote
- } else { // single quote signals end-of-string
- break;
- }
- } else {
- MelderString_appendCharacter (& arg, *p ++);
- }
- }
- } else {
- while (*p != U'\0' && ! Melder_isHorizontalSpace (*p))
- MelderString_appendCharacter (& arg, *p ++); // white space separates
- }
- if (*p) { *p = U'\0'; p ++; }
- } else { // else rest of line
- while (*p != '\0')
- MelderString_appendCharacter (& arg, *p ++);
- }
- if (q [-1] == '$') {
- save = *q; *q = U'\0';
- InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
- var -> stringValue = Melder_dup_f (arg.string);
- } else {
- double value;
- my callDepth --;
- Interpreter_numericExpression (me, arg.string, & value);
- my callDepth ++;
- save = *q; *q = U'\0';
- InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
- var -> numericValue = value;
- }
- }
- }
- if (callDepth == Interpreter_MAX_CALL_DEPTH)
- Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
- callStack [++ callDepth] = lineNumber;
- lineNumber = iline;
- break;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
- }
- static void assignToNumericVectorElement (Interpreter me, char32 *& p, const char32* vectorName, MelderString& valueString) {
- integer indexValue = 0;
- static MelderString index { };
- MelderString_empty (& index);
- int depth = 0;
- bool inString = false;
- while ((depth > 0 || *p != U']' || inString) && Melder_staysWithinLine (*p)) {
- MelderString_appendCharacter (& index, *p);
- if (*p == U'[') {
- if (! inString) depth ++;
- } else if (*p == U']') {
- if (! inString) depth --;
- }
- if (*p == U'"') inString = ! inString;
- p ++;
- }
- if (! Melder_staysWithinLine (*p))
- Melder_throw (U"Missing closing bracket (]) in array element.");
- Formula_Result result;
- Interpreter_anyExpression (me, index.string, & result);
- if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- indexValue = Melder_iround (result. numericResult);
- } else {
- Melder_throw (U"Element index should be numeric.");
- }
- p ++; // step over closing bracket
- while (Melder_isHorizontalSpace (*p)) p ++;
- if (*p != U'=')
- Melder_throw (U"Missing '=' after vector element ", vectorName, U" [", index.string, U"].");
- p ++; // step over equals sign
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
- if (*p == U'\0') {
- Melder_throw (U"Missing expression after vector element ", vectorName, U" [", index.string, U"].");
- }
- double value;
- if (isCommand (p)) {
- /*
- Get the value of the query.
- */
- MelderString_empty (& valueString);
- autoMelderDivertInfo divert (& valueString);
- MelderString_appendCharacter (& valueString, 1); // will be overwritten by something totally different if any MelderInfo function is called...
- int status = praat_executeCommand (me, p);
- if (status == 0) {
- value = undefined;
- } else if (valueString.string [0] == 1) { // ...not overwritten by any MelderInfo function? then the return value will be the selected object
- int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
- WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
- if (numberOfSelectedObjects > 1)
- Melder_throw (U"Multiple objects selected. Cannot assign object ID to vector element.");
- if (numberOfSelectedObjects == 0)
- Melder_throw (U"No objects selected. Cannot assign object ID to vector element.");
- value = theCurrentPraatObjects -> list [selectedObject]. id;
- } else {
- value = Melder_atof (valueString.string); // including --undefined--
- }
- } else {
- /*
- Get the value of the formula.
- */
- Interpreter_numericExpression (me, p, & value);
- }
- InterpreterVariable var = Interpreter_hasVariable (me, vectorName);
- if (! var)
- Melder_throw (U"Vector ", vectorName, U" does not exist.");
- if (indexValue < 1)
- Melder_throw (U"A vector index cannot be less than 1 (the index you supplied is ", indexValue, U").");
- if (indexValue > var -> numericVectorValue.size)
- Melder_throw (U"A vector index cannot be greater than the number of elements (here ",
- var -> numericVectorValue.size, U"). The index you supplied is ", indexValue, U".");
- var -> numericVectorValue [indexValue] = value;
- }
- static void assignToNumericMatrixElement (Interpreter me, char32 *& p, const char32* matrixName, MelderString& valueString) {
- integer rowNumber = 0, columnNumber = 0;
- /*
- Get the row number.
- */
- static MelderString rowFormula { };
- MelderString_empty (& rowFormula);
- int depth = 0;
- bool inString = false;
- while ((depth > 0 || *p != U',' || inString) && Melder_staysWithinLine (*p)) {
- MelderString_appendCharacter (& rowFormula, *p);
- if (*p == U'[' || *p == U'(') {
- if (! inString) depth ++;
- } else if (*p == U']' || *p == U')') {
- if (! inString) depth --;
- }
- if (*p == U'"') inString = ! inString;
- p ++;
- }
- if (! Melder_staysWithinLine (*p))
- Melder_throw (U"Missing comma in matrix indexing.");
- Formula_Result result;
- Interpreter_anyExpression (me, rowFormula.string, & result);
- if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- rowNumber = Melder_iround (result. numericResult);
- } else {
- Melder_throw (U"Row number should be numeric.");
- }
- p ++; // step over comma
- /*
- Get the column number.
- */
- static MelderString columnFormula { };
- MelderString_empty (& columnFormula);
- depth = 0;
- inString = false;
- while ((depth > 0 || *p != U']' || inString) && Melder_staysWithinLine (*p)) {
- MelderString_appendCharacter (& columnFormula, *p);
- if (*p == U'[') {
- if (! inString) depth ++;
- } else if (*p == U']') {
- if (! inString) depth --;
- }
- if (*p == U'"') inString = ! inString;
- p ++;
- }
- if (! Melder_staysWithinLine (*p))
- Melder_throw (U"Missing closing bracket (]) in matrix indexing.");
- Interpreter_anyExpression (me, columnFormula.string, & result);
- if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- columnNumber = Melder_iround (result. numericResult);
- } else {
- Melder_throw (U"Column number should be numeric.");
- }
- p ++; // step over closing bracket
- while (Melder_isHorizontalSpace (*p)) p ++;
- if (*p != U'=')
- Melder_throw (U"Missing '=' after matrix element ", matrixName, U" [",
- rowFormula.string, U",", columnFormula.string, U"].");
- p ++; // step over equals sign
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
- if (*p == U'\0') {
- Melder_throw (U"Missing expression after matrix element ", matrixName, U" [",
- rowFormula.string, U",", columnFormula.string, U"].");
- }
- double value;
- if (isCommand (p)) {
- /*
- Get the value of the query.
- */
- MelderString_empty (& valueString);
- autoMelderDivertInfo divert (& valueString);
- MelderString_appendCharacter (& valueString, 1); // will be overwritten by something totally different if any MelderInfo function is called...
- int status = praat_executeCommand (me, p);
- if (status == 0) {
- value = undefined;
- } else if (valueString.string [0] == 1) { // ...not overwritten by any MelderInfo function? then the return value will be the selected object
- int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
- WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
- if (numberOfSelectedObjects > 1) {
- Melder_throw (U"Multiple objects selected. Cannot assign object ID to matrix element.");
- } else if (numberOfSelectedObjects == 0) {
- Melder_throw (U"No objects selected. Cannot assign object ID to matrix element.");
- } else {
- value = theCurrentPraatObjects -> list [selectedObject]. id;
- }
- } else {
- value = Melder_atof (valueString.string); // including --undefined--
- }
- } else {
- Interpreter_numericExpression (me, p, & value);
- }
- InterpreterVariable var = Interpreter_hasVariable (me, matrixName);
- if (! var)
- Melder_throw (U"Matrix ", matrixName, U" does not exist.");
- if (rowNumber < 1)
- Melder_throw (U"A row number cannot be less than 1 (the row number you supplied is ", rowNumber, U").");
- if (rowNumber > var -> numericMatrixValue. nrow)
- Melder_throw (U"A row number cannot be greater than the number of rows (here ",
- var -> numericMatrixValue. nrow, U"). The row number you supplied is ", rowNumber, U".");
- if (columnNumber < 1)
- Melder_throw (U"A column number cannot be less than 1 (the column number you supplied is ", columnNumber, U").");
- if (columnNumber > var -> numericMatrixValue. ncol)
- Melder_throw (U"A column number cannot be greater than the number of columns (here ",
- var -> numericMatrixValue. ncol, U"). The column number you supplied is ", columnNumber, U".");
- var -> numericMatrixValue [rowNumber] [columnNumber] = value;
- }
- void Interpreter_run (Interpreter me, char32 *text) {
- autoNUMvector <char32 *> lines; // not autostringvector, because the elements are reference copies
- integer lineNumber = 0;
- bool assertionFailed = false;
- try {
- static MelderString valueString { }; // to divert the info
- static MelderString assertErrorString { };
- char32 *command = text;
- autoMelderString command2;
- autoMelderString buffer;
- integer numberOfLines = 0, assertErrorLineNumber = 0, callStack [1 + Interpreter_MAX_CALL_DEPTH];
- bool atLastLine = false, fromif = false, fromendfor = false;
- int callDepth = 0, chopped = 0, ipar;
- my callDepth = 0;
- /*
- * The "environment" is null if we are in the Praat shell, or an editor otherwise.
- */
- if (my editorClass) {
- praatP. editor = praat_findEditorFromString (my environmentName.get());
- } else {
- praatP. editor = nullptr;
- }
- /*
- * Start.
- */
- my running = true;
- /*
- * Count lines and set the newlines to zero.
- */
- while (! atLastLine) {
- char32 *endOfLine = command;
- while (Melder_staysWithinLine (*endOfLine)) endOfLine ++;
- if (*endOfLine == U'\0') atLastLine = true;
- *endOfLine = U'\0';
- numberOfLines ++;
- command = endOfLine + 1;
- }
- /*
- * Remember line starts and labels.
- */
- lines.reset (1, numberOfLines);
- for (lineNumber = 1, command = text; lineNumber <= numberOfLines; lineNumber ++, command += str32len (command) + 1 + chopped) {
- while (Melder_isHorizontalSpace (*command) || *command == UNICODE_NO_BREAK_SPACE) command ++; // nbsp can occur for scripts copied from the manual
- /*
- * Chop trailing spaces?
- */
- #if 0
- chopped = 0;
- int length = str32len (command);
- while (length > 0) { char kar = command [-- length]; if (! Melder_isHorizontalSpace (kar)) break; command [length] = '\0'; chopped ++; }
- #endif
- lines [lineNumber] = command;
- if (str32nequ (command, U"label ", 6)) {
- for (integer ilabel = 1; ilabel <= my numberOfLabels; ilabel ++)
- if (str32equ (command + 6, my labelNames [ilabel]))
- Melder_throw (U"Duplicate label \"", command + 6, U"\".");
- if (my numberOfLabels >= Interpreter_MAXNUM_LABELS)
- Melder_throw (U"Too many labels.");
- str32ncpy (my labelNames [++ my numberOfLabels], command + 6, 1+Interpreter_MAX_LABEL_LENGTH);
- my labelNames [my numberOfLabels] [Interpreter_MAX_LABEL_LENGTH] = U'\0';
- my labelLines [my numberOfLabels] = lineNumber;
- }
- }
- /*
- * Connect continuation lines.
- */
- trace (U"connect continuation lines");
- for (lineNumber = numberOfLines; lineNumber >= 2; lineNumber --) {
- char32 *line = lines [lineNumber];
- if (line [0] == U'.' && line [1] == U'.' && line [2] == U'.') {
- char32 *previous = lines [lineNumber - 1];
- MelderString_copy (& command2, line + 3);
- MelderString_get (& command2, previous + str32len (previous));
- static char32 emptyLine [] = { U'\0' };
- lines [lineNumber] = emptyLine;
- }
- }
- /*
- * Copy the parameter names and argument values into the array of variables.
- */
- my variablesMap. clear ();
- for (ipar = 1; ipar <= my numberOfParameters; ipar ++) {
- char32 parameter [200];
- /*
- * Create variable names as-are and variable names without capitals.
- */
- str32cpy (parameter, my parameters [ipar]);
- parameterToVariable (me, my types [ipar], parameter, ipar);
- if (parameter [0] >= U'A' && parameter [0] <= U'Z') {
- parameter [0] = Melder_toLowerCase (parameter [0]);
- parameterToVariable (me, my types [ipar], parameter, ipar);
- }
- }
- /*
- * Initialize some variables.
- */
- Interpreter_addStringVariable (me, U"newline$", U"\n");
- Interpreter_addStringVariable (me, U"tab$", U"\t");
- Interpreter_addStringVariable (me, U"shellDirectory$", Melder_getShellDirectory ());
- structMelderDir dir { }; Melder_getDefaultDir (& dir);
- Interpreter_addStringVariable (me, U"defaultDirectory$", Melder_dirToPath (& dir));
- Interpreter_addStringVariable (me, U"preferencesDirectory$", Melder_dirToPath (& praatDir));
- Melder_getHomeDir (& dir);
- Interpreter_addStringVariable (me, U"homeDirectory$", Melder_dirToPath (& dir));
- Melder_getTempDir (& dir);
- Interpreter_addStringVariable (me, U"temporaryDirectory$", Melder_dirToPath (& dir));
- #if defined (macintosh)
- Interpreter_addNumericVariable (me, U"macintosh", 1);
- Interpreter_addNumericVariable (me, U"windows", 0);
- Interpreter_addNumericVariable (me, U"unix", 0);
- #elif defined (_WIN32)
- Interpreter_addNumericVariable (me, U"macintosh", 0);
- Interpreter_addNumericVariable (me, U"windows", 1);
- Interpreter_addNumericVariable (me, U"unix", 0);
- #elif defined (UNIX)
- Interpreter_addNumericVariable (me, U"macintosh", 0);
- Interpreter_addNumericVariable (me, U"windows", 0);
- Interpreter_addNumericVariable (me, U"unix", 1);
- #else
- Interpreter_addNumericVariable (me, U"macintosh", 0);
- Interpreter_addNumericVariable (me, U"windows", 0);
- Interpreter_addNumericVariable (me, U"unix", 0);
- #endif
- Interpreter_addNumericVariable (me, U"left", 1); // to accommodate scripts from before Praat 5.2.06
- Interpreter_addNumericVariable (me, U"right", 2); // to accommodate scripts from before Praat 5.2.06
- Interpreter_addNumericVariable (me, U"mono", 1); // to accommodate scripts from before Praat 5.2.06
- Interpreter_addNumericVariable (me, U"stereo", 2); // to accommodate scripts from before Praat 5.2.06
- Interpreter_addNumericVariable (me, U"all", 0); // to accommodate scripts from before Praat 5.2.06
- Interpreter_addNumericVariable (me, U"average", 0); // to accommodate scripts from before Praat 5.2.06
- #define xstr(s) str(s)
- #define str(s) #s
- Interpreter_addStringVariable (me, U"praatVersion$", U"" xstr(PRAAT_VERSION_STR));
- Interpreter_addNumericVariable (me, U"praatVersion", PRAAT_VERSION_NUM);
- /*
- * Execute commands.
- */
- trace (U"going to handle ", numberOfLines, U" lines");
- //for (lineNumber = 1; lineNumber <= numberOfLines; lineNumber ++) {
- //trace (U"line ", lineNumber, U": ", lines [lineNumber]);
- //}
- for (lineNumber = 1; lineNumber <= numberOfLines; lineNumber ++) {
- if (my stopped) break;
- //trace (U"now at line ", lineNumber, U": ", lines [lineNumber]);
- //for (int lineNumber2 = 1; lineNumber2 <= numberOfLines; lineNumber2 ++) {
- //trace (U" line ", lineNumber2, U": ", lines [lineNumber2]);
- //}
- try {
- char32 c0;
- bool fail = false;
- MelderString_copy (& command2, lines [lineNumber]);
- c0 = command2. string [0];
- if (c0 == U'\0') continue;
- /*
- * Substitute variables.
- */
- trace (U"substituting variables");
- for (char32 *p = & command2. string [0]; *p != U'\0'; p ++) if (*p == U'\'') {
- /*
- * Found a left quote. Search for a matching right quote.
- */
- char32 *q = p + 1, varName [300], *r, *s, *colon;
- integer precision = -1;
- bool percent = false;
- while (*q != U'\0' && *q != U'\'' && q - p < 299) q ++;
- if (*q == U'\0') break; // no matching right quote? done with this line!
- if (q - p == 1 || q - p >= 299) continue; // ignore empty and too long variable names
- trace (U"found ", q - p - 1);
- /*
- * Found a right quote. Get potential variable name.
- */
- for (r = p + 1, s = varName; q - r > 0; r ++, s ++) *s = *r;
- *s = U'\0'; // trailing null byte
- colon = str32chr (varName, U':');
- if (colon) {
- precision = Melder_atoi (colon + 1);
- if (str32chr (colon + 1, U'%')) percent = true;
- *colon = '\0';
- }
- InterpreterVariable var = Interpreter_hasVariable (me, varName);
- if (var) {
- /*
- * Found a variable (p points to the left quote, q to the right quote). Substitute.
- */
- integer headlen = p - command2.string;
- conststring32 string = var -> stringValue ? var -> stringValue.get() :
- percent ? Melder_percent (var -> numericValue, precision) :
- precision >= 0 ? Melder_fixed (var -> numericValue, precision) :
- Melder_double (var -> numericValue);
- integer arglen = str32len (string);
- MelderString_ncopy (& buffer, command2.string, headlen);
- MelderString_append (& buffer, string, q + 1);
- MelderString_copy (& command2, buffer.string); // This invalidates p!! (really bad bug 20070203)
- p = command2.string + headlen + arglen - 1;
- } else {
- p = q - 1; // go to before next quote
- }
- }
- trace (U"resume");
- c0 = command2.string [0]; // resume in order to allow things like 'c$' = 5
- if ((c0 < U'a' || c0 > U'z') && c0 != U'@' && ! (c0 == U'.' && command2.string [1] >= U'a' && command2.string [1] <= U'z')) {
- praat_executeCommand (me, command2.string);
- /*
- * Interpret control flow and variables.
- */
- } else switch (c0) {
- case U'.':
- fail = true;
- break;
- case U'@':
- Interpreter_do_procedureCall (me, command2.string + 1, lines.peek(), numberOfLines, lineNumber, callStack, callDepth);
- break;
- case U'a':
- if (str32nequ (command2.string, U"assert ", 7)) {
- double value;
- Interpreter_numericExpression (me, command2.string + 7, & value);
- if (value == 0.0 || isundef (value)) {
- assertionFailed = true;
- Melder_throw (U"Script assertion fails in line ", lineNumber,
- U" (", value == 0.0 ? U"false" : U"undefined", U"):\n ", command2.string + 7);
- }
- } else if (str32nequ (command2.string, U"asserterror ", 12)) {
- MelderString_copy (& assertErrorString, command2.string + 12);
- assertErrorLineNumber = lineNumber;
- } else fail = true;
- break;
- case U'b':
- fail = true;
- break;
- case U'c':
- if (str32nequ (command2.string, U"call ", 5)) {
- Interpreter_do_oldProcedureCall (me, command2.string + 5, lines.peek(), numberOfLines, lineNumber, callStack, callDepth);
- } else fail = true;
- break;
- case U'd':
- if (str32nequ (command2.string, U"dec ", 4)) {
- InterpreterVariable var = Interpreter_lookUpVariable (me, command2.string + 4);
- var -> numericValue -= 1.0;
- } else fail = true;
- break;
- case U'e':
- if (command2.string [1] == 'n' && command2.string [2] == 'd') {
- if (str32nequ (command2.string, U"endif", 5) && ! Melder_staysWithinInk (command2.string [5])) {
- /* Ignore. */
- } else if (str32nequ (command2.string, U"endfor", 6) && ! Melder_staysWithinInk (command2.string [6])) {
- int depth = 0;
- integer iline;
- for (iline = lineNumber - 1; iline > 0; iline --) {
- char32 *line = lines [iline];
- if (line [0] == U'f' && line [1] == U'o' && line [2] == U'r' && line [3] == U' ') {
- if (depth == 0) { lineNumber = iline - 1; fromendfor = true; break; } // go before 'for'
- else depth --;
- } else if (str32nequ (lines [iline], U"endfor", 6) && ! Melder_staysWithinInk (lines [iline] [6])) {
- depth ++;
- }
- }
- if (iline <= 0) Melder_throw (U"Unmatched 'endfor'.");
- } else if (str32nequ (command2.string, U"endwhile", 8) && ! Melder_staysWithinInk (command2.string [8])) {
- int depth = 0;
- integer iline;
- for (iline = lineNumber - 1; iline > 0; iline --) {
- if (str32nequ (lines [iline], U"while ", 6)) {
- if (depth == 0) { lineNumber = iline - 1; break; } // go before 'while'
- else depth --;
- } else if (str32nequ (lines [iline], U"endwhile", 8) && ! Melder_staysWithinInk (lines [iline] [8])) {
- depth ++;
- }
- }
- if (iline <= 0) Melder_throw (U"Unmatched 'endwhile'.");
- } else if (str32nequ (command2.string, U"endproc", 7) && ! Melder_staysWithinInk (command2.string [7])) {
- if (callDepth == 0) Melder_throw (U"Unmatched 'endproc'.");
- lineNumber = callStack [callDepth --];
- -- my callDepth;
- } else fail = true;
- } else if (str32nequ (command2.string, U"else", 4) && ! Melder_staysWithinInk (command2.string [4])) {
- int depth = 0;
- integer iline;
- for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
- if (str32nequ (lines [iline], U"endif", 5) && ! Melder_staysWithinInk (lines [iline] [5])) {
- if (depth == 0) { lineNumber = iline; break; } // go after `endif`
- else depth --;
- } else if (str32nequ (lines [iline], U"if ", 3)) {
- depth ++;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"Unmatched 'else'.");
- } else if (str32nequ (command2.string, U"elsif ", 6) || str32nequ (command2.string, U"elif ", 5)) {
- if (fromif) {
- double value;
- fromif = false;
- Interpreter_numericExpression (me, command2.string + 5, & value);
- if (value == 0.0) {
- int depth = 0;
- integer iline;
- for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
- if (str32nequ (lines [iline], U"endif", 5) && ! Melder_staysWithinInk (lines [iline] [5])) {
- if (depth == 0) { lineNumber = iline; break; } // go after `endif`
- else depth --;
- } else if (str32nequ (lines [iline], U"else", 4) && ! Melder_staysWithinInk (lines [iline] [4])) {
- if (depth == 0) { lineNumber = iline; break; } // go after `else`
- } else if ((str32nequ (lines [iline], U"elsif", 5) && ! Melder_staysWithinInk (lines [iline] [5]))
- || (str32nequ (lines [iline], U"elif", 4) && ! Melder_staysWithinInk (lines [iline] [4]))) {
- if (depth == 0) { lineNumber = iline - 1; fromif = true; break; } // go at next 'elsif' or 'elif'
- } else if (str32nequ (lines [iline], U"if ", 3)) {
- depth ++;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"Unmatched 'elsif'.");
- }
- } else {
- int depth = 0;
- integer iline;
- for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
- if (str32nequ (lines [iline], U"endif", 5) && ! Melder_staysWithinInk (lines [iline] [5])) {
- if (depth == 0) { lineNumber = iline; break; } // go after `endif`
- else depth --;
- } else if (str32nequ (lines [iline], U"if ", 3)) {
- depth ++;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"'elsif' not matched with 'endif'.");
- }
- } else if (str32nequ (command2.string, U"exit", 4)) {
- if (command2.string [4] == U'\0') {
- lineNumber = numberOfLines; // go after end
- } else if (command2.string [4] == U' ') {
- Melder_throw (command2.string + 5);
- } else fail = true;
- } else if (str32nequ (command2.string, U"echo ", 5)) {
- /*
- * Make sure that lines like "echo = 3" will not be regarded as assignments.
- */
- praat_executeCommand (me, command2.string);
- } else fail = true;
- break;
- case U'f':
- if (command2.string [1] == U'o' && command2.string [2] == U'r' && command2.string [3] == U' ') { // for_
- double toValue, loopVariable;
- char32 *frompos = str32str (command2.string, U" from "), *topos = str32str (command2.string, U" to ");
- char32 *varpos = command2.string + 4, *endvar = frompos;
- if (! topos) Melder_throw (U"Missing \'to\' in \'for\' loop.");
- if (! endvar) endvar = topos;
- while (*endvar == U' ') { *endvar = '\0'; endvar --; }
- while (*varpos == U' ') varpos ++;
- if (endvar - varpos < 0) Melder_throw (U"Missing loop variable after \'for\'.");
- InterpreterVariable var = Interpreter_lookUpVariable (me, varpos);
- Interpreter_numericExpression (me, topos + 4, & toValue);
- if (fromendfor) {
- fromendfor = false;
- loopVariable = var -> numericValue + 1.0;
- } else if (frompos) {
- *topos = '\0';
- Interpreter_numericExpression (me, frompos + 6, & loopVariable);
- } else {
- loopVariable = 1.0;
- }
- var -> numericValue = loopVariable;
- if (loopVariable > toValue) {
- int depth = 0;
- integer iline;
- for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
- if (str32nequ (lines [iline], U"endfor", 6)) {
- if (depth == 0) { lineNumber = iline; break; } // go after 'endfor'
- else depth --;
- } else if (str32nequ (lines [iline], U"for ", 4)) {
- depth ++;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"Unmatched 'for'.");
- }
- } else if (str32nequ (command2.string, U"form", 4) && Melder_isEndOfInk (command2.string [4])) {
- integer iline;
- for (iline = lineNumber + 1; iline <= numberOfLines; iline ++)
- if (str32nequ (lines [iline], U"endform", 7) && Melder_isEndOfInk (lines [iline] [7]))
- { lineNumber = iline; break; } // go after 'endform'
- if (iline > numberOfLines) Melder_throw (U"Unmatched 'form'.");
- } else fail = true;
- break;
- case U'g':
- if (str32nequ (command2.string, U"goto ", 5)) {
- char32 labelName [1+Interpreter_MAX_LABEL_LENGTH];
- str32ncpy (labelName, command2.string + 5, 1+Interpreter_MAX_LABEL_LENGTH);
- labelName [Interpreter_MAX_LABEL_LENGTH] = U'\0';
- char32 *space = str32chr (labelName, U' ');
- if (space == labelName) Melder_throw (U"Missing label name after 'goto'.");
- bool dojump = true;
- if (space) {
- double value;
- *space = '\0';
- Interpreter_numericExpression (me, command2.string + 6 + str32len (labelName), & value);
- if (value == 0.0) dojump = false;
- }
- if (dojump) {
- integer ilabel = lookupLabel (me, labelName);
- lineNumber = my labelLines [ilabel]; // loop will add 1
- }
- } else fail = true;
- break;
- case U'h':
- fail = true;
- break;
- case U'i':
- if (command2.string [1] == U'f' && command2.string [2] == U' ') { // if_
- double value;
- Interpreter_numericExpression (me, command2.string + 3, & value);
- if (value == 0.0) {
- int depth = 0;
- integer iline;
- for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
- if (str32nequ (lines [iline], U"endif", 5)) {
- if (depth == 0) { lineNumber = iline; break; } // go after 'endif'
- else depth --;
- } else if (str32nequ (lines [iline], U"else", 4)) {
- if (depth == 0) { lineNumber = iline; break; } // go after 'else'
- } else if (str32nequ (lines [iline], U"elsif ", 6) || str32nequ (lines [iline], U"elif ", 5)) {
- if (depth == 0) { lineNumber = iline - 1; fromif = true; break; } // go at 'elsif'
- } else if (str32nequ (lines [iline], U"if ", 3)) {
- depth ++;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"Unmatched 'if'.");
- } else if (isundef (value)) {
- Melder_throw (U"The value of the 'if' condition is undefined.");
- }
- } else if (str32nequ (command2.string, U"inc ", 4)) {
- InterpreterVariable var = Interpreter_lookUpVariable (me, command2.string + 4);
- var -> numericValue += 1.0;
- } else fail = true;
- break;
- case U'j':
- fail = true;
- break;
- case U'k':
- fail = true;
- break;
- case U'l':
- if (str32nequ (command2.string, U"label ", 6)) {
- ; // ignore labels
- } else fail = true;
- break;
- case U'm':
- fail = true;
- break;
- case U'n':
- fail = true;
- break;
- case U'o':
- fail = true;
- break;
- case U'p':
- if (str32nequ (command2.string, U"procedure ", 10)) {
- integer iline = lineNumber + 1;
- for (; iline <= numberOfLines; iline ++) {
- if (str32nequ (lines [iline], U"endproc", 7) && ! Melder_staysWithinInk (lines [iline] [7])) {
- lineNumber = iline;
- break;
- } // go after `endproc`
- }
- if (iline > numberOfLines) Melder_throw (U"Unmatched 'procedure'.");
- } else if (str32nequ (command2.string, U"print", 5)) {
- /*
- * Make sure that lines like "print = 3" will not be regarded as assignments.
- */
- if (command2.string [5] == U' ' || (str32nequ (command2.string + 5, U"line", 4) && (command2.string [9] == U' ' || command2.string [9] == U'\0'))) {
- praat_executeCommand (me, command2.string);
- } else fail = true;
- } else fail = true;
- break;
- case U'q':
- fail = true;
- break;
- case U'r':
- if (str32nequ (command2.string, U"repeat", 6) && ! Melder_staysWithinInk (command2.string [6])) {
- /* Ignore. */
- } else fail = true;
- break;
- case U's':
- if (str32nequ (command2.string, U"stopwatch", 9) && ! Melder_staysWithinInk (command2.string [9])) {
- (void) Melder_stopwatch (); // reset stopwatch
- } else fail = true;
- break;
- case U't':
- fail = true;
- break;
- case U'u':
- if (str32nequ (command2.string, U"until ", 6)) {
- double value;
- Interpreter_numericExpression (me, command2.string + 6, & value);
- if (value == 0.0) {
- int depth = 0;
- integer iline = lineNumber - 1;
- for (; iline > 0; iline --) {
- if (str32nequ (lines [iline], U"repeat", 6) && ! Melder_staysWithinInk (lines [iline] [6])) {
- if (depth == 0) { lineNumber = iline; break; } // go after `repeat`
- else depth --;
- } else if (str32nequ (lines [iline], U"until ", 6)) {
- depth ++;
- }
- }
- if (iline <= 0) Melder_throw (U"Unmatched 'until'.");
- }
- } else fail = true;
- break;
- case U'v':
- fail = true;
- break;
- case U'w':
- if (str32nequ (command2.string, U"while ", 6)) {
- double value;
- Interpreter_numericExpression (me, command2.string + 6, & value);
- if (value == 0.0) {
- int depth = 0;
- integer iline = lineNumber + 1;
- for (; iline <= numberOfLines; iline ++) {
- if (str32nequ (lines [iline], U"endwhile", 8) && ! Melder_staysWithinInk (lines [iline] [8])) {
- if (depth == 0) { lineNumber = iline; break; } // go after `endwhile`
- else depth --;
- } else if (str32nequ (lines [iline], U"while ", 6)) {
- depth ++;
- }
- }
- if (iline > numberOfLines) Melder_throw (U"Unmatched 'while'.");
- }
- } else fail = true;
- break;
- case U'x':
- fail = true;
- break;
- case U'y':
- fail = true;
- break;
- case U'z':
- fail = true;
- break;
- default: break;
- }
- if (fail) {
- /*
- Found an unknown word starting with a lower-case letter, optionally preceded by a period.
- See whether the word is a variable name.
- */
- trace (U"found an unknown word starting with a lower-case letter, optionally preceded by a period");
- char32 *p = & command2.string [0];
- /*
- Variable names consist of a sequence of letters, digits, and underscores,
- optionally preceded by a period and optionally followed by a $ and/or #.
- */
- if (*p == U'.') p ++;
- while (Melder_isWordCharacter (*p) || *p == U'.') p ++;
- if (*p == U'$') {
- /*
- Assign to a string variable.
- */
- trace (U"detected an assignment to a string variable");
- char32 *endOfVariable = ++ p;
- char32 *variableName = command2.string;
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after variable name
- if (*p == U'[') {
- /*
- This must be an assignment to an indexed string variable.
- */
- *endOfVariable = U'\0';
- static MelderString indexedVariableName { };
- MelderString_copy (& indexedVariableName, command2.string, U"[");
- for (;;) {
- p ++; // skip opening bracket or comma
- static MelderString index { };
- MelderString_empty (& index);
- int depth = 0;
- bool inString = false;
- while ((depth > 0 || (*p != U',' && *p != U']') || inString) && Melder_staysWithinLine (*p)) {
- MelderString_appendCharacter (& index, *p);
- if (*p == U'[') {
- if (! inString)
- depth ++;
- } else if (*p == U']') {
- if (! inString)
- depth --;
- }
- if (*p == U'"')
- inString = ! inString;
- p ++;
- }
- if (! Melder_staysWithinLine (*p))
- Melder_throw (U"Missing closing bracket (]) in indexed variable.");
- Formula_Result result;
- Interpreter_anyExpression (me, index.string, & result);
- if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- double numericIndexValue = result. numericResult;
- MelderString_append (& indexedVariableName, numericIndexValue);
- } else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
- MelderString_append (& indexedVariableName, U"\"", result. stringResult.get(), U"\"");
- }
- MelderString_appendCharacter (& indexedVariableName, *p);
- if (*p == U']') {
- break;
- }
- }
- variableName = indexedVariableName.string;
- p ++; // skip closing bracket
- }
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after (perhaps indexed) variable name
- int typeOfAssignment; // 0, 1, 2, 3 or 4
- if (*p == U'=') {
- typeOfAssignment = 0; // assignment
- } else if (*p == U'+') {
- if (p [1] == U'=') {
- typeOfAssignment = 1; // adding assignment
- p ++;
- } else {
- Melder_throw (U"Missing \"=\", \"+=\", \"<\", or \">\" after variable ", variableName, U".");
- }
- } else if (*p == U'<') {
- typeOfAssignment = 2; // read from file
- } else if (*p == U'>') {
- if (p [1] == U'>') {
- typeOfAssignment = 3; // append to file
- p ++;
- } else {
- typeOfAssignment = 4; // save to file
- }
- } else Melder_throw (U"Missing \"=\", \"+=\", \"<\", or \">\" after variable ", variableName, U".");
- *endOfVariable = U'\0';
- p ++;
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment or I/O symbol
- if (*p == U'\0') {
- if (typeOfAssignment >= 2)
- Melder_throw (U"Missing file name after variable ", variableName, U".");
- else
- Melder_throw (U"Missing expression after variable ", variableName, U".");
- }
- if (typeOfAssignment >= 2) {
- structMelderFile file { };
- Melder_relativePathToFile (p, & file);
- if (typeOfAssignment == 2) {
- autostring32 stringValue = MelderFile_readText (& file);
- InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
- var -> stringValue = stringValue.move();
- } else if (typeOfAssignment == 3) {
- if (theCurrentPraatObjects != & theForegroundPraatObjects) Melder_throw (U"Commands that write to a file are not available inside pictures.");
- InterpreterVariable var = Interpreter_hasVariable (me, variableName);
- if (! var) Melder_throw (U"Variable ", variableName, U" undefined.");
- MelderFile_appendText (& file, var -> stringValue.get());
- } else {
- if (theCurrentPraatObjects != & theForegroundPraatObjects) Melder_throw (U"Commands that write to a file are not available inside pictures.");
- InterpreterVariable var = Interpreter_hasVariable (me, variableName);
- if (! var) Melder_throw (U"Variable ", variableName, U" undefined.");
- MelderFile_writeText (& file, var -> stringValue.get(), Melder_getOutputEncoding ());
- }
- } else if (isCommand (p)) {
- /*
- Statement like: name$ = Get name
- */
- MelderString_empty (& valueString); // empty because command may print nothing; also makes sure that valueString.string exists
- autoMelderDivertInfo divert (& valueString);
- int status = praat_executeCommand (me, p);
- InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
- var -> stringValue = Melder_dup (status ? valueString.string : U"");
- } else {
- /*
- Evaluate a string expression and assign the result to the variable.
- Statements like:
- sentence$ = subject$ + verb$ + object$
- extension$ = if index (file$, ".") <> 0
- ... then right$ (file$, length (file$) - rindex (file$, "."))
- ... else "" fi
- */
- trace (U"evaluating string expression");
- autostring32 stringValue = Interpreter_stringExpression (me, p);
- trace (U"assigning to string variable ", variableName);
- if (typeOfAssignment == 1) {
- InterpreterVariable var = Interpreter_hasVariable (me, variableName);
- if (! var)
- Melder_throw (U"The string ", variableName, U" does not exist.\n"
- U"You can increment (+=) only existing strings.");
- integer oldLength = str32len (var -> stringValue.get()), extraLength = str32len (stringValue.get());
- autostring32 newString = autostring32 (oldLength + extraLength, false);
- str32cpy (newString.get(), var -> stringValue.get());
- str32cpy (newString.get() + oldLength, stringValue.get());
- var -> stringValue = newString.move();
- } else {
- InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
- var -> stringValue = stringValue.move();
- }
- }
- } else if (*p == U'#') {
- if (p [1] == U'#') {
- /*
- Assign to a numeric matrix variable or to a matrix element.
- */
- static MelderString matrixName { };
- p ++; // go to second '#'
- *p = U'\0'; // erase the last number sign temporarily
- MelderString_copy (& matrixName, command2.string, U'#');
- *p = U'#'; // put the number sign back
- p ++; // step over last number sign
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after matrix name
- if (*p == U'=') {
- /*
- This must be an assignment to a matrix variable.
- */
- p ++; // step over equals sign
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
- if (*p == U'\0')
- Melder_throw (U"Missing right-hand expression in assignment to matrix ", matrixName.string, U".");
- if (isCommand (p)) {
- /*
- Statement like: values## = Get all values
- */
- praat_executeCommand (me, p);
- InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
- var -> numericMatrixValue = theInterpreterNummat.move();
- } else {
- MAT value;
- bool owned;
- Interpreter_numericMatrixExpression (me, p, & value, & owned);
- InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
- NumericMatrixVariable_move (var, value, owned);
- }
- } else if (*p == U'[') {
- assignToNumericMatrixElement (me, ++ p, matrixName.string, valueString);
- } else if (*p == U'+' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
- if (! var)
- Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
- U"You can increment (+=) only existing matrices.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
- NumericMatrixVariable_add (var, result. numericMatrixResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericMatrixVariable_add (var, result. numericResult);
- } else {
- Melder_throw (U"You can increment (+=) a numeric matrix only with a number or another numeric matrix.");
- }
- } else if (*p == U'-' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
- if (! var)
- Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
- U"You can decrement (-=) only existing matrices.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
- NumericMatrixVariable_subtract (var, result. numericMatrixResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericMatrixVariable_subtract (var, result. numericResult);
- } else {
- Melder_throw (U"You can decrement (-=) a numeric matrix only with a number or another numeric matrix.");
- }
- } else if (*p == U'*' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
- if (! var)
- Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
- U"You can multiply (*=) only existing matrices.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
- NumericMatrixVariable_multiply (var, result. numericMatrixResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericMatrixVariable_multiply (var, result. numericResult);
- } else {
- Melder_throw (U"You can multiply (*=) a numeric matrix only with a number or another numeric matrix.");
- }
- } else if (*p == U'/' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
- if (! var)
- Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
- U"You can divide (/=) only existing matrices.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
- NumericMatrixVariable_divide (var, result. numericMatrixResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericMatrixVariable_divide (var, result. numericResult);
- } else {
- Melder_throw (U"You can divide (/=) a numeric matrix only with a number or another numeric matrix.");
- }
- } else if (*p == U'~') {
- /*
- This must be a formula assignment to a matrix variable.
- */
- p ++; // step over tilde
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
- if (*p == U'\0')
- Melder_throw (U"Missing formula expression for matrix ", matrixName.string, U".");
- InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
- if (! var)
- Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
- "You can assign a formula only to an existing matrix.");
- static Matrix matrixObject;
- if (! matrixObject)
- matrixObject = Matrix_createSimple (1, 1). releaseToAmbiguousOwner(); // prevent exit-time destruction
- MAT mat = var -> numericMatrixValue.get();
- matrixObject -> xmax = mat.ncol + 0.5;
- matrixObject -> nx = mat.ncol;
- matrixObject -> ymax = mat.nrow + 0.5;
- matrixObject -> ny = mat.nrow;
- matrixObject -> z.at = mat.at; // just a reference (YUCK)
- matrixObject -> z.nrow = mat.nrow;
- matrixObject -> z.ncol = mat.ncol;
- Matrix_formula (matrixObject, p, me, nullptr);
- } else Melder_throw (U"Missing '=' after matrix variable ", matrixName.string, U".");
- } else {
- /*
- Assign to a numeric vector variable or to a vector element.
- */
- static MelderString vectorName { };
- *p = U'\0'; // erase the number sign temporarily
- MelderString_copy (& vectorName, command2.string, U"#");
- *p = U'#'; // put the number sign back
- p ++; // step over number sign
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after array name
- if (*p == U'=') {
- /*
- This must be an assignment to a vector variable.
- */
- p ++; // step over equals sign
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
- if (*p == U'\0')
- Melder_throw (U"Missing right-hand expression in assignment to vector ", vectorName.string, U".");
- if (isCommand (p)) {
- /*
- Statement like: times# = Get all times
- */
- praat_executeCommand (me, p);
- InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
- var -> numericVectorValue = theInterpreterNumvec.move();
- } else {
- VEC value;
- bool owned;
- Interpreter_numericVectorExpression (me, p, & value, & owned);
- InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
- NumericVectorVariable_move (var, value, owned);
- }
- } else if (*p == U'[') {
- assignToNumericVectorElement (me, ++ p, vectorName.string, valueString);
- } else if (*p == U'+' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
- if (! var)
- Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
- U"You can increment (+=) only existing vectors.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
- NumericVectorVariable_add (var, result. numericVectorResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericVectorVariable_add (var, result. numericResult);
- } else {
- Melder_throw (U"You can increment (+=) a numeric vector only with a number or another numeric vector.");
- }
- } else if (*p == U'-' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
- if (! var)
- Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
- U"You can decrement (-=) only existing vectors.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
- NumericVectorVariable_subtract (var, result. numericVectorResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericVectorVariable_subtract (var, result. numericResult);
- } else {
- Melder_throw (U"You can decrement (-=) a numeric vector only with a number or another numeric vector.");
- }
- } else if (*p == U'*' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
- if (! var)
- Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
- U"You can multiply (*=) only existing vectors.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
- NumericVectorVariable_multiply (var, result. numericVectorResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericVectorVariable_multiply (var, result. numericResult);
- } else {
- Melder_throw (U"You can multiply (*=) a numeric vector only with a number or another numeric vector.");
- }
- } else if (*p == U'/' && p [1] == U'=') {
- InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
- if (! var)
- Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
- U"You can divide (/=) only existing vectors.");
- Formula_Result result;
- Interpreter_anyExpression (me, p += 2, & result);
- if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
- NumericVectorVariable_divide (var, result. numericVectorResult);
- } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- NumericVectorVariable_divide (var, result. numericResult);
- } else {
- Melder_throw (U"You can divide (/=) a numeric vector only with a number or another numeric vector.");
- }
- } else if (*p == U'~') {
- /*
- This must be a formula assignment to a vector variable.
- */
- p ++; // step over tilde
- while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
- if (*p == U'\0')
- Melder_throw (U"Missing formula expression for vector ", vectorName.string, U".");
- InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
- if (! var)
- Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
- "You can assign a formula only to an existing vector.");
- static Matrix vectorObject;
- if (! vectorObject) {
- vectorObject = Matrix_createSimple (1, 1). releaseToAmbiguousOwner(); // prevent destruction when program ends
- }
- VEC vec = var -> numericVectorValue.get();
- //vectorObject -> xmin = 0.5;
- vectorObject -> xmax = vec.size + 0.5;
- vectorObject -> nx = vec.size;
- vectorObject -> z.at [1] = vec.at;
- //vectorObject -> z.nrow = 1;
- vectorObject -> z.ncol = vec.size;
- Matrix_formula (vectorObject, p, me, nullptr);
- } else Melder_throw (U"Missing '=' or '+=' or '[' or '~' after vector variable ", vectorName.string, U".");
- }
- } else {
- /*
- * Try to assign to a numeric variable.
- */
- double value;
- char32 *variableName = command2.string;
- int typeOfAssignment = 0; // plain assignment
- if (*p == U'\0') {
- /*
- * Command ends here: it may be a PraatShell command.
- */
- praat_executeCommand (me, command2.string);
- continue; // next line
- }
- char32 *endOfVariable = p;
- while (Melder_isHorizontalSpace (*p)) p ++;
- if (*p == U'=' || ((*p == U'+' || *p == U'-' || *p == U'*' || *p == U'/') && p [1] == U'=')) {
- /*
- This must be an assignment (though: "echo = ..." ???)
- */
- typeOfAssignment = *p == U'+' ? 1 : *p == U'-' ? 2 : *p == U'*' ? 3 : *p == U'/' ? 4 : 0;
- *endOfVariable = U'\0'; // close variable name; FIXME: this can be any weird character, e.g. hallo&
- } else if (*p == U'[') {
- /*
- This must be an assignment to an indexed numeric variable.
- */
- *endOfVariable = U'\0';
- static MelderString indexedVariableName { };
- MelderString_copy (& indexedVariableName, command2.string, U"[");
- for (;;) {
- p ++; // skip opening bracket or comma
- static MelderString index { };
- MelderString_empty (& index);
- int depth = 0;
- bool inString = false;
- while ((depth > 0 || (*p != U',' && *p != U']') || inString) && Melder_staysWithinLine (*p)) {
- MelderString_appendCharacter (& index, *p);
- if (*p == U'[') {
- if (! inString)
- depth ++;
- } else if (*p == U']') {
- if (! inString)
- depth --;
- }
- if (*p == U'"')
- inString = ! inString;
- p ++;
- }
- if (! Melder_staysWithinLine (*p))
- Melder_throw (U"Missing closing bracket (]) in indexed variable.");
- Formula_Result result;
- Melder_assert (! result. stringResult);
- Interpreter_anyExpression (me, index.string, & result);
- if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
- double numericIndexValue = result. numericResult;
- MelderString_append (& indexedVariableName, numericIndexValue);
- } else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
- MelderString_append (& indexedVariableName, U"\"", result. stringResult.get(), U"\"");
- }
- MelderString_appendCharacter (& indexedVariableName, *p);
- if (*p == U']')
- break;
- }
- variableName = indexedVariableName.string;
- p ++; // skip closing bracket
- while (Melder_isHorizontalSpace (*p)) p ++;
- if (*p == U'=' || ((*p == U'+' || *p == U'-' || *p == U'*' || *p == U'/') && p [1] == U'=')) {
- typeOfAssignment = *p == U'+' ? 1 : *p == U'-' ? 2 : *p == U'*' ? 3 : *p == U'/' ? 4 : 0;
- }
- } else {
- /*
- Not an assignment: perhaps a PraatShell command (select, echo, execute, pause ...).
- */
- praat_executeCommand (me, variableName);
- continue; // next line
- }
- p += typeOfAssignment == 0 ? 1 : 2;
- while (Melder_isHorizontalSpace (*p)) p ++;
- if (*p == U'\0') Melder_throw (U"Missing expression after variable ", variableName, U".");
- /*
- Three classes of assignments:
- var = formula
- var = Query
- var = Object creation
- */
- if (isCommand (p)) {
- /*
- Get the value of the query.
- */
- MelderString_empty (& valueString);
- autoMelderDivertInfo divert (& valueString);
- MelderString_appendCharacter (& valueString, 1); // will be overwritten by something totally different if any MelderInfo function is called...
- int status = praat_executeCommand (me, p);
- if (status == 0) {
- value = undefined;
- } else if (valueString.string [0] == 1) { // ...not overwritten by any MelderInfo function? then the return value will be the selected object
- int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
- WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
- if (numberOfSelectedObjects > 1) {
- Melder_throw (U"Multiple objects selected. Cannot assign object ID to variable.");
- } else if (numberOfSelectedObjects == 0) {
- Melder_throw (U"No objects selected. Cannot assign object ID to variable.");
- } else {
- value = theCurrentPraatObjects -> list [selectedObject]. id;
- }
- } else {
- value = Melder_atof (valueString.string); // including --undefined--
- }
- } else {
- /*
- Get the value of the formula.
- */
- Interpreter_numericExpression (me, p, & value);
- }
- /*
- * Assign the value to a variable.
- */
- if (typeOfAssignment == 0) {
- /*
- Use an existing variable, or create a new one.
- */
- //Melder_casual (U"looking up variable ", variableName);
- InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
- var -> numericValue = value;
- } else {
- /*
- Modify an existing variable.
- */
- InterpreterVariable var = Interpreter_hasVariable (me, variableName);
- if (! var) Melder_throw (U"The variable ", variableName, U" does not exist. You can modify only existing variables.");
- if (isundef (var -> numericValue)) {
- /* Keep it that way. */
- } else {
- if (typeOfAssignment == 1) {
- var -> numericValue += value;
- } else if (typeOfAssignment == 2) {
- var -> numericValue -= value;
- } else if (typeOfAssignment == 3) {
- var -> numericValue *= value;
- } else if (value == 0) {
- var -> numericValue = undefined;
- } else {
- var -> numericValue /= value;
- }
- }
- }
- }
- } // endif fail
- if (assertErrorLineNumber != 0 && assertErrorLineNumber != lineNumber) {
- integer save_assertErrorLineNumber = assertErrorLineNumber;
- assertErrorLineNumber = 0;
- Melder_throw (U"Script assertion fails in line ", save_assertErrorLineNumber,
- U": error « ", assertErrorString.string, U" » not raised. Instead: no error.");
-
- }
- } catch (MelderError) {
- // Melder_casual (U"Error: << ", Melder_getError(),
- // U" >>\nassertErrorLineNumber: ", assertErrorLineNumber,
- // U"\nlineNumber: ", lineNumber,
- // U"\nAssert error string: << ", assertErrorString.string,
- // U" >>\n"
- // );
- if (assertErrorLineNumber == 0) {
- throw;
- } else if (assertErrorLineNumber != lineNumber) {
- if (str32str (Melder_getError (), assertErrorString.string)) {
- Melder_clearError ();
- assertErrorLineNumber = 0;
- } else {
- autostring32 errorCopy_nothrow = Melder_dup_f (Melder_getError ());
- Melder_clearError ();
- Melder_throw (U"Script assertion fails in line ", assertErrorLineNumber,
- U": error « ", assertErrorString.string, U" » not raised. Instead:\n",
- errorCopy_nothrow.get());
- }
- }
- }
- } // endfor lineNumber
- my numberOfLabels = 0;
- my running = false;
- my stopped = false;
- } catch (MelderError) {
- if (lineNumber > 0) {
- bool normalExplicitExit = str32nequ (lines [lineNumber], U"exit ", 5) || Melder_hasError (U"Script exited.");
- if (! normalExplicitExit && ! assertionFailed) { // don't show the message twice!
- while (lines [lineNumber] [0] == U'\0') { // did this use to be a continuation line?
- lineNumber --;
- Melder_assert (lineNumber > 0); // originally empty lines that stayed empty should not generate errors
- }
- Melder_appendError (U"Script line ", lineNumber, U" not performed or completed:\n« ", lines [lineNumber], U" »");
- }
- }
- my numberOfLabels = 0;
- my running = false;
- my stopped = false;
- if (str32equ (Melder_getError (), U"\nScript exited.\n")) {
- Melder_clearError ();
- } else {
- throw;
- }
- }
- }
- void Interpreter_stop (Interpreter me) {
- //Melder_casual (U"Interpreter_stop in: ", Melder_pointer (me));
- my stopped = true;
- //Melder_casual (U"Interpreter_stop out: ", Melder_pointer (me));
- }
- void Interpreter_voidExpression (Interpreter me, conststring32 expression) {
- Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC, false);
- Formula_Result result;
- Formula_run (0, 0, & result);
- }
- void Interpreter_numericExpression (Interpreter me, conststring32 expression, double *p_value) {
- Melder_assert (p_value);
- if (str32str (expression, U"(=")) {
- *p_value = Melder_atof (expression);
- } else {
- Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC, false);
- Formula_Result result;
- Formula_run (0, 0, & result);
- *p_value = result. numericResult;
- }
- }
- void Interpreter_numericVectorExpression (Interpreter me, conststring32 expression, VEC *p_value, bool *p_owned) {
- Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR, false);
- Formula_Result result;
- Formula_run (0, 0, & result);
- *p_value = result. numericVectorResult;
- *p_owned = result. owned;
- result. owned = false;
- }
- void Interpreter_numericMatrixExpression (Interpreter me, conststring32 expression, MAT *p_value, bool *p_owned) {
- Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX, false);
- Formula_Result result;
- Formula_run (0, 0, & result);
- *p_value = result. numericMatrixResult;
- *p_owned = result. owned;
- result. owned = false;
- }
- autostring32 Interpreter_stringExpression (Interpreter me, conststring32 expression) {
- Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_STRING, false);
- Formula_Result result;
- Formula_run (0, 0, & result);
- return result. stringResult.move();
- }
- void Interpreter_anyExpression (Interpreter me, conststring32 expression, Formula_Result *p_result) {
- Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_UNKNOWN, false);
- Formula_run (0, 0, p_result);
- }
- /* End of file Interpreter.cpp */
|