Interpreter.cpp 103 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466
  1. /* Interpreter.cpp
  2. *
  3. * Copyright (C) 1993-2018 Paul Boersma
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <ctype.h>
  19. #include "Interpreter.h"
  20. #include "praatP.h"
  21. extern structMelderDir praatDir;
  22. #include "praat_script.h"
  23. #include "Formula.h"
  24. #include "praat_version.h"
  25. #include "../kar/UnicodeData.h"
  26. #include "../fon/Vector.h"
  27. #define Interpreter_WORD 1
  28. #define Interpreter_REAL 2
  29. #define Interpreter_POSITIVE 3
  30. #define Interpreter_INTEGER 4
  31. #define Interpreter_NATURAL 5
  32. #define Interpreter_BOOLEAN 6
  33. #define Interpreter_SENTENCE 7
  34. #define Interpreter_TEXT 8
  35. #define Interpreter_CHOICE 9
  36. #define Interpreter_OPTIONMENU 10
  37. #define Interpreter_BUTTON 11
  38. #define Interpreter_OPTION 12
  39. #define Interpreter_COMMENT 13
  40. autoVEC theInterpreterNumvec;
  41. autoMAT theInterpreterNummat;
  42. Thing_implement (InterpreterVariable, SimpleString, 0);
  43. static autoInterpreterVariable InterpreterVariable_create (conststring32 key) {
  44. try {
  45. if (key [0] == U'e' && key [1] == U'\0')
  46. Melder_throw (U"You cannot use 'e' as the name of a variable (e is the constant 2.71...).");
  47. if (key [0] == U'p' && key [1] == U'i' && key [2] == U'\0')
  48. Melder_throw (U"You cannot use 'pi' as the name of a variable (pi is the constant 3.14...).");
  49. 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' &&
  50. key [6] == U'n' && key [7] == U'e' && key [8] == U'd' && key [9] == U'\0')
  51. Melder_throw (U"You cannot use 'undefined' as the name of a variable.");
  52. autoInterpreterVariable me = Thing_new (InterpreterVariable);
  53. my string = Melder_dup (key);
  54. return me;
  55. } catch (MelderError) {
  56. Melder_throw (U"Interpreter variable not created.");
  57. }
  58. }
  59. Thing_implement (Interpreter, Thing, 0);
  60. autoInterpreter Interpreter_create (conststring32 environmentName, ClassInfo editorClass) {
  61. try {
  62. autoInterpreter me = Thing_new (Interpreter);
  63. my variablesMap. max_load_factor (0.65f);
  64. my environmentName = Melder_dup (environmentName);
  65. my editorClass = editorClass;
  66. return me;
  67. } catch (MelderError) {
  68. Melder_throw (U"Interpreter not created.");
  69. }
  70. }
  71. autoInterpreter Interpreter_createFromEnvironment (Editor editor) {
  72. if (! editor) return Interpreter_create (nullptr, nullptr);
  73. return Interpreter_create (editor -> name.get(), editor -> classInfo);
  74. }
  75. void Melder_includeIncludeFiles (autostring32 *inout_text) {
  76. for (int depth = 0; ; depth ++) {
  77. char32 *head = inout_text->get();
  78. integer numberOfIncludes = 0;
  79. if (depth > 10)
  80. Melder_throw (U"Include files nested too deep. Probably cyclic.");
  81. for (;;) {
  82. char32 *includeLocation, *includeFileName, *tail;
  83. integer headLength, includeTextLength, newLength;
  84. /*
  85. Look for an include statement. If not found, we have finished.
  86. */
  87. includeLocation = ( str32nequ (head, U"include ", 8) ? head : str32str (head, U"\ninclude ") );
  88. if (! includeLocation)
  89. break;
  90. if (includeLocation != head)
  91. includeLocation += 1;
  92. numberOfIncludes += 1;
  93. /*
  94. Separate out the head.
  95. */
  96. *includeLocation = U'\0';
  97. /*
  98. Separate out the name of the include file.
  99. */
  100. includeFileName = includeLocation + 8;
  101. while (Melder_isHorizontalSpace (*includeFileName)) includeFileName ++;
  102. tail = includeFileName;
  103. while (Melder_staysWithinLine (*tail)) tail ++;
  104. if (*tail != U'\0') {
  105. *tail = U'\0';
  106. tail += 1;
  107. }
  108. /*
  109. Get the contents of the include file.
  110. */
  111. structMelderFile includeFile { };
  112. Melder_relativePathToFile (includeFileName, & includeFile);
  113. autostring32 includeText;
  114. try {
  115. includeText = MelderFile_readText (& includeFile);
  116. } catch (MelderError) {
  117. Melder_throw (U"Include file ", & includeFile, U" not read.");
  118. }
  119. /*
  120. Construct the new text.
  121. */
  122. headLength = (head - inout_text->get()) + str32len (head);
  123. includeTextLength = str32len (includeText.get());
  124. newLength = headLength + includeTextLength + 1 + str32len (tail);
  125. autostring32 newText (newLength);
  126. str32cpy (newText.get(), inout_text->get());
  127. str32cpy (newText.get() + headLength, includeText.get());
  128. str32cpy (newText.get() + headLength + includeTextLength, U"\n");
  129. str32cpy (newText.get() + headLength + includeTextLength + 1, tail);
  130. /*
  131. Replace the old text with the new. This will work even within an autostring.
  132. */
  133. *inout_text = newText.move();
  134. /*
  135. Cycle.
  136. */
  137. head = inout_text->get() + headLength + includeTextLength + 1;
  138. }
  139. if (numberOfIncludes == 0) break;
  140. }
  141. }
  142. integer Interpreter_readParameters (Interpreter me, mutablestring32 text) {
  143. char32 *formLocation = nullptr;
  144. integer npar = 0;
  145. my dialogTitle [0] = U'\0';
  146. /*
  147. Look for a "form" line.
  148. */
  149. {// scope
  150. char32 *p = & text [0];
  151. for (;;) {
  152. /*
  153. Invariant here: we are at the beginning of a line.
  154. */
  155. Melder_skipHorizontalSpace (& p);
  156. if (str32nequ (p, U"form", 4) && Melder_isEndOfInk (p [4])) {
  157. formLocation = p;
  158. break;
  159. }
  160. Melder_skipToEndOfLine (& p);
  161. if (*p == U'\0') break;
  162. p ++; // skip newline symbol
  163. }
  164. }
  165. /*
  166. If there is no "form" line, there are no parameters.
  167. */
  168. if (formLocation) {
  169. char32 *dialogTitle = Melder_findEndOfHorizontalSpace (formLocation + 4);
  170. char32 *endOfLine = Melder_findEndOfLine (dialogTitle);
  171. if (*endOfLine == U'\0')
  172. Melder_throw (U"Unfinished form (only a \"form\" line).");
  173. *endOfLine = U'\0'; // destroy input temporarily in order to limit copying of dialog title
  174. str32ncpy (my dialogTitle, dialogTitle, Interpreter_MAX_DIALOG_TITLE_LENGTH);
  175. *endOfLine = U'\n'; // restore input
  176. my numberOfParameters = 0;
  177. while (true) {
  178. int type = 0;
  179. char32 *startOfLine = Melder_findEndOfHorizontalSpace (endOfLine + 1);
  180. /*
  181. Skip empty lines and lines with comments.
  182. */
  183. while (*startOfLine == U'#' || *startOfLine == U';' || *startOfLine == U'!' || Melder_isEndOfLine (*startOfLine)) {
  184. endOfLine = Melder_findEndOfLine (startOfLine);
  185. if (Melder_isEndOfText (*endOfLine))
  186. Melder_throw (U"Unfinished form (missing \"endform\").");
  187. startOfLine = Melder_findEndOfHorizontalSpace (endOfLine + 1);
  188. }
  189. if (str32nequ (startOfLine, U"endform", 7) && Melder_isEndOfInk (startOfLine [7])) break;
  190. char32 *parameterLocation;
  191. if (str32nequ (startOfLine, U"word", 4) && Melder_isEndOfInk (startOfLine [4]))
  192. { type = Interpreter_WORD; parameterLocation = startOfLine + 4; }
  193. else if (str32nequ (startOfLine, U"real", 4) && Melder_isEndOfInk (startOfLine [4]))
  194. { type = Interpreter_REAL; parameterLocation = startOfLine + 4; }
  195. else if (str32nequ (startOfLine, U"positive", 8) && Melder_isEndOfInk (startOfLine [8]))
  196. { type = Interpreter_POSITIVE; parameterLocation = startOfLine + 8; }
  197. else if (str32nequ (startOfLine, U"integer", 7) && Melder_isEndOfInk (startOfLine [7]))
  198. { type = Interpreter_INTEGER; parameterLocation = startOfLine + 7; }
  199. else if (str32nequ (startOfLine, U"natural", 7) && Melder_isEndOfInk (startOfLine [7]))
  200. { type = Interpreter_NATURAL; parameterLocation = startOfLine + 7; }
  201. else if (str32nequ (startOfLine, U"boolean", 7) && Melder_isEndOfInk (startOfLine [7]))
  202. { type = Interpreter_BOOLEAN; parameterLocation = startOfLine + 7; }
  203. else if (str32nequ (startOfLine, U"sentence", 8) && Melder_isEndOfInk (startOfLine [8]))
  204. { type = Interpreter_SENTENCE; parameterLocation = startOfLine + 8; }
  205. else if (str32nequ (startOfLine, U"text", 4) && Melder_isEndOfInk (startOfLine [4]))
  206. { type = Interpreter_TEXT; parameterLocation = startOfLine + 4; }
  207. else if (str32nequ (startOfLine, U"choice", 6) && Melder_isEndOfInk (startOfLine [6]))
  208. { type = Interpreter_CHOICE; parameterLocation = startOfLine + 6; }
  209. else if (str32nequ (startOfLine, U"optionmenu", 10) && Melder_isEndOfInk (startOfLine [10]))
  210. { type = Interpreter_OPTIONMENU; parameterLocation = startOfLine + 10; }
  211. else if (str32nequ (startOfLine, U"button", 6) && Melder_isEndOfInk (startOfLine [6]))
  212. { type = Interpreter_BUTTON; parameterLocation = startOfLine + 6; }
  213. else if (str32nequ (startOfLine, U"option", 6) && Melder_isEndOfInk (startOfLine [6]))
  214. { type = Interpreter_OPTION; parameterLocation = startOfLine + 6; }
  215. else if (str32nequ (startOfLine, U"comment", 7) && Melder_isEndOfInk (startOfLine [7]))
  216. { type = Interpreter_COMMENT; parameterLocation = startOfLine + 7; }
  217. else {
  218. endOfLine = Melder_findEndOfLine (startOfLine);
  219. *endOfLine = U'\0'; // destroy input in order to limit printing of parameter type
  220. Melder_throw (U"Unknown parameter type:\n\"", startOfLine, U"\".");
  221. }
  222. /*
  223. Example:
  224. form Something
  225. real Time_(s) 3.14 (= pi)
  226. choice Colour 2
  227. button Red
  228. button Green
  229. button Blue
  230. endform
  231. my parameters [1] := "Time_(s)"
  232. my parameters [2] := "Colour"
  233. my parameters [3] := ""
  234. my parameters [4] := ""
  235. my parameters [5] := ""
  236. my arguments [1] := "3.14 (= pi)"
  237. my arguments [2] := "2"
  238. my arguments [3] := "Red" (funny, but needed in Interpreter_getArgumentsFromString)
  239. my arguments [4] := "Green"
  240. my arguments [5] := "Blue"
  241. */
  242. if (type <= Interpreter_OPTIONMENU) {
  243. Melder_skipHorizontalSpace (& parameterLocation);
  244. if (Melder_isEndOfLine (*parameterLocation)) {
  245. *parameterLocation = U'\0'; // destroy input in order to limit printing of line
  246. Melder_throw (U"Missing parameter:\n\"", startOfLine, U"\".");
  247. }
  248. char32 *q = my parameters [++ my numberOfParameters];
  249. while (Melder_staysWithinInk (*parameterLocation)) * (q ++) = * (parameterLocation ++);
  250. *q = U'\0';
  251. npar ++;
  252. } else {
  253. my parameters [++ my numberOfParameters] [0] = U'\0';
  254. }
  255. char32 *argumentLocation = Melder_findEndOfHorizontalSpace (parameterLocation);
  256. endOfLine = Melder_findEndOfLine (argumentLocation);
  257. if (Melder_isEndOfText (*endOfLine))
  258. Melder_throw (U"Unfinished form (missing \"endform\").");
  259. *endOfLine = U'\0'; // destroy input temporarily in order to limit copying of argument
  260. my arguments [my numberOfParameters] = Melder_dup_f (argumentLocation);
  261. *endOfLine = U'\n'; // restore input
  262. my types [my numberOfParameters] = type;
  263. }
  264. } else {
  265. npar = my numberOfParameters = 0;
  266. }
  267. return npar;
  268. }
  269. autoUiForm Interpreter_createForm (Interpreter me, GuiWindow parent, conststring32 path,
  270. void (*okCallback) (UiForm, integer, Stackel, conststring32, Interpreter, conststring32, bool, void *), void *okClosure,
  271. bool selectionOnly)
  272. {
  273. autoUiForm form = UiForm_create (parent,
  274. Melder_cat (selectionOnly ? U"Run script (selection only): " : U"Run script: ", my dialogTitle),
  275. okCallback, okClosure, nullptr, nullptr);
  276. UiField radio = nullptr;
  277. if (path)
  278. UiForm_addText (form.get(), nullptr, nullptr, U"$file", path);
  279. for (int ipar = 1; ipar <= my numberOfParameters; ipar ++) {
  280. /*
  281. * Convert underscores to spaces.
  282. */
  283. char32 parameter [100], *p = & parameter [0];
  284. str32cpy (parameter, my parameters [ipar]);
  285. while (*p) { if (*p == U'_') *p = U' '; p ++; }
  286. switch (my types [ipar]) {
  287. case Interpreter_WORD:
  288. UiForm_addWord (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
  289. case Interpreter_REAL:
  290. UiForm_addReal (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break; // TODO: an address of a real variable
  291. case Interpreter_POSITIVE:
  292. UiForm_addPositive (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
  293. case Interpreter_INTEGER:
  294. UiForm_addInteger (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
  295. case Interpreter_NATURAL:
  296. UiForm_addNatural (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
  297. case Interpreter_BOOLEAN:
  298. UiForm_addBoolean (form.get(), nullptr, nullptr, parameter, my arguments [ipar] [0] == U'1' ||
  299. my arguments [ipar] [0] == U'y' || my arguments [ipar] [0] == U'Y' ||
  300. (my arguments [ipar] [0] == U'o' && my arguments [ipar] [1] == U'n')); break;
  301. case Interpreter_SENTENCE:
  302. UiForm_addSentence (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
  303. case Interpreter_TEXT:
  304. UiForm_addText (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
  305. case Interpreter_CHOICE:
  306. radio = UiForm_addRadio (form.get(), nullptr, nullptr, nullptr, parameter, Melder_atoi (my arguments [ipar].get()), 1); break;
  307. case Interpreter_OPTIONMENU:
  308. radio = UiForm_addOptionMenu (form.get(), nullptr, nullptr, nullptr, parameter, Melder_atoi (my arguments [ipar].get()), 1); break;
  309. case Interpreter_BUTTON:
  310. if (radio) UiRadio_addButton (radio, my arguments [ipar].get()); break;
  311. case Interpreter_OPTION:
  312. if (radio) UiOptionMenu_addButton (radio, my arguments [ipar].get()); break;
  313. case Interpreter_COMMENT:
  314. UiForm_addLabel (form.get(), nullptr, my arguments [ipar].get()); break;
  315. default:
  316. UiForm_addWord (form.get(), nullptr, nullptr, parameter, my arguments [ipar].get()); break;
  317. }
  318. /*
  319. * Strip parentheses and colon off parameter name.
  320. */
  321. if ((p = str32chr (my parameters [ipar], U'(')) != nullptr) {
  322. *p = U'\0';
  323. if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
  324. }
  325. p = my parameters [ipar];
  326. if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
  327. }
  328. UiForm_finish (form.get());
  329. return form;
  330. }
  331. void Interpreter_getArgumentsFromDialog (Interpreter me, UiForm dialog) {
  332. for (int ipar = 1; ipar <= my numberOfParameters; ipar ++) {
  333. char32 parameter [100], *p;
  334. /*
  335. * Strip parentheses and colon off parameter name.
  336. */
  337. if ((p = str32chr (my parameters [ipar], U'(')) != nullptr) {
  338. *p = U'\0';
  339. if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
  340. }
  341. p = my parameters [ipar];
  342. if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
  343. /*
  344. * Convert underscores to spaces.
  345. */
  346. str32cpy (parameter, my parameters [ipar]);
  347. p = & parameter [0]; while (*p) { if (*p == U'_') *p = U' '; p ++; }
  348. switch (my types [ipar]) {
  349. case Interpreter_REAL:
  350. case Interpreter_POSITIVE: {
  351. double value = UiForm_getReal_check (dialog, parameter);
  352. my arguments [ipar] = autostring32 (40, true);
  353. Melder_sprint (my arguments [ipar].get(),40+1, value);
  354. break;
  355. }
  356. case Interpreter_INTEGER:
  357. case Interpreter_NATURAL:
  358. case Interpreter_BOOLEAN: {
  359. integer value = UiForm_getInteger (dialog, parameter);
  360. my arguments [ipar] = autostring32 (40, true);
  361. Melder_sprint (my arguments [ipar].get(),40+1, value);
  362. break;
  363. }
  364. case Interpreter_CHOICE:
  365. case Interpreter_OPTIONMENU: {
  366. integer integerValue = 0;
  367. integerValue = UiForm_getInteger (dialog, parameter);
  368. conststring32 stringValue = UiForm_getString (dialog, parameter);
  369. my arguments [ipar] = autostring32 (40, true);
  370. Melder_sprint (my arguments [ipar].get(),40+1, integerValue);
  371. Melder_sprint (my choiceArguments [ipar],100, stringValue);
  372. break;
  373. }
  374. case Interpreter_BUTTON:
  375. case Interpreter_OPTION:
  376. case Interpreter_COMMENT:
  377. break;
  378. default: {
  379. conststring32 value = UiForm_getString (dialog, parameter);
  380. my arguments [ipar] = Melder_dup_f (value);
  381. break;
  382. }
  383. }
  384. }
  385. }
  386. void Interpreter_getArgumentsFromString (Interpreter me, conststring32 arguments) {
  387. int size = my numberOfParameters;
  388. integer length = str32len (arguments);
  389. while (size >= 1 && my parameters [size] [0] == U'\0')
  390. size --; /* Ignore fields without a variable name (button, comment). */
  391. for (int ipar = 1; ipar <= size; ipar ++) {
  392. char32 *p = my parameters [ipar];
  393. /*
  394. * Ignore buttons and comments again.
  395. */
  396. if (! *p) continue;
  397. /*
  398. * Strip parentheses and colon off parameter name.
  399. */
  400. if ((p = str32chr (p, U'(')) != nullptr) {
  401. *p = U'\0';
  402. if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
  403. }
  404. p = my parameters [ipar];
  405. if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
  406. }
  407. for (int ipar = 1; ipar < size; ipar ++) {
  408. int ichar = 0;
  409. /*
  410. * Ignore buttons and comments again. The buttons will keep their labels as "arguments".
  411. */
  412. if (my parameters [ipar] [0] == U'\0') continue;
  413. /*
  414. Erase the current values, probably the default values,
  415. and replace with the actual arguments.
  416. */
  417. my arguments [ipar] = autostring32 (length);
  418. /*
  419. Skip spaces until next argument.
  420. */
  421. while (Melder_isHorizontalSpace (*arguments)) arguments ++;
  422. /*
  423. The argument is everything up to the next space, or, if it starts with a double quote,
  424. everything between this quote and the matching double quote;
  425. in this case, the argument can represent a double quote by a sequence of two double quotes.
  426. Example: the string
  427. "I said ""hello"""
  428. will be passed to the dialog as a single argument containing the text
  429. I said "hello"
  430. */
  431. if (*arguments == U'\"') {
  432. arguments ++; // do not include leading double quote
  433. for (;;) {
  434. if (*arguments == U'\0')
  435. Melder_throw (U"Missing matching quote.");
  436. if (*arguments == U'\"' && * ++ arguments != U'\"') break; // remember second quote
  437. my arguments [ipar] [ichar ++] = *arguments ++;
  438. }
  439. } else {
  440. while (Melder_staysWithinInk (*arguments))
  441. my arguments [ipar] [ichar ++] = *arguments ++;
  442. }
  443. my arguments [ipar] [ichar] = U'\0'; // trailing null byte
  444. }
  445. /*
  446. The last item is handled separately, because it consists of the rest of the line.
  447. Leading spaces are skipped, but trailing spaces are included.
  448. */
  449. if (size > 0) {
  450. while (Melder_isHorizontalSpace (*arguments)) arguments ++;
  451. my arguments [size] = Melder_dup_f (arguments);
  452. }
  453. /*
  454. Convert booleans and choices to numbers.
  455. */
  456. for (int ipar = 1; ipar <= size; ipar ++) {
  457. if (my types [ipar] == Interpreter_BOOLEAN) {
  458. mutablestring32 arg = & my arguments [ipar] [0];
  459. if (str32equ (arg, U"1") || str32equ (arg, U"yes") || str32equ (arg, U"on") ||
  460. str32equ (arg, U"Yes") || str32equ (arg, U"On") || str32equ (arg, U"YES") || str32equ (arg, U"ON"))
  461. {
  462. str32cpy (arg, U"1");
  463. } else if (str32equ (arg, U"0") || str32equ (arg, U"no") || str32equ (arg, U"off") ||
  464. str32equ (arg, U"No") || str32equ (arg, U"Off") || str32equ (arg, U"NO") || str32equ (arg, U"OFF"))
  465. {
  466. str32cpy (arg, U"0");
  467. } else {
  468. Melder_throw (U"Unknown value \"", arg, U"\" for boolean \"", my parameters [ipar], U"\".");
  469. }
  470. } else if (my types [ipar] == Interpreter_CHOICE) {
  471. int jpar;
  472. mutablestring32 arg = & my arguments [ipar] [0];
  473. for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
  474. if (my types [jpar] != Interpreter_BUTTON && my types [jpar] != Interpreter_OPTION)
  475. Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
  476. if (str32equ (my arguments [jpar].get(), arg)) { // the button labels are in the arguments; see Interpreter_readParameters
  477. str32cpy (arg, Melder_integer (jpar - ipar));
  478. str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
  479. break;
  480. }
  481. }
  482. if (jpar > my numberOfParameters)
  483. Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
  484. } else if (my types [ipar] == Interpreter_OPTIONMENU) {
  485. int jpar;
  486. mutablestring32 arg = & my arguments [ipar] [0];
  487. for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
  488. if (my types [jpar] != Interpreter_OPTION && my types [jpar] != Interpreter_BUTTON)
  489. Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
  490. if (str32equ (my arguments [jpar].get(), arg)) {
  491. str32cpy (arg, Melder_integer (jpar - ipar));
  492. str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
  493. break;
  494. }
  495. }
  496. if (jpar > my numberOfParameters)
  497. Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
  498. }
  499. }
  500. }
  501. void Interpreter_getArgumentsFromArgs (Interpreter me, int narg, Stackel args) {
  502. trace (narg, U" arguments");
  503. int size = my numberOfParameters;
  504. while (size >= 1 && my parameters [size] [0] == U'\0')
  505. size --; // ignore trailing fields without a variable name (button, comment)
  506. for (int ipar = 1; ipar <= size; ipar ++) {
  507. mutablestring32 p = my parameters [ipar];
  508. /*
  509. * Ignore buttons and comments again.
  510. */
  511. if (! *p) continue;
  512. /*
  513. * Strip parentheses and colon off parameter name.
  514. */
  515. if ((p = str32chr (p, U'(')) != nullptr) {
  516. *p = U'\0';
  517. if (p - my parameters [ipar] > 0 && p [-1] == U'_') p [-1] = U'\0';
  518. }
  519. p = my parameters [ipar];
  520. if (*p != U'\0' && p [str32len (p) - 1] == U':') p [str32len (p) - 1] = U'\0';
  521. }
  522. int iarg = 0;
  523. for (int ipar = 1; ipar <= size; ipar ++) {
  524. /*
  525. * Ignore buttons and comments again. The buttons will keep their labels as "arguments".
  526. */
  527. if (my parameters [ipar] [0] == U'\0') continue;
  528. /*
  529. Erase the current values, probably the default values...
  530. */
  531. my arguments [ipar]. reset(); //
  532. if (iarg == narg)
  533. Melder_throw (U"Found ", narg, U" arguments but expected more.");
  534. Stackel arg = & args [++ iarg];
  535. /*
  536. ... and replace with the actual arguments.
  537. */
  538. my arguments [ipar] =
  539. arg -> which == Stackel_NUMBER ? Melder_dup (Melder_double (arg -> number)) :
  540. arg -> which == Stackel_STRING ? Melder_dup (arg -> getString()) : autostring32();
  541. Melder_assert (my arguments [ipar]);
  542. }
  543. if (iarg < narg)
  544. Melder_throw (U"Found ", narg, U" arguments but expected only ", iarg, U".");
  545. /*
  546. * Convert booleans and choices to numbers.
  547. */
  548. for (int ipar = 1; ipar <= size; ipar ++) {
  549. if (my types [ipar] == Interpreter_BOOLEAN) {
  550. mutablestring32 arg = & my arguments [ipar] [0];
  551. if (str32equ (arg, U"1") || str32equ (arg, U"yes") || str32equ (arg, U"on") ||
  552. str32equ (arg, U"Yes") || str32equ (arg, U"On") || str32equ (arg, U"YES") || str32equ (arg, U"ON"))
  553. {
  554. str32cpy (arg, U"1");
  555. } else if (str32equ (arg, U"0") || str32equ (arg, U"no") || str32equ (arg, U"off") ||
  556. str32equ (arg, U"No") || str32equ (arg, U"Off") || str32equ (arg, U"NO") || str32equ (arg, U"OFF"))
  557. {
  558. str32cpy (arg, U"0");
  559. } else {
  560. Melder_throw (U"Unknown value \"", arg, U"\" for boolean \"", my parameters [ipar], U"\".");
  561. }
  562. } else if (my types [ipar] == Interpreter_CHOICE) {
  563. int jpar;
  564. mutablestring32 arg = & my arguments [ipar] [0];
  565. for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
  566. if (my types [jpar] != Interpreter_BUTTON && my types [jpar] != Interpreter_OPTION)
  567. Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
  568. if (str32equ (my arguments [jpar].get(), arg)) { // the button labels are in the arguments; see Interpreter_readParameters
  569. str32cpy (arg, Melder_integer (jpar - ipar));
  570. str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
  571. break;
  572. }
  573. }
  574. if (jpar > my numberOfParameters)
  575. Melder_throw (U"Unknown value \"", arg, U"\" for choice \"", my parameters [ipar], U"\".");
  576. } else if (my types [ipar] == Interpreter_OPTIONMENU) {
  577. int jpar;
  578. mutablestring32 arg = & my arguments [ipar] [0];
  579. for (jpar = ipar + 1; jpar <= my numberOfParameters; jpar ++) {
  580. if (my types [jpar] != Interpreter_OPTION && my types [jpar] != Interpreter_BUTTON)
  581. Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
  582. if (str32equ (my arguments [jpar].get(), arg)) {
  583. str32cpy (arg, Melder_integer (jpar - ipar));
  584. str32cpy (my choiceArguments [ipar], my arguments [jpar].get());
  585. break;
  586. }
  587. }
  588. if (jpar > my numberOfParameters)
  589. Melder_throw (U"Unknown value \"", arg, U"\" for option menu \"", my parameters [ipar], U"\".");
  590. }
  591. }
  592. }
  593. static void Interpreter_addNumericVariable (Interpreter me, conststring32 key, double value) {
  594. autoInterpreterVariable variable = InterpreterVariable_create (key);
  595. variable -> numericValue = value;
  596. my variablesMap [key] = variable.move();
  597. variable.releaseToAmbiguousOwner();
  598. }
  599. static void Interpreter_addStringVariable (Interpreter me, conststring32 key, conststring32 value) {
  600. autoInterpreterVariable variable = InterpreterVariable_create (key);
  601. variable -> stringValue = Melder_dup (value);
  602. my variablesMap [key] = variable.move();
  603. variable.releaseToAmbiguousOwner();
  604. }
  605. InterpreterVariable Interpreter_hasVariable (Interpreter me, conststring32 key) {
  606. Melder_assert (key);
  607. auto it = my variablesMap. find (key [0] == U'.' ? Melder_cat (my procedureNames [my callDepth], key) : key);
  608. if (it != my variablesMap. end()) {
  609. return it -> second.get();
  610. } else {
  611. return nullptr;
  612. }
  613. }
  614. InterpreterVariable Interpreter_lookUpVariable (Interpreter me, conststring32 key) {
  615. Melder_assert (key);
  616. conststring32 variableNameIncludingProcedureName =
  617. key [0] == U'.' ? Melder_cat (my procedureNames [my callDepth], key) : key;
  618. auto it = my variablesMap. find (variableNameIncludingProcedureName);
  619. if (it != my variablesMap. end()) {
  620. return it -> second.get();
  621. }
  622. /*
  623. * The variable doesn't yet exist: create a new one.
  624. */
  625. autoInterpreterVariable variable = InterpreterVariable_create (variableNameIncludingProcedureName);
  626. InterpreterVariable variable_ref = variable.get();
  627. my variablesMap [variableNameIncludingProcedureName] = variable.move();
  628. return variable_ref;
  629. }
  630. static integer lookupLabel (Interpreter me, conststring32 labelName) {
  631. for (integer ilabel = 1; ilabel <= my numberOfLabels; ilabel ++)
  632. if (str32equ (labelName, my labelNames [ilabel]))
  633. return ilabel;
  634. Melder_throw (U"Unknown label \"", labelName, U"\".");
  635. }
  636. static bool isCommand (conststring32 string) {
  637. const char32 *p = & string [0];
  638. /*
  639. * Things that start with "nowarn", "noprogress", or "nocheck" are commands.
  640. */
  641. if (p [0] == U'n' && p [1] == U'o' &&
  642. (str32nequ (p + 2, U"warn ", 5) || str32nequ (p + 2, U"progress ", 9) || str32nequ (p + 2, U"check ", 6))) return true;
  643. if (str32nequ (p, U"demo ", 5)) return true;
  644. /*
  645. * Otherwise, things that start with lower case are formulas.
  646. */
  647. if (! Melder_isUpperCaseLetter (*p)) return false;
  648. /*
  649. * The remaining possibility is things that start with upper case.
  650. * If they contain an underscore, they are object names, hence we must have a formula.
  651. * Otherwise, we have a command.
  652. */
  653. while (Melder_isAlphanumeric (*p)) p ++;
  654. return *p != '_';
  655. }
  656. static void parameterToVariable (Interpreter me, int type, conststring32 in_parameter, int ipar) {
  657. char32 parameter [200];
  658. Melder_assert (type != 0);
  659. str32cpy (parameter, in_parameter);
  660. if (type >= Interpreter_REAL && type <= Interpreter_BOOLEAN) {
  661. Interpreter_addNumericVariable (me, parameter, Melder_atof (my arguments [ipar].get()));
  662. } else if (type == Interpreter_CHOICE || type == Interpreter_OPTIONMENU) {
  663. Interpreter_addNumericVariable (me, parameter, Melder_atof (my arguments [ipar].get()));
  664. str32cat (parameter, U"$");
  665. Interpreter_addStringVariable (me, parameter, my choiceArguments [ipar]);
  666. } else if (type == Interpreter_BUTTON || type == Interpreter_OPTION || type == Interpreter_COMMENT) {
  667. /* Do not add a variable. */
  668. } else {
  669. str32cat (parameter, U"$");
  670. Interpreter_addStringVariable (me, parameter, my arguments [ipar].get());
  671. }
  672. }
  673. inline static void NumericVectorVariable_move (InterpreterVariable variable, VEC movedVector, bool rightHandSideOwned) {
  674. if (rightHandSideOwned) {
  675. /*
  676. Statement like: a# = b# + c#
  677. */
  678. variable -> numericVectorValue. adoptFromAmbiguousOwner (movedVector);
  679. } else if (variable -> numericVectorValue.size == movedVector.size) {
  680. if (variable -> numericVectorValue.at == movedVector.at) {
  681. /*
  682. Statement like: a# = a#
  683. */
  684. (void) 0; // assigning a variable to itself: do nothing
  685. } else {
  686. /*
  687. Statement like: a# = b# // with matching sizes
  688. */
  689. vectorcopy_preallocated (variable -> numericVectorValue.get(), movedVector);
  690. }
  691. } else {
  692. /*
  693. Statement like: a# = b# // with non-matching sizes
  694. */
  695. variable -> numericVectorValue = VECcopy (movedVector);
  696. }
  697. }
  698. inline static void NumericMatrixVariable_move (InterpreterVariable variable, MAT movedMatrix, bool rightHandSideOwned) {
  699. if (rightHandSideOwned) {
  700. /*
  701. Statement like: a## = b## + c##
  702. */
  703. variable -> numericMatrixValue. adoptFromAmbiguousOwner (movedMatrix);
  704. } else if (variable -> numericMatrixValue.nrow == movedMatrix.nrow && variable -> numericMatrixValue.ncol == movedMatrix.ncol) {
  705. if (variable -> numericMatrixValue.at == movedMatrix.at) {
  706. /*
  707. Statement like: a## = a##
  708. */
  709. (void) 0; // assigning a variable to itself: do nothing
  710. } else {
  711. /*
  712. Statement like: a## = b## // with matching sizes
  713. */
  714. matrixcopy_preallocated (variable -> numericMatrixValue.get(), movedMatrix);
  715. }
  716. } else {
  717. /*
  718. Statement like: a## = b## // with non-matching sizes
  719. */
  720. variable -> numericMatrixValue = matrixcopy (movedMatrix);
  721. }
  722. }
  723. inline static void NumericVectorVariable_add (InterpreterVariable variable, double scalar) {
  724. variable -> numericVectorValue += scalar;
  725. }
  726. inline static void NumericVectorVariable_add (InterpreterVariable variable, constVEC vector) {
  727. VEC variableVector = variable -> numericVectorValue.get();
  728. Melder_require (vector.size == variableVector.size,
  729. U"You cannot add a vector with size ", vector.size,
  730. U" to a vector with a different size (", variableVector.size, U")."
  731. );
  732. variable -> numericVectorValue += vector;
  733. }
  734. inline static void NumericVectorVariable_subtract (InterpreterVariable variable, double scalar) {
  735. VEC variableVector = variable -> numericVectorValue.get();
  736. for (integer i = 1; i <= variable -> numericVectorValue.size; i ++)
  737. variable -> numericVectorValue [i] -= scalar;
  738. }
  739. inline static void NumericVectorVariable_subtract (InterpreterVariable variable, constVEC vector) {
  740. VEC variableVector = variable -> numericVectorValue.get();
  741. if (vector.size != variable -> numericVectorValue.size)
  742. Melder_throw (U"You cannot subtract a vector with size ", vector.size,
  743. U" from a vector with a different size (", variable -> numericVectorValue.size, U").");
  744. for (integer i = 1; i <= variable -> numericVectorValue.size; i ++)
  745. variable -> numericVectorValue [i] -= vector [i];
  746. }
  747. inline static void NumericVectorVariable_multiply (InterpreterVariable variable, double scalar) {
  748. VEC variableVector = variable -> numericVectorValue.get();
  749. for (integer i = 1; i <= variableVector.size; i ++)
  750. variableVector [i] *= scalar;
  751. }
  752. inline static void NumericVectorVariable_multiply (InterpreterVariable variable, constVEC vector) {
  753. VEC variableVector = variable -> numericVectorValue.get();
  754. if (vector.size != variableVector.size)
  755. Melder_throw (U"You cannot multiply a vector with size ", variableVector.size,
  756. U" with a vector with a different size (", vector.size, U").");
  757. for (integer i = 1; i <= variableVector.size; i ++)
  758. variableVector [i] *= vector [i];
  759. }
  760. inline static void NumericVectorVariable_divide (InterpreterVariable variable, double scalar) {
  761. VEC variableVector = variable -> numericVectorValue.get();
  762. for (integer i = 1; i <= variableVector.size; i ++)
  763. variableVector [i] /= scalar;
  764. }
  765. inline static void NumericVectorVariable_divide (InterpreterVariable variable, constVEC vector) {
  766. VEC variableVector = variable -> numericVectorValue.get();
  767. if (vector.size != variableVector.size)
  768. Melder_throw (U"You cannot divide a vector with size ", variableVector.size,
  769. U" by a vector with a different size (", vector.size, U").");
  770. for (integer i = 1; i <= variableVector.size; i ++)
  771. variableVector [i] /= vector [i];
  772. }
  773. inline static void NumericMatrixVariable_add (InterpreterVariable variable, double scalar) {
  774. MAT variableMatrix = variable -> numericMatrixValue.get();
  775. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  776. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  777. variableMatrix [irow] [icol] += scalar;
  778. }
  779. inline static void NumericMatrixVariable_add (InterpreterVariable variable, constMAT matrix) {
  780. MAT variableMatrix = variable -> numericMatrixValue.get();
  781. if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
  782. Melder_throw (U"You cannot add a matrix with size ", matrix.nrow, U"x", matrix.ncol,
  783. U" to a matrix with a different size (", variableMatrix.nrow, U"x", variableMatrix.ncol, U").");
  784. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  785. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  786. variableMatrix [irow] [icol] += matrix [irow] [icol];
  787. }
  788. inline static void NumericMatrixVariable_subtract (InterpreterVariable variable, double scalar) {
  789. MAT variableMatrix = variable -> numericMatrixValue.get();
  790. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  791. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  792. variableMatrix [irow] [icol] -= scalar;
  793. }
  794. inline static void NumericMatrixVariable_subtract (InterpreterVariable variable, constMAT matrix) {
  795. MAT variableMatrix = variable -> numericMatrixValue.get();
  796. if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
  797. Melder_throw (U"You cannot subtract a matrix with size ", matrix.nrow, U"x", matrix.ncol,
  798. U" from a matrix with a different size (", variableMatrix.nrow, U"x", variableMatrix.ncol, U").");
  799. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  800. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  801. variableMatrix [irow] [icol] -= matrix [irow] [icol];
  802. }
  803. inline static void NumericMatrixVariable_multiply (InterpreterVariable variable, double scalar) {
  804. MAT variableMatrix = variable -> numericMatrixValue.get();
  805. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  806. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  807. variableMatrix [irow] [icol] *= scalar;
  808. }
  809. inline static void NumericMatrixVariable_multiply (InterpreterVariable variable, constMAT matrix) {
  810. MAT variableMatrix = variable -> numericMatrixValue.get();
  811. if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
  812. Melder_throw (U"You cannot multiply a matrix with size ", variableMatrix.nrow, U"x", variableMatrix.ncol,
  813. U" from a matrix with a different size (", matrix.nrow, U"x", matrix.ncol, U").");
  814. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  815. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  816. variableMatrix [irow] [icol] *= matrix [irow] [icol];
  817. }
  818. inline static void NumericMatrixVariable_divide (InterpreterVariable variable, double scalar) {
  819. MAT variableMatrix = variable -> numericMatrixValue.get();
  820. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  821. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  822. variableMatrix [irow] [icol] /= scalar;
  823. }
  824. inline static void NumericMatrixVariable_divide (InterpreterVariable variable, constMAT matrix) {
  825. MAT variableMatrix = variable -> numericMatrixValue.get();
  826. if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
  827. Melder_throw (U"You cannot divide a matrix with size ", variableMatrix.nrow, U"x", variableMatrix.ncol,
  828. U" by a matrix with a different size (", matrix.nrow, U"x", matrix.ncol, U").");
  829. for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
  830. for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
  831. variableMatrix [irow] [icol] /= matrix [irow] [icol];
  832. }
  833. static void Interpreter_do_procedureCall (Interpreter me, char32 *command,
  834. char32 * const *lines, integer numberOfLines, integer& lineNumber, integer callStack [], int& callDepth)
  835. {
  836. /*
  837. Modern type of procedure calls, with comma separation, quoted strings, and array support.
  838. We just passed the `@` sign, so we continue by looking for a procedure name at the call site.
  839. */
  840. char32 *p = command;
  841. while (Melder_isHorizontalSpace (*p)) p ++; // skip whitespace
  842. char32 *callName = p;
  843. while (Melder_staysWithinInk (*p) && *p != U'(' && *p != U':') p ++;
  844. if (p == callName) Melder_throw (U"Missing procedure name after \"@\".");
  845. bool hasArguments = ( *p != U'\0' );
  846. if (hasArguments) {
  847. bool parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
  848. *p = U'\0'; // close procedure name
  849. if (! parenthesisOrColonFound) {
  850. p ++; // step over first white space
  851. while (Melder_isHorizontalSpace (*p)) p ++; // skip more whitespace
  852. hasArguments = ( *p != U'\0' );
  853. parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
  854. if (hasArguments && ! parenthesisOrColonFound)
  855. Melder_throw (U"Missing parenthesis or colon after procedure name \"", callName, U"\".");
  856. }
  857. p ++; // step over parenthesis or colon
  858. }
  859. integer callLength = str32len (callName);
  860. integer iline = 1;
  861. for (; iline <= numberOfLines; iline ++) {
  862. if (! str32nequ (lines [iline], U"procedure ", 10)) continue;
  863. char32 *q = lines [iline] + 10;
  864. while (Melder_isHorizontalSpace (*q)) q ++; // skip whitespace before procedure name
  865. char32 *procName = q;
  866. while (Melder_staysWithinInk (*q) && *q != U'(' && *q != U':') q ++;
  867. if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
  868. if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
  869. /*
  870. * We found the procedure definition.
  871. */
  872. if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
  873. Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
  874. str32cpy (my procedureNames [my callDepth], callName);
  875. bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
  876. if (*q) q ++; // step over parenthesis or colon or first white space
  877. if (! parenthesisOrColonFound) {
  878. while (Melder_isHorizontalSpace (*q)) q ++; // skip more whitespace
  879. if (*q == U'(' || *q == U':') q ++; // step over parenthesis or colon
  880. }
  881. while (*q && *q != U')') {
  882. static MelderString argument { };
  883. MelderString_empty (& argument);
  884. while (Melder_isHorizontalSpace (*p)) p ++;
  885. while (Melder_isHorizontalSpace (*q)) q ++;
  886. conststring32 parameterName = q;
  887. while (Melder_staysWithinInk (*q) && *q != U',' && *q != U')') q ++; // collect parameter name
  888. int expressionDepth = 0;
  889. for (; *p; p ++) {
  890. if (*p == U',') {
  891. if (expressionDepth == 0) break; // depth-0 comma ends expression
  892. MelderString_appendCharacter (& argument, U',');
  893. } else if (*p == U')') {
  894. if (expressionDepth == 0) break; // depth-0 closing parenthesis ends expression
  895. expressionDepth --;
  896. MelderString_appendCharacter (& argument, U')');
  897. } else if (*p == U'(') {
  898. expressionDepth ++;
  899. MelderString_appendCharacter (& argument, U'(');
  900. } else if (*p == U'\"') {
  901. /*
  902. * Enter a string literal.
  903. */
  904. MelderString_appendCharacter (& argument, U'\"');
  905. p ++;
  906. for (;; p ++) {
  907. if (*p == U'\0') {
  908. Melder_throw (U"Incomplete string literal: the quotes don't match.");
  909. } else if (*p == U'\"') {
  910. MelderString_appendCharacter (& argument, U'\"');
  911. if (p [1] == '\"') {
  912. p ++; // stay in the string literal
  913. MelderString_appendCharacter (& argument, U'\"');
  914. } else {
  915. break;
  916. }
  917. } else {
  918. MelderString_appendCharacter (& argument, *p);
  919. }
  920. }
  921. } else {
  922. MelderString_appendCharacter (& argument, *p);
  923. }
  924. }
  925. if (q == parameterName)
  926. break;
  927. if (*p) {
  928. *p = U'\0';
  929. p ++;
  930. }
  931. if (q [-1] == U'$') {
  932. my callDepth --;
  933. autostring32 value = Interpreter_stringExpression (me, argument.string);
  934. my callDepth ++;
  935. char32 save = *q;
  936. *q = U'\0';
  937. InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
  938. *q = save;
  939. var -> stringValue = value.move();
  940. } else if (q [-1] == U'#') {
  941. if (q [-2] == U'#') {
  942. MAT value;
  943. bool owned;
  944. my callDepth --;
  945. Interpreter_numericMatrixExpression (me, argument.string, & value, & owned);
  946. my callDepth ++;
  947. char32 save = *q;
  948. *q = U'\0';
  949. InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
  950. *q = save;
  951. NumericMatrixVariable_move (var, value, owned);
  952. } else {
  953. VEC value;
  954. bool owned;
  955. my callDepth --;
  956. Interpreter_numericVectorExpression (me, argument.string, & value, & owned);
  957. my callDepth ++;
  958. char32 save = *q;
  959. *q = U'\0';
  960. InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
  961. *q = save;
  962. NumericVectorVariable_move (var, value, owned);
  963. }
  964. } else {
  965. double value;
  966. my callDepth --;
  967. Interpreter_numericExpression (me, argument.string, & value);
  968. my callDepth ++;
  969. char32 save = *q;
  970. *q = U'\0';
  971. InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName);
  972. *q = save;
  973. var -> numericValue = value;
  974. }
  975. if (*q)
  976. q ++; // skip comma
  977. }
  978. if (callDepth == Interpreter_MAX_CALL_DEPTH)
  979. Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
  980. callStack [++ callDepth] = lineNumber;
  981. lineNumber = iline;
  982. break;
  983. }
  984. }
  985. if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
  986. }
  987. static void Interpreter_do_oldProcedureCall (Interpreter me, char32 *command,
  988. char32 * const *lines, integer numberOfLines, integer& lineNumber, integer callStack [], int& callDepth)
  989. {
  990. /*
  991. Old type of procedure calls, with space separation, unquoted strings, and no array support.
  992. */
  993. char32 *p = command;
  994. while (Melder_isHorizontalSpace (*p)) p ++; // skip whitespace
  995. char32 *callName = p;
  996. while (*p != U'\0' && ! Melder_isHorizontalSpace (*p) && *p != U'(' && *p != U':') p ++;
  997. if (p == callName) Melder_throw (U"Missing procedure name after 'call'.");
  998. bool hasArguments = *p != U'\0';
  999. *p = U'\0'; // close procedure name
  1000. integer callLength = str32len (callName);
  1001. integer iline = 1;
  1002. for (; iline <= numberOfLines; iline ++) {
  1003. if (! str32nequ (lines [iline], U"procedure ", 10)) continue;
  1004. char32 *q = lines [iline] + 10;
  1005. while (Melder_isHorizontalSpace (*q)) q ++;
  1006. char32 *procName = q;
  1007. while (*q != U'\0' && ! Melder_isHorizontalSpace (*q) && *q != U'(' && *q != U':') q ++;
  1008. if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
  1009. bool hasParameters = ( *q != U'\0' );
  1010. if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
  1011. if (hasArguments && ! hasParameters)
  1012. Melder_throw (U"Call to procedure \"", callName, U"\" has too many arguments.");
  1013. if (hasParameters && ! hasArguments)
  1014. Melder_throw (U"Call to procedure \"", callName, U"\" has too few arguments.");
  1015. if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
  1016. Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
  1017. str32cpy (my procedureNames [my callDepth], callName);
  1018. if (hasParameters) {
  1019. bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
  1020. q ++; // step over parenthesis or colon or first white space
  1021. if (! parenthesisOrColonFound) {
  1022. while (Melder_isHorizontalSpace (*q)) q ++; // skip more whitespace
  1023. if (*q == U'(' || *q == U':') q ++; // step over parenthesis or colon
  1024. }
  1025. ++ p; // first argument
  1026. while (*q && *q != ')') {
  1027. char32 *par, save;
  1028. static MelderString arg { };
  1029. MelderString_empty (& arg);
  1030. while (Melder_isHorizontalSpace (*p)) p ++;
  1031. while (Melder_isHorizontalSpace (*q) || *q == U',' || *q == U')') q ++;
  1032. par = q;
  1033. while (*q != U'\0' && ! Melder_isHorizontalSpace (*q) && *q != U',' && *q != U')') q ++; // collect parameter name
  1034. if (*q) { // does anything follow the parameter name?
  1035. if (*p == U'\"') {
  1036. p ++; // skip initial quote
  1037. while (*p != U'\0') {
  1038. if (*p == U'\"') { // quote signals end-of-string or string-internal quote
  1039. if (p [1] == U'\"') { // double quote signals string-internal quote
  1040. MelderString_appendCharacter (& arg, U'\"');
  1041. p += 2; // skip second quote
  1042. } else { // single quote signals end-of-string
  1043. break;
  1044. }
  1045. } else {
  1046. MelderString_appendCharacter (& arg, *p ++);
  1047. }
  1048. }
  1049. } else {
  1050. while (*p != U'\0' && ! Melder_isHorizontalSpace (*p))
  1051. MelderString_appendCharacter (& arg, *p ++); // white space separates
  1052. }
  1053. if (*p) { *p = U'\0'; p ++; }
  1054. } else { // else rest of line
  1055. while (*p != '\0')
  1056. MelderString_appendCharacter (& arg, *p ++);
  1057. }
  1058. if (q [-1] == '$') {
  1059. save = *q; *q = U'\0';
  1060. InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
  1061. var -> stringValue = Melder_dup_f (arg.string);
  1062. } else {
  1063. double value;
  1064. my callDepth --;
  1065. Interpreter_numericExpression (me, arg.string, & value);
  1066. my callDepth ++;
  1067. save = *q; *q = U'\0';
  1068. InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
  1069. var -> numericValue = value;
  1070. }
  1071. }
  1072. }
  1073. if (callDepth == Interpreter_MAX_CALL_DEPTH)
  1074. Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
  1075. callStack [++ callDepth] = lineNumber;
  1076. lineNumber = iline;
  1077. break;
  1078. }
  1079. }
  1080. if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
  1081. }
  1082. static void assignToNumericVectorElement (Interpreter me, char32 *& p, const char32* vectorName, MelderString& valueString) {
  1083. integer indexValue = 0;
  1084. static MelderString index { };
  1085. MelderString_empty (& index);
  1086. int depth = 0;
  1087. bool inString = false;
  1088. while ((depth > 0 || *p != U']' || inString) && Melder_staysWithinLine (*p)) {
  1089. MelderString_appendCharacter (& index, *p);
  1090. if (*p == U'[') {
  1091. if (! inString) depth ++;
  1092. } else if (*p == U']') {
  1093. if (! inString) depth --;
  1094. }
  1095. if (*p == U'"') inString = ! inString;
  1096. p ++;
  1097. }
  1098. if (! Melder_staysWithinLine (*p))
  1099. Melder_throw (U"Missing closing bracket (]) in array element.");
  1100. Formula_Result result;
  1101. Interpreter_anyExpression (me, index.string, & result);
  1102. if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  1103. indexValue = Melder_iround (result. numericResult);
  1104. } else {
  1105. Melder_throw (U"Element index should be numeric.");
  1106. }
  1107. p ++; // step over closing bracket
  1108. while (Melder_isHorizontalSpace (*p)) p ++;
  1109. if (*p != U'=')
  1110. Melder_throw (U"Missing '=' after vector element ", vectorName, U" [", index.string, U"].");
  1111. p ++; // step over equals sign
  1112. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
  1113. if (*p == U'\0') {
  1114. Melder_throw (U"Missing expression after vector element ", vectorName, U" [", index.string, U"].");
  1115. }
  1116. double value;
  1117. if (isCommand (p)) {
  1118. /*
  1119. Get the value of the query.
  1120. */
  1121. MelderString_empty (& valueString);
  1122. autoMelderDivertInfo divert (& valueString);
  1123. MelderString_appendCharacter (& valueString, 1); // will be overwritten by something totally different if any MelderInfo function is called...
  1124. int status = praat_executeCommand (me, p);
  1125. if (status == 0) {
  1126. value = undefined;
  1127. } else if (valueString.string [0] == 1) { // ...not overwritten by any MelderInfo function? then the return value will be the selected object
  1128. int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
  1129. WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
  1130. if (numberOfSelectedObjects > 1)
  1131. Melder_throw (U"Multiple objects selected. Cannot assign object ID to vector element.");
  1132. if (numberOfSelectedObjects == 0)
  1133. Melder_throw (U"No objects selected. Cannot assign object ID to vector element.");
  1134. value = theCurrentPraatObjects -> list [selectedObject]. id;
  1135. } else {
  1136. value = Melder_atof (valueString.string); // including --undefined--
  1137. }
  1138. } else {
  1139. /*
  1140. Get the value of the formula.
  1141. */
  1142. Interpreter_numericExpression (me, p, & value);
  1143. }
  1144. InterpreterVariable var = Interpreter_hasVariable (me, vectorName);
  1145. if (! var)
  1146. Melder_throw (U"Vector ", vectorName, U" does not exist.");
  1147. if (indexValue < 1)
  1148. Melder_throw (U"A vector index cannot be less than 1 (the index you supplied is ", indexValue, U").");
  1149. if (indexValue > var -> numericVectorValue.size)
  1150. Melder_throw (U"A vector index cannot be greater than the number of elements (here ",
  1151. var -> numericVectorValue.size, U"). The index you supplied is ", indexValue, U".");
  1152. var -> numericVectorValue [indexValue] = value;
  1153. }
  1154. static void assignToNumericMatrixElement (Interpreter me, char32 *& p, const char32* matrixName, MelderString& valueString) {
  1155. integer rowNumber = 0, columnNumber = 0;
  1156. /*
  1157. Get the row number.
  1158. */
  1159. static MelderString rowFormula { };
  1160. MelderString_empty (& rowFormula);
  1161. int depth = 0;
  1162. bool inString = false;
  1163. while ((depth > 0 || *p != U',' || inString) && Melder_staysWithinLine (*p)) {
  1164. MelderString_appendCharacter (& rowFormula, *p);
  1165. if (*p == U'[' || *p == U'(') {
  1166. if (! inString) depth ++;
  1167. } else if (*p == U']' || *p == U')') {
  1168. if (! inString) depth --;
  1169. }
  1170. if (*p == U'"') inString = ! inString;
  1171. p ++;
  1172. }
  1173. if (! Melder_staysWithinLine (*p))
  1174. Melder_throw (U"Missing comma in matrix indexing.");
  1175. Formula_Result result;
  1176. Interpreter_anyExpression (me, rowFormula.string, & result);
  1177. if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  1178. rowNumber = Melder_iround (result. numericResult);
  1179. } else {
  1180. Melder_throw (U"Row number should be numeric.");
  1181. }
  1182. p ++; // step over comma
  1183. /*
  1184. Get the column number.
  1185. */
  1186. static MelderString columnFormula { };
  1187. MelderString_empty (& columnFormula);
  1188. depth = 0;
  1189. inString = false;
  1190. while ((depth > 0 || *p != U']' || inString) && Melder_staysWithinLine (*p)) {
  1191. MelderString_appendCharacter (& columnFormula, *p);
  1192. if (*p == U'[') {
  1193. if (! inString) depth ++;
  1194. } else if (*p == U']') {
  1195. if (! inString) depth --;
  1196. }
  1197. if (*p == U'"') inString = ! inString;
  1198. p ++;
  1199. }
  1200. if (! Melder_staysWithinLine (*p))
  1201. Melder_throw (U"Missing closing bracket (]) in matrix indexing.");
  1202. Interpreter_anyExpression (me, columnFormula.string, & result);
  1203. if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  1204. columnNumber = Melder_iround (result. numericResult);
  1205. } else {
  1206. Melder_throw (U"Column number should be numeric.");
  1207. }
  1208. p ++; // step over closing bracket
  1209. while (Melder_isHorizontalSpace (*p)) p ++;
  1210. if (*p != U'=')
  1211. Melder_throw (U"Missing '=' after matrix element ", matrixName, U" [",
  1212. rowFormula.string, U",", columnFormula.string, U"].");
  1213. p ++; // step over equals sign
  1214. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
  1215. if (*p == U'\0') {
  1216. Melder_throw (U"Missing expression after matrix element ", matrixName, U" [",
  1217. rowFormula.string, U",", columnFormula.string, U"].");
  1218. }
  1219. double value;
  1220. if (isCommand (p)) {
  1221. /*
  1222. Get the value of the query.
  1223. */
  1224. MelderString_empty (& valueString);
  1225. autoMelderDivertInfo divert (& valueString);
  1226. MelderString_appendCharacter (& valueString, 1); // will be overwritten by something totally different if any MelderInfo function is called...
  1227. int status = praat_executeCommand (me, p);
  1228. if (status == 0) {
  1229. value = undefined;
  1230. } else if (valueString.string [0] == 1) { // ...not overwritten by any MelderInfo function? then the return value will be the selected object
  1231. int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
  1232. WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
  1233. if (numberOfSelectedObjects > 1) {
  1234. Melder_throw (U"Multiple objects selected. Cannot assign object ID to matrix element.");
  1235. } else if (numberOfSelectedObjects == 0) {
  1236. Melder_throw (U"No objects selected. Cannot assign object ID to matrix element.");
  1237. } else {
  1238. value = theCurrentPraatObjects -> list [selectedObject]. id;
  1239. }
  1240. } else {
  1241. value = Melder_atof (valueString.string); // including --undefined--
  1242. }
  1243. } else {
  1244. Interpreter_numericExpression (me, p, & value);
  1245. }
  1246. InterpreterVariable var = Interpreter_hasVariable (me, matrixName);
  1247. if (! var)
  1248. Melder_throw (U"Matrix ", matrixName, U" does not exist.");
  1249. if (rowNumber < 1)
  1250. Melder_throw (U"A row number cannot be less than 1 (the row number you supplied is ", rowNumber, U").");
  1251. if (rowNumber > var -> numericMatrixValue. nrow)
  1252. Melder_throw (U"A row number cannot be greater than the number of rows (here ",
  1253. var -> numericMatrixValue. nrow, U"). The row number you supplied is ", rowNumber, U".");
  1254. if (columnNumber < 1)
  1255. Melder_throw (U"A column number cannot be less than 1 (the column number you supplied is ", columnNumber, U").");
  1256. if (columnNumber > var -> numericMatrixValue. ncol)
  1257. Melder_throw (U"A column number cannot be greater than the number of columns (here ",
  1258. var -> numericMatrixValue. ncol, U"). The column number you supplied is ", columnNumber, U".");
  1259. var -> numericMatrixValue [rowNumber] [columnNumber] = value;
  1260. }
  1261. void Interpreter_run (Interpreter me, char32 *text) {
  1262. autoNUMvector <char32 *> lines; // not autostringvector, because the elements are reference copies
  1263. integer lineNumber = 0;
  1264. bool assertionFailed = false;
  1265. try {
  1266. static MelderString valueString { }; // to divert the info
  1267. static MelderString assertErrorString { };
  1268. char32 *command = text;
  1269. autoMelderString command2;
  1270. autoMelderString buffer;
  1271. integer numberOfLines = 0, assertErrorLineNumber = 0, callStack [1 + Interpreter_MAX_CALL_DEPTH];
  1272. bool atLastLine = false, fromif = false, fromendfor = false;
  1273. int callDepth = 0, chopped = 0, ipar;
  1274. my callDepth = 0;
  1275. /*
  1276. * The "environment" is null if we are in the Praat shell, or an editor otherwise.
  1277. */
  1278. if (my editorClass) {
  1279. praatP. editor = praat_findEditorFromString (my environmentName.get());
  1280. } else {
  1281. praatP. editor = nullptr;
  1282. }
  1283. /*
  1284. * Start.
  1285. */
  1286. my running = true;
  1287. /*
  1288. * Count lines and set the newlines to zero.
  1289. */
  1290. while (! atLastLine) {
  1291. char32 *endOfLine = command;
  1292. while (Melder_staysWithinLine (*endOfLine)) endOfLine ++;
  1293. if (*endOfLine == U'\0') atLastLine = true;
  1294. *endOfLine = U'\0';
  1295. numberOfLines ++;
  1296. command = endOfLine + 1;
  1297. }
  1298. /*
  1299. * Remember line starts and labels.
  1300. */
  1301. lines.reset (1, numberOfLines);
  1302. for (lineNumber = 1, command = text; lineNumber <= numberOfLines; lineNumber ++, command += str32len (command) + 1 + chopped) {
  1303. while (Melder_isHorizontalSpace (*command) || *command == UNICODE_NO_BREAK_SPACE) command ++; // nbsp can occur for scripts copied from the manual
  1304. /*
  1305. * Chop trailing spaces?
  1306. */
  1307. #if 0
  1308. chopped = 0;
  1309. int length = str32len (command);
  1310. while (length > 0) { char kar = command [-- length]; if (! Melder_isHorizontalSpace (kar)) break; command [length] = '\0'; chopped ++; }
  1311. #endif
  1312. lines [lineNumber] = command;
  1313. if (str32nequ (command, U"label ", 6)) {
  1314. for (integer ilabel = 1; ilabel <= my numberOfLabels; ilabel ++)
  1315. if (str32equ (command + 6, my labelNames [ilabel]))
  1316. Melder_throw (U"Duplicate label \"", command + 6, U"\".");
  1317. if (my numberOfLabels >= Interpreter_MAXNUM_LABELS)
  1318. Melder_throw (U"Too many labels.");
  1319. str32ncpy (my labelNames [++ my numberOfLabels], command + 6, 1+Interpreter_MAX_LABEL_LENGTH);
  1320. my labelNames [my numberOfLabels] [Interpreter_MAX_LABEL_LENGTH] = U'\0';
  1321. my labelLines [my numberOfLabels] = lineNumber;
  1322. }
  1323. }
  1324. /*
  1325. * Connect continuation lines.
  1326. */
  1327. trace (U"connect continuation lines");
  1328. for (lineNumber = numberOfLines; lineNumber >= 2; lineNumber --) {
  1329. char32 *line = lines [lineNumber];
  1330. if (line [0] == U'.' && line [1] == U'.' && line [2] == U'.') {
  1331. char32 *previous = lines [lineNumber - 1];
  1332. MelderString_copy (& command2, line + 3);
  1333. MelderString_get (& command2, previous + str32len (previous));
  1334. static char32 emptyLine [] = { U'\0' };
  1335. lines [lineNumber] = emptyLine;
  1336. }
  1337. }
  1338. /*
  1339. * Copy the parameter names and argument values into the array of variables.
  1340. */
  1341. my variablesMap. clear ();
  1342. for (ipar = 1; ipar <= my numberOfParameters; ipar ++) {
  1343. char32 parameter [200];
  1344. /*
  1345. * Create variable names as-are and variable names without capitals.
  1346. */
  1347. str32cpy (parameter, my parameters [ipar]);
  1348. parameterToVariable (me, my types [ipar], parameter, ipar);
  1349. if (parameter [0] >= U'A' && parameter [0] <= U'Z') {
  1350. parameter [0] = Melder_toLowerCase (parameter [0]);
  1351. parameterToVariable (me, my types [ipar], parameter, ipar);
  1352. }
  1353. }
  1354. /*
  1355. * Initialize some variables.
  1356. */
  1357. Interpreter_addStringVariable (me, U"newline$", U"\n");
  1358. Interpreter_addStringVariable (me, U"tab$", U"\t");
  1359. Interpreter_addStringVariable (me, U"shellDirectory$", Melder_getShellDirectory ());
  1360. structMelderDir dir { }; Melder_getDefaultDir (& dir);
  1361. Interpreter_addStringVariable (me, U"defaultDirectory$", Melder_dirToPath (& dir));
  1362. Interpreter_addStringVariable (me, U"preferencesDirectory$", Melder_dirToPath (& praatDir));
  1363. Melder_getHomeDir (& dir);
  1364. Interpreter_addStringVariable (me, U"homeDirectory$", Melder_dirToPath (& dir));
  1365. Melder_getTempDir (& dir);
  1366. Interpreter_addStringVariable (me, U"temporaryDirectory$", Melder_dirToPath (& dir));
  1367. #if defined (macintosh)
  1368. Interpreter_addNumericVariable (me, U"macintosh", 1);
  1369. Interpreter_addNumericVariable (me, U"windows", 0);
  1370. Interpreter_addNumericVariable (me, U"unix", 0);
  1371. #elif defined (_WIN32)
  1372. Interpreter_addNumericVariable (me, U"macintosh", 0);
  1373. Interpreter_addNumericVariable (me, U"windows", 1);
  1374. Interpreter_addNumericVariable (me, U"unix", 0);
  1375. #elif defined (UNIX)
  1376. Interpreter_addNumericVariable (me, U"macintosh", 0);
  1377. Interpreter_addNumericVariable (me, U"windows", 0);
  1378. Interpreter_addNumericVariable (me, U"unix", 1);
  1379. #else
  1380. Interpreter_addNumericVariable (me, U"macintosh", 0);
  1381. Interpreter_addNumericVariable (me, U"windows", 0);
  1382. Interpreter_addNumericVariable (me, U"unix", 0);
  1383. #endif
  1384. Interpreter_addNumericVariable (me, U"left", 1); // to accommodate scripts from before Praat 5.2.06
  1385. Interpreter_addNumericVariable (me, U"right", 2); // to accommodate scripts from before Praat 5.2.06
  1386. Interpreter_addNumericVariable (me, U"mono", 1); // to accommodate scripts from before Praat 5.2.06
  1387. Interpreter_addNumericVariable (me, U"stereo", 2); // to accommodate scripts from before Praat 5.2.06
  1388. Interpreter_addNumericVariable (me, U"all", 0); // to accommodate scripts from before Praat 5.2.06
  1389. Interpreter_addNumericVariable (me, U"average", 0); // to accommodate scripts from before Praat 5.2.06
  1390. #define xstr(s) str(s)
  1391. #define str(s) #s
  1392. Interpreter_addStringVariable (me, U"praatVersion$", U"" xstr(PRAAT_VERSION_STR));
  1393. Interpreter_addNumericVariable (me, U"praatVersion", PRAAT_VERSION_NUM);
  1394. /*
  1395. * Execute commands.
  1396. */
  1397. trace (U"going to handle ", numberOfLines, U" lines");
  1398. //for (lineNumber = 1; lineNumber <= numberOfLines; lineNumber ++) {
  1399. //trace (U"line ", lineNumber, U": ", lines [lineNumber]);
  1400. //}
  1401. for (lineNumber = 1; lineNumber <= numberOfLines; lineNumber ++) {
  1402. if (my stopped) break;
  1403. //trace (U"now at line ", lineNumber, U": ", lines [lineNumber]);
  1404. //for (int lineNumber2 = 1; lineNumber2 <= numberOfLines; lineNumber2 ++) {
  1405. //trace (U" line ", lineNumber2, U": ", lines [lineNumber2]);
  1406. //}
  1407. try {
  1408. char32 c0;
  1409. bool fail = false;
  1410. MelderString_copy (& command2, lines [lineNumber]);
  1411. c0 = command2. string [0];
  1412. if (c0 == U'\0') continue;
  1413. /*
  1414. * Substitute variables.
  1415. */
  1416. trace (U"substituting variables");
  1417. for (char32 *p = & command2. string [0]; *p != U'\0'; p ++) if (*p == U'\'') {
  1418. /*
  1419. * Found a left quote. Search for a matching right quote.
  1420. */
  1421. char32 *q = p + 1, varName [300], *r, *s, *colon;
  1422. integer precision = -1;
  1423. bool percent = false;
  1424. while (*q != U'\0' && *q != U'\'' && q - p < 299) q ++;
  1425. if (*q == U'\0') break; // no matching right quote? done with this line!
  1426. if (q - p == 1 || q - p >= 299) continue; // ignore empty and too long variable names
  1427. trace (U"found ", q - p - 1);
  1428. /*
  1429. * Found a right quote. Get potential variable name.
  1430. */
  1431. for (r = p + 1, s = varName; q - r > 0; r ++, s ++) *s = *r;
  1432. *s = U'\0'; // trailing null byte
  1433. colon = str32chr (varName, U':');
  1434. if (colon) {
  1435. precision = Melder_atoi (colon + 1);
  1436. if (str32chr (colon + 1, U'%')) percent = true;
  1437. *colon = '\0';
  1438. }
  1439. InterpreterVariable var = Interpreter_hasVariable (me, varName);
  1440. if (var) {
  1441. /*
  1442. * Found a variable (p points to the left quote, q to the right quote). Substitute.
  1443. */
  1444. integer headlen = p - command2.string;
  1445. conststring32 string = var -> stringValue ? var -> stringValue.get() :
  1446. percent ? Melder_percent (var -> numericValue, precision) :
  1447. precision >= 0 ? Melder_fixed (var -> numericValue, precision) :
  1448. Melder_double (var -> numericValue);
  1449. integer arglen = str32len (string);
  1450. MelderString_ncopy (& buffer, command2.string, headlen);
  1451. MelderString_append (& buffer, string, q + 1);
  1452. MelderString_copy (& command2, buffer.string); // This invalidates p!! (really bad bug 20070203)
  1453. p = command2.string + headlen + arglen - 1;
  1454. } else {
  1455. p = q - 1; // go to before next quote
  1456. }
  1457. }
  1458. trace (U"resume");
  1459. c0 = command2.string [0]; // resume in order to allow things like 'c$' = 5
  1460. if ((c0 < U'a' || c0 > U'z') && c0 != U'@' && ! (c0 == U'.' && command2.string [1] >= U'a' && command2.string [1] <= U'z')) {
  1461. praat_executeCommand (me, command2.string);
  1462. /*
  1463. * Interpret control flow and variables.
  1464. */
  1465. } else switch (c0) {
  1466. case U'.':
  1467. fail = true;
  1468. break;
  1469. case U'@':
  1470. Interpreter_do_procedureCall (me, command2.string + 1, lines.peek(), numberOfLines, lineNumber, callStack, callDepth);
  1471. break;
  1472. case U'a':
  1473. if (str32nequ (command2.string, U"assert ", 7)) {
  1474. double value;
  1475. Interpreter_numericExpression (me, command2.string + 7, & value);
  1476. if (value == 0.0 || isundef (value)) {
  1477. assertionFailed = true;
  1478. Melder_throw (U"Script assertion fails in line ", lineNumber,
  1479. U" (", value == 0.0 ? U"false" : U"undefined", U"):\n ", command2.string + 7);
  1480. }
  1481. } else if (str32nequ (command2.string, U"asserterror ", 12)) {
  1482. MelderString_copy (& assertErrorString, command2.string + 12);
  1483. assertErrorLineNumber = lineNumber;
  1484. } else fail = true;
  1485. break;
  1486. case U'b':
  1487. fail = true;
  1488. break;
  1489. case U'c':
  1490. if (str32nequ (command2.string, U"call ", 5)) {
  1491. Interpreter_do_oldProcedureCall (me, command2.string + 5, lines.peek(), numberOfLines, lineNumber, callStack, callDepth);
  1492. } else fail = true;
  1493. break;
  1494. case U'd':
  1495. if (str32nequ (command2.string, U"dec ", 4)) {
  1496. InterpreterVariable var = Interpreter_lookUpVariable (me, command2.string + 4);
  1497. var -> numericValue -= 1.0;
  1498. } else fail = true;
  1499. break;
  1500. case U'e':
  1501. if (command2.string [1] == 'n' && command2.string [2] == 'd') {
  1502. if (str32nequ (command2.string, U"endif", 5) && ! Melder_staysWithinInk (command2.string [5])) {
  1503. /* Ignore. */
  1504. } else if (str32nequ (command2.string, U"endfor", 6) && ! Melder_staysWithinInk (command2.string [6])) {
  1505. int depth = 0;
  1506. integer iline;
  1507. for (iline = lineNumber - 1; iline > 0; iline --) {
  1508. char32 *line = lines [iline];
  1509. if (line [0] == U'f' && line [1] == U'o' && line [2] == U'r' && line [3] == U' ') {
  1510. if (depth == 0) { lineNumber = iline - 1; fromendfor = true; break; } // go before 'for'
  1511. else depth --;
  1512. } else if (str32nequ (lines [iline], U"endfor", 6) && ! Melder_staysWithinInk (lines [iline] [6])) {
  1513. depth ++;
  1514. }
  1515. }
  1516. if (iline <= 0) Melder_throw (U"Unmatched 'endfor'.");
  1517. } else if (str32nequ (command2.string, U"endwhile", 8) && ! Melder_staysWithinInk (command2.string [8])) {
  1518. int depth = 0;
  1519. integer iline;
  1520. for (iline = lineNumber - 1; iline > 0; iline --) {
  1521. if (str32nequ (lines [iline], U"while ", 6)) {
  1522. if (depth == 0) { lineNumber = iline - 1; break; } // go before 'while'
  1523. else depth --;
  1524. } else if (str32nequ (lines [iline], U"endwhile", 8) && ! Melder_staysWithinInk (lines [iline] [8])) {
  1525. depth ++;
  1526. }
  1527. }
  1528. if (iline <= 0) Melder_throw (U"Unmatched 'endwhile'.");
  1529. } else if (str32nequ (command2.string, U"endproc", 7) && ! Melder_staysWithinInk (command2.string [7])) {
  1530. if (callDepth == 0) Melder_throw (U"Unmatched 'endproc'.");
  1531. lineNumber = callStack [callDepth --];
  1532. -- my callDepth;
  1533. } else fail = true;
  1534. } else if (str32nequ (command2.string, U"else", 4) && ! Melder_staysWithinInk (command2.string [4])) {
  1535. int depth = 0;
  1536. integer iline;
  1537. for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
  1538. if (str32nequ (lines [iline], U"endif", 5) && ! Melder_staysWithinInk (lines [iline] [5])) {
  1539. if (depth == 0) { lineNumber = iline; break; } // go after `endif`
  1540. else depth --;
  1541. } else if (str32nequ (lines [iline], U"if ", 3)) {
  1542. depth ++;
  1543. }
  1544. }
  1545. if (iline > numberOfLines) Melder_throw (U"Unmatched 'else'.");
  1546. } else if (str32nequ (command2.string, U"elsif ", 6) || str32nequ (command2.string, U"elif ", 5)) {
  1547. if (fromif) {
  1548. double value;
  1549. fromif = false;
  1550. Interpreter_numericExpression (me, command2.string + 5, & value);
  1551. if (value == 0.0) {
  1552. int depth = 0;
  1553. integer iline;
  1554. for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
  1555. if (str32nequ (lines [iline], U"endif", 5) && ! Melder_staysWithinInk (lines [iline] [5])) {
  1556. if (depth == 0) { lineNumber = iline; break; } // go after `endif`
  1557. else depth --;
  1558. } else if (str32nequ (lines [iline], U"else", 4) && ! Melder_staysWithinInk (lines [iline] [4])) {
  1559. if (depth == 0) { lineNumber = iline; break; } // go after `else`
  1560. } else if ((str32nequ (lines [iline], U"elsif", 5) && ! Melder_staysWithinInk (lines [iline] [5]))
  1561. || (str32nequ (lines [iline], U"elif", 4) && ! Melder_staysWithinInk (lines [iline] [4]))) {
  1562. if (depth == 0) { lineNumber = iline - 1; fromif = true; break; } // go at next 'elsif' or 'elif'
  1563. } else if (str32nequ (lines [iline], U"if ", 3)) {
  1564. depth ++;
  1565. }
  1566. }
  1567. if (iline > numberOfLines) Melder_throw (U"Unmatched 'elsif'.");
  1568. }
  1569. } else {
  1570. int depth = 0;
  1571. integer iline;
  1572. for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
  1573. if (str32nequ (lines [iline], U"endif", 5) && ! Melder_staysWithinInk (lines [iline] [5])) {
  1574. if (depth == 0) { lineNumber = iline; break; } // go after `endif`
  1575. else depth --;
  1576. } else if (str32nequ (lines [iline], U"if ", 3)) {
  1577. depth ++;
  1578. }
  1579. }
  1580. if (iline > numberOfLines) Melder_throw (U"'elsif' not matched with 'endif'.");
  1581. }
  1582. } else if (str32nequ (command2.string, U"exit", 4)) {
  1583. if (command2.string [4] == U'\0') {
  1584. lineNumber = numberOfLines; // go after end
  1585. } else if (command2.string [4] == U' ') {
  1586. Melder_throw (command2.string + 5);
  1587. } else fail = true;
  1588. } else if (str32nequ (command2.string, U"echo ", 5)) {
  1589. /*
  1590. * Make sure that lines like "echo = 3" will not be regarded as assignments.
  1591. */
  1592. praat_executeCommand (me, command2.string);
  1593. } else fail = true;
  1594. break;
  1595. case U'f':
  1596. if (command2.string [1] == U'o' && command2.string [2] == U'r' && command2.string [3] == U' ') { // for_
  1597. double toValue, loopVariable;
  1598. char32 *frompos = str32str (command2.string, U" from "), *topos = str32str (command2.string, U" to ");
  1599. char32 *varpos = command2.string + 4, *endvar = frompos;
  1600. if (! topos) Melder_throw (U"Missing \'to\' in \'for\' loop.");
  1601. if (! endvar) endvar = topos;
  1602. while (*endvar == U' ') { *endvar = '\0'; endvar --; }
  1603. while (*varpos == U' ') varpos ++;
  1604. if (endvar - varpos < 0) Melder_throw (U"Missing loop variable after \'for\'.");
  1605. InterpreterVariable var = Interpreter_lookUpVariable (me, varpos);
  1606. Interpreter_numericExpression (me, topos + 4, & toValue);
  1607. if (fromendfor) {
  1608. fromendfor = false;
  1609. loopVariable = var -> numericValue + 1.0;
  1610. } else if (frompos) {
  1611. *topos = '\0';
  1612. Interpreter_numericExpression (me, frompos + 6, & loopVariable);
  1613. } else {
  1614. loopVariable = 1.0;
  1615. }
  1616. var -> numericValue = loopVariable;
  1617. if (loopVariable > toValue) {
  1618. int depth = 0;
  1619. integer iline;
  1620. for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
  1621. if (str32nequ (lines [iline], U"endfor", 6)) {
  1622. if (depth == 0) { lineNumber = iline; break; } // go after 'endfor'
  1623. else depth --;
  1624. } else if (str32nequ (lines [iline], U"for ", 4)) {
  1625. depth ++;
  1626. }
  1627. }
  1628. if (iline > numberOfLines) Melder_throw (U"Unmatched 'for'.");
  1629. }
  1630. } else if (str32nequ (command2.string, U"form", 4) && Melder_isEndOfInk (command2.string [4])) {
  1631. integer iline;
  1632. for (iline = lineNumber + 1; iline <= numberOfLines; iline ++)
  1633. if (str32nequ (lines [iline], U"endform", 7) && Melder_isEndOfInk (lines [iline] [7]))
  1634. { lineNumber = iline; break; } // go after 'endform'
  1635. if (iline > numberOfLines) Melder_throw (U"Unmatched 'form'.");
  1636. } else fail = true;
  1637. break;
  1638. case U'g':
  1639. if (str32nequ (command2.string, U"goto ", 5)) {
  1640. char32 labelName [1+Interpreter_MAX_LABEL_LENGTH];
  1641. str32ncpy (labelName, command2.string + 5, 1+Interpreter_MAX_LABEL_LENGTH);
  1642. labelName [Interpreter_MAX_LABEL_LENGTH] = U'\0';
  1643. char32 *space = str32chr (labelName, U' ');
  1644. if (space == labelName) Melder_throw (U"Missing label name after 'goto'.");
  1645. bool dojump = true;
  1646. if (space) {
  1647. double value;
  1648. *space = '\0';
  1649. Interpreter_numericExpression (me, command2.string + 6 + str32len (labelName), & value);
  1650. if (value == 0.0) dojump = false;
  1651. }
  1652. if (dojump) {
  1653. integer ilabel = lookupLabel (me, labelName);
  1654. lineNumber = my labelLines [ilabel]; // loop will add 1
  1655. }
  1656. } else fail = true;
  1657. break;
  1658. case U'h':
  1659. fail = true;
  1660. break;
  1661. case U'i':
  1662. if (command2.string [1] == U'f' && command2.string [2] == U' ') { // if_
  1663. double value;
  1664. Interpreter_numericExpression (me, command2.string + 3, & value);
  1665. if (value == 0.0) {
  1666. int depth = 0;
  1667. integer iline;
  1668. for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
  1669. if (str32nequ (lines [iline], U"endif", 5)) {
  1670. if (depth == 0) { lineNumber = iline; break; } // go after 'endif'
  1671. else depth --;
  1672. } else if (str32nequ (lines [iline], U"else", 4)) {
  1673. if (depth == 0) { lineNumber = iline; break; } // go after 'else'
  1674. } else if (str32nequ (lines [iline], U"elsif ", 6) || str32nequ (lines [iline], U"elif ", 5)) {
  1675. if (depth == 0) { lineNumber = iline - 1; fromif = true; break; } // go at 'elsif'
  1676. } else if (str32nequ (lines [iline], U"if ", 3)) {
  1677. depth ++;
  1678. }
  1679. }
  1680. if (iline > numberOfLines) Melder_throw (U"Unmatched 'if'.");
  1681. } else if (isundef (value)) {
  1682. Melder_throw (U"The value of the 'if' condition is undefined.");
  1683. }
  1684. } else if (str32nequ (command2.string, U"inc ", 4)) {
  1685. InterpreterVariable var = Interpreter_lookUpVariable (me, command2.string + 4);
  1686. var -> numericValue += 1.0;
  1687. } else fail = true;
  1688. break;
  1689. case U'j':
  1690. fail = true;
  1691. break;
  1692. case U'k':
  1693. fail = true;
  1694. break;
  1695. case U'l':
  1696. if (str32nequ (command2.string, U"label ", 6)) {
  1697. ; // ignore labels
  1698. } else fail = true;
  1699. break;
  1700. case U'm':
  1701. fail = true;
  1702. break;
  1703. case U'n':
  1704. fail = true;
  1705. break;
  1706. case U'o':
  1707. fail = true;
  1708. break;
  1709. case U'p':
  1710. if (str32nequ (command2.string, U"procedure ", 10)) {
  1711. integer iline = lineNumber + 1;
  1712. for (; iline <= numberOfLines; iline ++) {
  1713. if (str32nequ (lines [iline], U"endproc", 7) && ! Melder_staysWithinInk (lines [iline] [7])) {
  1714. lineNumber = iline;
  1715. break;
  1716. } // go after `endproc`
  1717. }
  1718. if (iline > numberOfLines) Melder_throw (U"Unmatched 'procedure'.");
  1719. } else if (str32nequ (command2.string, U"print", 5)) {
  1720. /*
  1721. * Make sure that lines like "print = 3" will not be regarded as assignments.
  1722. */
  1723. if (command2.string [5] == U' ' || (str32nequ (command2.string + 5, U"line", 4) && (command2.string [9] == U' ' || command2.string [9] == U'\0'))) {
  1724. praat_executeCommand (me, command2.string);
  1725. } else fail = true;
  1726. } else fail = true;
  1727. break;
  1728. case U'q':
  1729. fail = true;
  1730. break;
  1731. case U'r':
  1732. if (str32nequ (command2.string, U"repeat", 6) && ! Melder_staysWithinInk (command2.string [6])) {
  1733. /* Ignore. */
  1734. } else fail = true;
  1735. break;
  1736. case U's':
  1737. if (str32nequ (command2.string, U"stopwatch", 9) && ! Melder_staysWithinInk (command2.string [9])) {
  1738. (void) Melder_stopwatch (); // reset stopwatch
  1739. } else fail = true;
  1740. break;
  1741. case U't':
  1742. fail = true;
  1743. break;
  1744. case U'u':
  1745. if (str32nequ (command2.string, U"until ", 6)) {
  1746. double value;
  1747. Interpreter_numericExpression (me, command2.string + 6, & value);
  1748. if (value == 0.0) {
  1749. int depth = 0;
  1750. integer iline = lineNumber - 1;
  1751. for (; iline > 0; iline --) {
  1752. if (str32nequ (lines [iline], U"repeat", 6) && ! Melder_staysWithinInk (lines [iline] [6])) {
  1753. if (depth == 0) { lineNumber = iline; break; } // go after `repeat`
  1754. else depth --;
  1755. } else if (str32nequ (lines [iline], U"until ", 6)) {
  1756. depth ++;
  1757. }
  1758. }
  1759. if (iline <= 0) Melder_throw (U"Unmatched 'until'.");
  1760. }
  1761. } else fail = true;
  1762. break;
  1763. case U'v':
  1764. fail = true;
  1765. break;
  1766. case U'w':
  1767. if (str32nequ (command2.string, U"while ", 6)) {
  1768. double value;
  1769. Interpreter_numericExpression (me, command2.string + 6, & value);
  1770. if (value == 0.0) {
  1771. int depth = 0;
  1772. integer iline = lineNumber + 1;
  1773. for (; iline <= numberOfLines; iline ++) {
  1774. if (str32nequ (lines [iline], U"endwhile", 8) && ! Melder_staysWithinInk (lines [iline] [8])) {
  1775. if (depth == 0) { lineNumber = iline; break; } // go after `endwhile`
  1776. else depth --;
  1777. } else if (str32nequ (lines [iline], U"while ", 6)) {
  1778. depth ++;
  1779. }
  1780. }
  1781. if (iline > numberOfLines) Melder_throw (U"Unmatched 'while'.");
  1782. }
  1783. } else fail = true;
  1784. break;
  1785. case U'x':
  1786. fail = true;
  1787. break;
  1788. case U'y':
  1789. fail = true;
  1790. break;
  1791. case U'z':
  1792. fail = true;
  1793. break;
  1794. default: break;
  1795. }
  1796. if (fail) {
  1797. /*
  1798. Found an unknown word starting with a lower-case letter, optionally preceded by a period.
  1799. See whether the word is a variable name.
  1800. */
  1801. trace (U"found an unknown word starting with a lower-case letter, optionally preceded by a period");
  1802. char32 *p = & command2.string [0];
  1803. /*
  1804. Variable names consist of a sequence of letters, digits, and underscores,
  1805. optionally preceded by a period and optionally followed by a $ and/or #.
  1806. */
  1807. if (*p == U'.') p ++;
  1808. while (Melder_isWordCharacter (*p) || *p == U'.') p ++;
  1809. if (*p == U'$') {
  1810. /*
  1811. Assign to a string variable.
  1812. */
  1813. trace (U"detected an assignment to a string variable");
  1814. char32 *endOfVariable = ++ p;
  1815. char32 *variableName = command2.string;
  1816. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after variable name
  1817. if (*p == U'[') {
  1818. /*
  1819. This must be an assignment to an indexed string variable.
  1820. */
  1821. *endOfVariable = U'\0';
  1822. static MelderString indexedVariableName { };
  1823. MelderString_copy (& indexedVariableName, command2.string, U"[");
  1824. for (;;) {
  1825. p ++; // skip opening bracket or comma
  1826. static MelderString index { };
  1827. MelderString_empty (& index);
  1828. int depth = 0;
  1829. bool inString = false;
  1830. while ((depth > 0 || (*p != U',' && *p != U']') || inString) && Melder_staysWithinLine (*p)) {
  1831. MelderString_appendCharacter (& index, *p);
  1832. if (*p == U'[') {
  1833. if (! inString)
  1834. depth ++;
  1835. } else if (*p == U']') {
  1836. if (! inString)
  1837. depth --;
  1838. }
  1839. if (*p == U'"')
  1840. inString = ! inString;
  1841. p ++;
  1842. }
  1843. if (! Melder_staysWithinLine (*p))
  1844. Melder_throw (U"Missing closing bracket (]) in indexed variable.");
  1845. Formula_Result result;
  1846. Interpreter_anyExpression (me, index.string, & result);
  1847. if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  1848. double numericIndexValue = result. numericResult;
  1849. MelderString_append (& indexedVariableName, numericIndexValue);
  1850. } else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
  1851. MelderString_append (& indexedVariableName, U"\"", result. stringResult.get(), U"\"");
  1852. }
  1853. MelderString_appendCharacter (& indexedVariableName, *p);
  1854. if (*p == U']') {
  1855. break;
  1856. }
  1857. }
  1858. variableName = indexedVariableName.string;
  1859. p ++; // skip closing bracket
  1860. }
  1861. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after (perhaps indexed) variable name
  1862. int typeOfAssignment; // 0, 1, 2, 3 or 4
  1863. if (*p == U'=') {
  1864. typeOfAssignment = 0; // assignment
  1865. } else if (*p == U'+') {
  1866. if (p [1] == U'=') {
  1867. typeOfAssignment = 1; // adding assignment
  1868. p ++;
  1869. } else {
  1870. Melder_throw (U"Missing \"=\", \"+=\", \"<\", or \">\" after variable ", variableName, U".");
  1871. }
  1872. } else if (*p == U'<') {
  1873. typeOfAssignment = 2; // read from file
  1874. } else if (*p == U'>') {
  1875. if (p [1] == U'>') {
  1876. typeOfAssignment = 3; // append to file
  1877. p ++;
  1878. } else {
  1879. typeOfAssignment = 4; // save to file
  1880. }
  1881. } else Melder_throw (U"Missing \"=\", \"+=\", \"<\", or \">\" after variable ", variableName, U".");
  1882. *endOfVariable = U'\0';
  1883. p ++;
  1884. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment or I/O symbol
  1885. if (*p == U'\0') {
  1886. if (typeOfAssignment >= 2)
  1887. Melder_throw (U"Missing file name after variable ", variableName, U".");
  1888. else
  1889. Melder_throw (U"Missing expression after variable ", variableName, U".");
  1890. }
  1891. if (typeOfAssignment >= 2) {
  1892. structMelderFile file { };
  1893. Melder_relativePathToFile (p, & file);
  1894. if (typeOfAssignment == 2) {
  1895. autostring32 stringValue = MelderFile_readText (& file);
  1896. InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
  1897. var -> stringValue = stringValue.move();
  1898. } else if (typeOfAssignment == 3) {
  1899. if (theCurrentPraatObjects != & theForegroundPraatObjects) Melder_throw (U"Commands that write to a file are not available inside pictures.");
  1900. InterpreterVariable var = Interpreter_hasVariable (me, variableName);
  1901. if (! var) Melder_throw (U"Variable ", variableName, U" undefined.");
  1902. MelderFile_appendText (& file, var -> stringValue.get());
  1903. } else {
  1904. if (theCurrentPraatObjects != & theForegroundPraatObjects) Melder_throw (U"Commands that write to a file are not available inside pictures.");
  1905. InterpreterVariable var = Interpreter_hasVariable (me, variableName);
  1906. if (! var) Melder_throw (U"Variable ", variableName, U" undefined.");
  1907. MelderFile_writeText (& file, var -> stringValue.get(), Melder_getOutputEncoding ());
  1908. }
  1909. } else if (isCommand (p)) {
  1910. /*
  1911. Statement like: name$ = Get name
  1912. */
  1913. MelderString_empty (& valueString); // empty because command may print nothing; also makes sure that valueString.string exists
  1914. autoMelderDivertInfo divert (& valueString);
  1915. int status = praat_executeCommand (me, p);
  1916. InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
  1917. var -> stringValue = Melder_dup (status ? valueString.string : U"");
  1918. } else {
  1919. /*
  1920. Evaluate a string expression and assign the result to the variable.
  1921. Statements like:
  1922. sentence$ = subject$ + verb$ + object$
  1923. extension$ = if index (file$, ".") <> 0
  1924. ... then right$ (file$, length (file$) - rindex (file$, "."))
  1925. ... else "" fi
  1926. */
  1927. trace (U"evaluating string expression");
  1928. autostring32 stringValue = Interpreter_stringExpression (me, p);
  1929. trace (U"assigning to string variable ", variableName);
  1930. if (typeOfAssignment == 1) {
  1931. InterpreterVariable var = Interpreter_hasVariable (me, variableName);
  1932. if (! var)
  1933. Melder_throw (U"The string ", variableName, U" does not exist.\n"
  1934. U"You can increment (+=) only existing strings.");
  1935. integer oldLength = str32len (var -> stringValue.get()), extraLength = str32len (stringValue.get());
  1936. autostring32 newString = autostring32 (oldLength + extraLength, false);
  1937. str32cpy (newString.get(), var -> stringValue.get());
  1938. str32cpy (newString.get() + oldLength, stringValue.get());
  1939. var -> stringValue = newString.move();
  1940. } else {
  1941. InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
  1942. var -> stringValue = stringValue.move();
  1943. }
  1944. }
  1945. } else if (*p == U'#') {
  1946. if (p [1] == U'#') {
  1947. /*
  1948. Assign to a numeric matrix variable or to a matrix element.
  1949. */
  1950. static MelderString matrixName { };
  1951. p ++; // go to second '#'
  1952. *p = U'\0'; // erase the last number sign temporarily
  1953. MelderString_copy (& matrixName, command2.string, U'#');
  1954. *p = U'#'; // put the number sign back
  1955. p ++; // step over last number sign
  1956. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after matrix name
  1957. if (*p == U'=') {
  1958. /*
  1959. This must be an assignment to a matrix variable.
  1960. */
  1961. p ++; // step over equals sign
  1962. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
  1963. if (*p == U'\0')
  1964. Melder_throw (U"Missing right-hand expression in assignment to matrix ", matrixName.string, U".");
  1965. if (isCommand (p)) {
  1966. /*
  1967. Statement like: values## = Get all values
  1968. */
  1969. praat_executeCommand (me, p);
  1970. InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
  1971. var -> numericMatrixValue = theInterpreterNummat.move();
  1972. } else {
  1973. MAT value;
  1974. bool owned;
  1975. Interpreter_numericMatrixExpression (me, p, & value, & owned);
  1976. InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
  1977. NumericMatrixVariable_move (var, value, owned);
  1978. }
  1979. } else if (*p == U'[') {
  1980. assignToNumericMatrixElement (me, ++ p, matrixName.string, valueString);
  1981. } else if (*p == U'+' && p [1] == U'=') {
  1982. InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
  1983. if (! var)
  1984. Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
  1985. U"You can increment (+=) only existing matrices.");
  1986. Formula_Result result;
  1987. Interpreter_anyExpression (me, p += 2, & result);
  1988. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
  1989. NumericMatrixVariable_add (var, result. numericMatrixResult);
  1990. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  1991. NumericMatrixVariable_add (var, result. numericResult);
  1992. } else {
  1993. Melder_throw (U"You can increment (+=) a numeric matrix only with a number or another numeric matrix.");
  1994. }
  1995. } else if (*p == U'-' && p [1] == U'=') {
  1996. InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
  1997. if (! var)
  1998. Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
  1999. U"You can decrement (-=) only existing matrices.");
  2000. Formula_Result result;
  2001. Interpreter_anyExpression (me, p += 2, & result);
  2002. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
  2003. NumericMatrixVariable_subtract (var, result. numericMatrixResult);
  2004. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2005. NumericMatrixVariable_subtract (var, result. numericResult);
  2006. } else {
  2007. Melder_throw (U"You can decrement (-=) a numeric matrix only with a number or another numeric matrix.");
  2008. }
  2009. } else if (*p == U'*' && p [1] == U'=') {
  2010. InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
  2011. if (! var)
  2012. Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
  2013. U"You can multiply (*=) only existing matrices.");
  2014. Formula_Result result;
  2015. Interpreter_anyExpression (me, p += 2, & result);
  2016. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
  2017. NumericMatrixVariable_multiply (var, result. numericMatrixResult);
  2018. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2019. NumericMatrixVariable_multiply (var, result. numericResult);
  2020. } else {
  2021. Melder_throw (U"You can multiply (*=) a numeric matrix only with a number or another numeric matrix.");
  2022. }
  2023. } else if (*p == U'/' && p [1] == U'=') {
  2024. InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
  2025. if (! var)
  2026. Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
  2027. U"You can divide (/=) only existing matrices.");
  2028. Formula_Result result;
  2029. Interpreter_anyExpression (me, p += 2, & result);
  2030. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
  2031. NumericMatrixVariable_divide (var, result. numericMatrixResult);
  2032. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2033. NumericMatrixVariable_divide (var, result. numericResult);
  2034. } else {
  2035. Melder_throw (U"You can divide (/=) a numeric matrix only with a number or another numeric matrix.");
  2036. }
  2037. } else if (*p == U'~') {
  2038. /*
  2039. This must be a formula assignment to a matrix variable.
  2040. */
  2041. p ++; // step over tilde
  2042. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
  2043. if (*p == U'\0')
  2044. Melder_throw (U"Missing formula expression for matrix ", matrixName.string, U".");
  2045. InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
  2046. if (! var)
  2047. Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
  2048. "You can assign a formula only to an existing matrix.");
  2049. static Matrix matrixObject;
  2050. if (! matrixObject)
  2051. matrixObject = Matrix_createSimple (1, 1). releaseToAmbiguousOwner(); // prevent exit-time destruction
  2052. MAT mat = var -> numericMatrixValue.get();
  2053. matrixObject -> xmax = mat.ncol + 0.5;
  2054. matrixObject -> nx = mat.ncol;
  2055. matrixObject -> ymax = mat.nrow + 0.5;
  2056. matrixObject -> ny = mat.nrow;
  2057. matrixObject -> z.at = mat.at; // just a reference (YUCK)
  2058. matrixObject -> z.nrow = mat.nrow;
  2059. matrixObject -> z.ncol = mat.ncol;
  2060. Matrix_formula (matrixObject, p, me, nullptr);
  2061. } else Melder_throw (U"Missing '=' after matrix variable ", matrixName.string, U".");
  2062. } else {
  2063. /*
  2064. Assign to a numeric vector variable or to a vector element.
  2065. */
  2066. static MelderString vectorName { };
  2067. *p = U'\0'; // erase the number sign temporarily
  2068. MelderString_copy (& vectorName, command2.string, U"#");
  2069. *p = U'#'; // put the number sign back
  2070. p ++; // step over number sign
  2071. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after array name
  2072. if (*p == U'=') {
  2073. /*
  2074. This must be an assignment to a vector variable.
  2075. */
  2076. p ++; // step over equals sign
  2077. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
  2078. if (*p == U'\0')
  2079. Melder_throw (U"Missing right-hand expression in assignment to vector ", vectorName.string, U".");
  2080. if (isCommand (p)) {
  2081. /*
  2082. Statement like: times# = Get all times
  2083. */
  2084. praat_executeCommand (me, p);
  2085. InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
  2086. var -> numericVectorValue = theInterpreterNumvec.move();
  2087. } else {
  2088. VEC value;
  2089. bool owned;
  2090. Interpreter_numericVectorExpression (me, p, & value, & owned);
  2091. InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
  2092. NumericVectorVariable_move (var, value, owned);
  2093. }
  2094. } else if (*p == U'[') {
  2095. assignToNumericVectorElement (me, ++ p, vectorName.string, valueString);
  2096. } else if (*p == U'+' && p [1] == U'=') {
  2097. InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
  2098. if (! var)
  2099. Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
  2100. U"You can increment (+=) only existing vectors.");
  2101. Formula_Result result;
  2102. Interpreter_anyExpression (me, p += 2, & result);
  2103. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
  2104. NumericVectorVariable_add (var, result. numericVectorResult);
  2105. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2106. NumericVectorVariable_add (var, result. numericResult);
  2107. } else {
  2108. Melder_throw (U"You can increment (+=) a numeric vector only with a number or another numeric vector.");
  2109. }
  2110. } else if (*p == U'-' && p [1] == U'=') {
  2111. InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
  2112. if (! var)
  2113. Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
  2114. U"You can decrement (-=) only existing vectors.");
  2115. Formula_Result result;
  2116. Interpreter_anyExpression (me, p += 2, & result);
  2117. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
  2118. NumericVectorVariable_subtract (var, result. numericVectorResult);
  2119. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2120. NumericVectorVariable_subtract (var, result. numericResult);
  2121. } else {
  2122. Melder_throw (U"You can decrement (-=) a numeric vector only with a number or another numeric vector.");
  2123. }
  2124. } else if (*p == U'*' && p [1] == U'=') {
  2125. InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
  2126. if (! var)
  2127. Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
  2128. U"You can multiply (*=) only existing vectors.");
  2129. Formula_Result result;
  2130. Interpreter_anyExpression (me, p += 2, & result);
  2131. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
  2132. NumericVectorVariable_multiply (var, result. numericVectorResult);
  2133. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2134. NumericVectorVariable_multiply (var, result. numericResult);
  2135. } else {
  2136. Melder_throw (U"You can multiply (*=) a numeric vector only with a number or another numeric vector.");
  2137. }
  2138. } else if (*p == U'/' && p [1] == U'=') {
  2139. InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
  2140. if (! var)
  2141. Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
  2142. U"You can divide (/=) only existing vectors.");
  2143. Formula_Result result;
  2144. Interpreter_anyExpression (me, p += 2, & result);
  2145. if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
  2146. NumericVectorVariable_divide (var, result. numericVectorResult);
  2147. } else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2148. NumericVectorVariable_divide (var, result. numericResult);
  2149. } else {
  2150. Melder_throw (U"You can divide (/=) a numeric vector only with a number or another numeric vector.");
  2151. }
  2152. } else if (*p == U'~') {
  2153. /*
  2154. This must be a formula assignment to a vector variable.
  2155. */
  2156. p ++; // step over tilde
  2157. while (Melder_isHorizontalSpace (*p)) p ++; // go to first token after assignment
  2158. if (*p == U'\0')
  2159. Melder_throw (U"Missing formula expression for vector ", vectorName.string, U".");
  2160. InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
  2161. if (! var)
  2162. Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
  2163. "You can assign a formula only to an existing vector.");
  2164. static Matrix vectorObject;
  2165. if (! vectorObject) {
  2166. vectorObject = Matrix_createSimple (1, 1). releaseToAmbiguousOwner(); // prevent destruction when program ends
  2167. }
  2168. VEC vec = var -> numericVectorValue.get();
  2169. //vectorObject -> xmin = 0.5;
  2170. vectorObject -> xmax = vec.size + 0.5;
  2171. vectorObject -> nx = vec.size;
  2172. vectorObject -> z.at [1] = vec.at;
  2173. //vectorObject -> z.nrow = 1;
  2174. vectorObject -> z.ncol = vec.size;
  2175. Matrix_formula (vectorObject, p, me, nullptr);
  2176. } else Melder_throw (U"Missing '=' or '+=' or '[' or '~' after vector variable ", vectorName.string, U".");
  2177. }
  2178. } else {
  2179. /*
  2180. * Try to assign to a numeric variable.
  2181. */
  2182. double value;
  2183. char32 *variableName = command2.string;
  2184. int typeOfAssignment = 0; // plain assignment
  2185. if (*p == U'\0') {
  2186. /*
  2187. * Command ends here: it may be a PraatShell command.
  2188. */
  2189. praat_executeCommand (me, command2.string);
  2190. continue; // next line
  2191. }
  2192. char32 *endOfVariable = p;
  2193. while (Melder_isHorizontalSpace (*p)) p ++;
  2194. if (*p == U'=' || ((*p == U'+' || *p == U'-' || *p == U'*' || *p == U'/') && p [1] == U'=')) {
  2195. /*
  2196. This must be an assignment (though: "echo = ..." ???)
  2197. */
  2198. typeOfAssignment = *p == U'+' ? 1 : *p == U'-' ? 2 : *p == U'*' ? 3 : *p == U'/' ? 4 : 0;
  2199. *endOfVariable = U'\0'; // close variable name; FIXME: this can be any weird character, e.g. hallo&
  2200. } else if (*p == U'[') {
  2201. /*
  2202. This must be an assignment to an indexed numeric variable.
  2203. */
  2204. *endOfVariable = U'\0';
  2205. static MelderString indexedVariableName { };
  2206. MelderString_copy (& indexedVariableName, command2.string, U"[");
  2207. for (;;) {
  2208. p ++; // skip opening bracket or comma
  2209. static MelderString index { };
  2210. MelderString_empty (& index);
  2211. int depth = 0;
  2212. bool inString = false;
  2213. while ((depth > 0 || (*p != U',' && *p != U']') || inString) && Melder_staysWithinLine (*p)) {
  2214. MelderString_appendCharacter (& index, *p);
  2215. if (*p == U'[') {
  2216. if (! inString)
  2217. depth ++;
  2218. } else if (*p == U']') {
  2219. if (! inString)
  2220. depth --;
  2221. }
  2222. if (*p == U'"')
  2223. inString = ! inString;
  2224. p ++;
  2225. }
  2226. if (! Melder_staysWithinLine (*p))
  2227. Melder_throw (U"Missing closing bracket (]) in indexed variable.");
  2228. Formula_Result result;
  2229. Melder_assert (! result. stringResult);
  2230. Interpreter_anyExpression (me, index.string, & result);
  2231. if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
  2232. double numericIndexValue = result. numericResult;
  2233. MelderString_append (& indexedVariableName, numericIndexValue);
  2234. } else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
  2235. MelderString_append (& indexedVariableName, U"\"", result. stringResult.get(), U"\"");
  2236. }
  2237. MelderString_appendCharacter (& indexedVariableName, *p);
  2238. if (*p == U']')
  2239. break;
  2240. }
  2241. variableName = indexedVariableName.string;
  2242. p ++; // skip closing bracket
  2243. while (Melder_isHorizontalSpace (*p)) p ++;
  2244. if (*p == U'=' || ((*p == U'+' || *p == U'-' || *p == U'*' || *p == U'/') && p [1] == U'=')) {
  2245. typeOfAssignment = *p == U'+' ? 1 : *p == U'-' ? 2 : *p == U'*' ? 3 : *p == U'/' ? 4 : 0;
  2246. }
  2247. } else {
  2248. /*
  2249. Not an assignment: perhaps a PraatShell command (select, echo, execute, pause ...).
  2250. */
  2251. praat_executeCommand (me, variableName);
  2252. continue; // next line
  2253. }
  2254. p += typeOfAssignment == 0 ? 1 : 2;
  2255. while (Melder_isHorizontalSpace (*p)) p ++;
  2256. if (*p == U'\0') Melder_throw (U"Missing expression after variable ", variableName, U".");
  2257. /*
  2258. Three classes of assignments:
  2259. var = formula
  2260. var = Query
  2261. var = Object creation
  2262. */
  2263. if (isCommand (p)) {
  2264. /*
  2265. Get the value of the query.
  2266. */
  2267. MelderString_empty (& valueString);
  2268. autoMelderDivertInfo divert (& valueString);
  2269. MelderString_appendCharacter (& valueString, 1); // will be overwritten by something totally different if any MelderInfo function is called...
  2270. int status = praat_executeCommand (me, p);
  2271. if (status == 0) {
  2272. value = undefined;
  2273. } else if (valueString.string [0] == 1) { // ...not overwritten by any MelderInfo function? then the return value will be the selected object
  2274. int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
  2275. WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
  2276. if (numberOfSelectedObjects > 1) {
  2277. Melder_throw (U"Multiple objects selected. Cannot assign object ID to variable.");
  2278. } else if (numberOfSelectedObjects == 0) {
  2279. Melder_throw (U"No objects selected. Cannot assign object ID to variable.");
  2280. } else {
  2281. value = theCurrentPraatObjects -> list [selectedObject]. id;
  2282. }
  2283. } else {
  2284. value = Melder_atof (valueString.string); // including --undefined--
  2285. }
  2286. } else {
  2287. /*
  2288. Get the value of the formula.
  2289. */
  2290. Interpreter_numericExpression (me, p, & value);
  2291. }
  2292. /*
  2293. * Assign the value to a variable.
  2294. */
  2295. if (typeOfAssignment == 0) {
  2296. /*
  2297. Use an existing variable, or create a new one.
  2298. */
  2299. //Melder_casual (U"looking up variable ", variableName);
  2300. InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
  2301. var -> numericValue = value;
  2302. } else {
  2303. /*
  2304. Modify an existing variable.
  2305. */
  2306. InterpreterVariable var = Interpreter_hasVariable (me, variableName);
  2307. if (! var) Melder_throw (U"The variable ", variableName, U" does not exist. You can modify only existing variables.");
  2308. if (isundef (var -> numericValue)) {
  2309. /* Keep it that way. */
  2310. } else {
  2311. if (typeOfAssignment == 1) {
  2312. var -> numericValue += value;
  2313. } else if (typeOfAssignment == 2) {
  2314. var -> numericValue -= value;
  2315. } else if (typeOfAssignment == 3) {
  2316. var -> numericValue *= value;
  2317. } else if (value == 0) {
  2318. var -> numericValue = undefined;
  2319. } else {
  2320. var -> numericValue /= value;
  2321. }
  2322. }
  2323. }
  2324. }
  2325. } // endif fail
  2326. if (assertErrorLineNumber != 0 && assertErrorLineNumber != lineNumber) {
  2327. integer save_assertErrorLineNumber = assertErrorLineNumber;
  2328. assertErrorLineNumber = 0;
  2329. Melder_throw (U"Script assertion fails in line ", save_assertErrorLineNumber,
  2330. U": error « ", assertErrorString.string, U" » not raised. Instead: no error.");
  2331. }
  2332. } catch (MelderError) {
  2333. // Melder_casual (U"Error: << ", Melder_getError(),
  2334. // U" >>\nassertErrorLineNumber: ", assertErrorLineNumber,
  2335. // U"\nlineNumber: ", lineNumber,
  2336. // U"\nAssert error string: << ", assertErrorString.string,
  2337. // U" >>\n"
  2338. // );
  2339. if (assertErrorLineNumber == 0) {
  2340. throw;
  2341. } else if (assertErrorLineNumber != lineNumber) {
  2342. if (str32str (Melder_getError (), assertErrorString.string)) {
  2343. Melder_clearError ();
  2344. assertErrorLineNumber = 0;
  2345. } else {
  2346. autostring32 errorCopy_nothrow = Melder_dup_f (Melder_getError ());
  2347. Melder_clearError ();
  2348. Melder_throw (U"Script assertion fails in line ", assertErrorLineNumber,
  2349. U": error « ", assertErrorString.string, U" » not raised. Instead:\n",
  2350. errorCopy_nothrow.get());
  2351. }
  2352. }
  2353. }
  2354. } // endfor lineNumber
  2355. my numberOfLabels = 0;
  2356. my running = false;
  2357. my stopped = false;
  2358. } catch (MelderError) {
  2359. if (lineNumber > 0) {
  2360. bool normalExplicitExit = str32nequ (lines [lineNumber], U"exit ", 5) || Melder_hasError (U"Script exited.");
  2361. if (! normalExplicitExit && ! assertionFailed) { // don't show the message twice!
  2362. while (lines [lineNumber] [0] == U'\0') { // did this use to be a continuation line?
  2363. lineNumber --;
  2364. Melder_assert (lineNumber > 0); // originally empty lines that stayed empty should not generate errors
  2365. }
  2366. Melder_appendError (U"Script line ", lineNumber, U" not performed or completed:\n« ", lines [lineNumber], U" »");
  2367. }
  2368. }
  2369. my numberOfLabels = 0;
  2370. my running = false;
  2371. my stopped = false;
  2372. if (str32equ (Melder_getError (), U"\nScript exited.\n")) {
  2373. Melder_clearError ();
  2374. } else {
  2375. throw;
  2376. }
  2377. }
  2378. }
  2379. void Interpreter_stop (Interpreter me) {
  2380. //Melder_casual (U"Interpreter_stop in: ", Melder_pointer (me));
  2381. my stopped = true;
  2382. //Melder_casual (U"Interpreter_stop out: ", Melder_pointer (me));
  2383. }
  2384. void Interpreter_voidExpression (Interpreter me, conststring32 expression) {
  2385. Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC, false);
  2386. Formula_Result result;
  2387. Formula_run (0, 0, & result);
  2388. }
  2389. void Interpreter_numericExpression (Interpreter me, conststring32 expression, double *p_value) {
  2390. Melder_assert (p_value);
  2391. if (str32str (expression, U"(=")) {
  2392. *p_value = Melder_atof (expression);
  2393. } else {
  2394. Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC, false);
  2395. Formula_Result result;
  2396. Formula_run (0, 0, & result);
  2397. *p_value = result. numericResult;
  2398. }
  2399. }
  2400. void Interpreter_numericVectorExpression (Interpreter me, conststring32 expression, VEC *p_value, bool *p_owned) {
  2401. Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR, false);
  2402. Formula_Result result;
  2403. Formula_run (0, 0, & result);
  2404. *p_value = result. numericVectorResult;
  2405. *p_owned = result. owned;
  2406. result. owned = false;
  2407. }
  2408. void Interpreter_numericMatrixExpression (Interpreter me, conststring32 expression, MAT *p_value, bool *p_owned) {
  2409. Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX, false);
  2410. Formula_Result result;
  2411. Formula_run (0, 0, & result);
  2412. *p_value = result. numericMatrixResult;
  2413. *p_owned = result. owned;
  2414. result. owned = false;
  2415. }
  2416. autostring32 Interpreter_stringExpression (Interpreter me, conststring32 expression) {
  2417. Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_STRING, false);
  2418. Formula_Result result;
  2419. Formula_run (0, 0, & result);
  2420. return result. stringResult.move();
  2421. }
  2422. void Interpreter_anyExpression (Interpreter me, conststring32 expression, Formula_Result *p_result) {
  2423. Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_UNKNOWN, false);
  2424. Formula_run (0, 0, p_result);
  2425. }
  2426. /* End of file Interpreter.cpp */