[med-svn] [Git][med-team/praat][upstream] New upstream version 6.1.50

Rafael Laboissière (@rafael) gitlab at salsa.debian.org
Sun Jul 4 16:28:28 BST 2021



Rafael Laboissière pushed to branch upstream at Debian Med / praat


Commits:
eb03c933 by Rafael Laboissière at 2021-07-04T06:20:27-03:00
New upstream version 6.1.50
- - - - -


16 changed files:

- dwtest/test_MDS.praat
- dwtools/Configuration.cpp
- dwtools/KlattTable.cpp
- dwtools/Proximity.cpp
- fon/manual_Script.cpp
- fon/manual_soundFiles.cpp
- fon/manual_tutorials.cpp
- sys/Formula.cpp
- sys/Interpreter.cpp
- sys/praat.cpp
- sys/praat_script.cpp
- sys/praat_script.h
- sys/praat_version.h
- + test/dwtools/KlattTable.praat
- + test/fon/Spectrum.praat
- test/script/undefined.praat


Changes:

=====================================
dwtest/test_MDS.praat
=====================================
@@ -1,35 +1,72 @@
 # test_MDS.praat
 
 appendInfoLine: "test_MDS.praat"
+
 @test_additiveConstant
+
+ at testLetterRExample
+
 @testDissimilarityInterface
-# side effect: 6 configurations in the list of objects: configuration[1]...configuration[6]
-# testINDSCAL uses these 6 configurations
- at testINDSCAL
- at testProcrustus
 
-for i to 6
-	removeObject: configuration [i]
-endfor
+ at testDissimilarityToConfiguration: 5
+
+ at testCarrolWishExample
+
+ at testProcrustes
 
 appendInfoLine: "test_MDS.praat OK"
 
-procedure testProcrustus
-	appendInfoLine: tab$,  tab$, "Configuration & Configuration"
-	for .i from 2 to  6
-		selectObject: configuration [1]
-		plusObject: configuration [.i]
-		.procrustus [1] = To Procrustes: "no"
-		plusObject: configuration [.i]
-		.ct [.i] = To Configuration
-		plusObject: configuration [1]
-		.procrustus [2] = To Procrustes... no
-		@check_if_identity_transform: .procrustus [2]
-		removeObject: .procrustus [1], .procrustus [2]
+procedure testLetterRExample
+	appendInfoLine: tab$, "test Dissimilarity letter R"
+	.dissimilarity = Create letter R example: 0
+	.numberOfRows = Get number of rows
+	.numberOfColumns = Get number of columns
+	assert .numberOfRows = .numberOfColumns
+	assert .numberOfRows = 32
+	
+	for .irow to .numberOfRows
+		.rowLabel$ = Get row label: .irow
+		.rowIndex = Get row index: .rowLabel$
+		assert .irow = .rowIndex; '.irow' '.rowIndex'
 	endfor
-	for .i from 2 to 6
-		removeObject: .ct [.i]
+	for .icol to .numberOfColumns
+		.columnLabel$ = Get column label: .icol
+		.columnIndex = Get column index: .columnLabel$
+		assert .icol = .columnIndex; '.icol' '.columnIndex'
 	endfor
+
+	# check some dissimilarity values
+	assert object[.dissimilarity, 1, 1] = 0
+	assert object[.dissimilarity, 1, 2] = 6
+	assert object[.dissimilarity, 1, 3] = 9
+	assert object[.dissimilarity, 12, 10] = 7
+	assert object[.dissimilarity, 22, 13] = 46
+	assert object[.dissimilarity, 32, 27] = 79
+	assert object[.dissimilarity, 32, 31] = 7
+	.norm = Get table norm
+
+	.additiveConstant = Get additive constant
+	assert abs (.additiveConstant -153.74)/ 153.74 < 1e-6
+
+	removeObject: .dissimilarity
+	appendInfoLine: tab$, "test Dissimilarity letter R OK"
+endproc
+
+procedure testProcrustes
+	appendInfoLine: tab$, "Procrustes: Configuration & Configuration"
+	.dissimilarity1 = Create letter R example: 0.4
+	.configuration1 = To Configuration (monotone mds): 2, "Primary approach", 1e-5, 50, 1
+	.dissimilarity2 = Create letter R example: 0.5
+	.configuration2 = To Configuration (monotone mds): 2, "Primary approach", 1e-5, 50, 1
+	selectObject: .configuration1, .configuration2
+	.procrustes1 = To Procrustes: "no"
+	plusObject: .configuration2
+	.ct1 = To Configuration
+	plusObject: .configuration1
+	.procrustes2 = To Procrustes: "no"
+	@check_if_identity_transform: .procrustes2
+	removeObject: .procrustes1, .procrustes2, .ct1,.dissimilarity1, .dissimilarity2, .configuration1, .configuration2
+	appendInfoLine: tab$, "Procrustes: Configuration & Configuration OK"
 endproc
 
 procedure check_if_identity_transform:  .p
@@ -50,63 +87,101 @@ procedure check_if_identity_transform:  .p
 	endfor
 endproc
 
+procedure testDissimilarityToConfiguration: .numberOfTries
+	appendInfoLine: tab$, "Dissimilarity to Configuration"
+	.mdsToConfigurationCommands$# = {"To Configuration (monotone mds): 2, ""Primary approach"", 1e-5, 2, 1",
+	...	 "To Configuration (i-spline mds): 2, 1, 1, 1e-5, 2, 1",
+	...	"To Configuration (interval mds): 2, 1e-5, 2, 1", 
+	...	"To Configuration (ratio mds): 2, 1e-5, 2, 1", 
+	...	"To Configuration (absolute mds): 2, 1e-5, 2, 1", 
+	...	"To Configuration (kruskal): 2, 2, ""Primary approach"", ""Kruskal's stress-1"", 1e-5, 2, 1" }
+
+	.mdsImproveConfigurationCommands$# = {"To Configuration (monotone mds): ""Primary approach"", 1e-5, 50, 1",
+	...	 "To Configuration (i-spline mds): 1, 1, 1e-5, 50, 1",
+	...	"To Configuration (interval mds): 1e-5, 50, 1", 
+	...	"To Configuration (ratio mds): 1e-5, 50, 1", 
+	...	"To Configuration (absolute mds): 1e-5, 50, 1", 
+	...	"To Configuration (kruskal): ""Primary approach"", ""Kruskal's stress-1"", 1e-5, 50, 1" }
+
+	.mdsStressQuery$# = { "Get stress (monotone mds): ""Primary approach"", ""Normalized""",	
+	...	 "Get stress (i-spline mds): 1, 2, ""Normalized""",	
+	...	 "Get stress (interval mds): ""Normalized""",	
+	...	 "Get stress (ratio mds): ""Normalized""",	
+	...	 "Get stress (absolute mds): ""Normalized""",	
+	...	 "Get stress: ""Primary approach"", ""Kruskal's stress-1"""}	
+
+	for .itry to .numberOfTries
+		.noiseStdev = randomUniform (0, 10)
+		.dissimilarity = Create letter R example: .noiseStdev
+		for .icommand to size (.mdsToConfigurationCommands$#)
+			selectObject: .dissimilarity
+			.command$ = .mdsToConfigurationCommands$# [.icommand]
+			.conf = '.command$'
+			selectObject: .conf, .dissimilarity
+			.getStressCommand$ = .mdsStressQuery$# [.icommand]
+			.stressBefore = '.getStressCommand$'
+			.improveCommand$ = .mdsImproveConfigurationCommands$# [.icommand]
+			.confImproved = '.improveCommand$'
+			selectObject: .dissimilarity, .confImproved
+			.stressAfter = '.getStressCommand$'
+			assert .stressAfter <= .stressBefore+1e-5; '.stressAfter' <= '.stressBefore'
+			removeObject: .conf, .confImproved
+		endfor
+		removeObject: .dissimilarity
+	endfor
+	appendInfoLine: tab$, "Dissimilarity to Configuration OK"
+endproc
+
 procedure testDissimilarityInterface
 	appendInfoLine: tab$, "test interface"
 	appendInfoLine: tab$, tab$, "Query"
 	.dissimilarity = Create letter R example: 0
 	.numberOfRows = Get number of rows
 	.numberOfColumns = Get number of columns
-	for .irow to .numberOfRows
-		.rowLabel$ = Get row label: .irow
-		.rowIndex = Get row index: .rowLabel$
-		assert .irow = .rowIndex; '.irow' '.rowIndex'
-	endfor
-	for .icol to .numberOfColumns
-		.columnLabel$ = Get column label: .icol
-		.columnIndex = Get column index: .columnLabel$
-		assert .icol = .columnIndex; '.icol' '.columnIndex'
-	endfor
 	for .irow to .numberOfRows
 		for .icol to .numberOfColumns
 			val = Get value: .irow, .icol
 		endfor
 	endfor
-	.norm = Get table norm
-	.additiveConstant = Get additive constant
 
 	appendInfoLine: tab$, tab$, "Modify: skipped"
 	appendInfoLine: tab$, tab$, "Synthesize: skipped"
 
 	appendInfoLine: tab$, tab$, "Extract part"
 	selectObject: .dissimilarity
-	.tmp1 = Extract row ranges: "1 2"
+	.tor1 = Extract row ranges: "1 2"
 	.numberOfRows1 = Get number of rows
 	assert .numberOfRows1 == 2; '.numberOfRows1' "= 2"
+
 	selectObject: .dissimilarity
-	.tmp2 = Extract rows where: "1"
+	.tor2 = Extract rows where: "1"
 	.numberOfRows2 = Get number of rows
 	.numberOfColumns2 = Get number of columns
 	assert .numberOfRows2 == .numberOfRows; '.numberOfRows2' "==" '.numberOfRows'
 	assert .numberOfColumns2 == .numberOfColumns; '.numberOfColumns2' "==" '.numberOfColumns'
+
 	selectObject: .dissimilarity
-	.tmp3 = Extract column ranges: "1 2"
+	.tor3 = Extract column ranges: "1 2"
 	.numberOfColumns3 = Get number of columns
 	assert .numberOfColumns3 == 2; '.numberOfColumns3' "= 2"
+
 	selectObject: .dissimilarity
-	.tmp4 = Extract columns where: "1"
+	.tor4 = Extract columns where: "1"
 	.numberOfRows4 = Get number of rows
 	.numberOfColumns4 = Get number of columns
 	assert .numberOfRows4 == .numberOfRows; '.numberOfRows4' "==" '.numberOfRows'
 	assert .numberOfColumns4 == .numberOfColumns; '.numberOfColumns4' "==" '.numberOfColumns'
-	removeObject: .tmp1, .tmp2, .tmp3, .tmp4
+	removeObject: .tor1, .tor2, .tor3, .tor4
+
 	for .irow to .numberOfRows
 		selectObject: .dissimilarity
 		.rowLabel$ = Get row label: .irow
-		.tmpi = Extract rows where label: "is equal to", .rowLabel$
+		.tori = Extract rows where label: "is equal to", .rowLabel$
 		.numberOfRows5 = Get number of rows
 		assert .numberOfRows5 >= 1
-		removeObject: .tmpi
+		removeObject: .tori
 	endfor
+
 	for .icol to .numberOfColumns
 		selectObject: .dissimilarity
 		.columnLabel$ = Get column label: .icol
@@ -121,6 +196,7 @@ procedure testDissimilarityInterface
 	.strings1 = Extract row labels as Strings
 	.numberOfStrings = Get number of strings
 	assert .numberOfStrings == .numberOfRows
+
 	selectObject: .dissimilarity
 	.strings2 = Extract column labels as Strings
 	.numberOfStrings = Get number of strings
@@ -132,12 +208,14 @@ procedure testDissimilarityInterface
 	.table = To Table: "col1"
 	.numberOfColumnsT = Get number of columns
 	assert .numberOfColumnsT = .numberOfColumns + 1
+
 	selectObject: .dissimilarity
 	.matrix = To Matrix
 	.numberOfRowsM = Get number of rows
 	.numberOfColumnsM = Get number of columns
 	assert .numberOfRowsM == .numberOfRows
 	assert .numberOfColumnsM == .numberOfColumns
+
 	selectObject: .dissimilarity
 	.tableOfReal = To TableOfReal
 	.numberOfRowsT = Get number of rows
@@ -146,153 +224,26 @@ procedure testDissimilarityInterface
 	assert .numberOfColumnsT == .numberOfColumns
 	removeObject: .table, .matrix, .tableOfReal
 
-	appendInfoLine: tab$, tab$, "To Configuration"
-	selectObject: .dissimilarity
-	for .ipar to 6
-		.numberOfDimensions$ [.ipar] = "2, "
-	endfor
-	.numberOfDimensions$[6] = "2, 2, "
-	.minimizationParameters$ = "1e-05, 10, 1"
-	.mdsCommand$ [1] = "To Configuration (monotone mds): "
-	.extraParameters$ [1] = """Primary approach"", "
-	.mdsCommand$ [2] = "To Configuration (i-spline mds): "
-	.extraParameters$ [2] = "1, 1, "
-	.mdsCommand$ [3] = "To Configuration (interval mds): "
-	.extraParameters$ [3] = ""
-	.mdsCommand$ [4] = "To Configuration (ratio mds): "
-	.extraParameters$ [4] = ""
-	.mdsCommand$ [5] = "To Configuration (absolute mds): "
-	.extraParameters$ [5] = ""
-	.mdsCommand$ [6] = "To Configuration (kruskal): "
-	.extraParameters$ [6] = """Primary approach"", ""Kruskal's stress-1"", "
-	
-	# Create a random configuration
-	.command$ = .mdsCommand$ [1] + .numberOfDimensions$ [1] + .extraParameters$ [1] + .minimizationParameters$
-	.randomConfiguration = '.command$'
-	Formula: "randomUniform (-1, 1)"
-	Rename: "random"
-
-	# Use the 6 different "To Configuration (..)" commands to get 6 configurations
-
-	for .itype to 6
-		selectObject: .dissimilarity
-		.command$ = .mdsCommand$ [.itype] + .numberOfDimensions$ [.itype] + .extraParameters$ [.itype] + .minimizationParameters$
-		configuration [.itype] = '.command$'
-	endfor
-
-	# Use the dissimilarity and the configuration and try to improve the configuration 
-
-	appendInfoLine: tab$, tab$, "Dissimilarity & Configuration"
-	.minimizationParameters$ = "1e-08, 50, 1"
-	for .itype to 6
-		selectObject: .dissimilarity, configuration [.itype]
-		.command$ = .mdsCommand$ [.itype] + .extraParameters$ [.itype] + .minimizationParameters$
-		.configuration [.itype] = '.command$'
-	endfor
-
-	.stressMeasure$ [1] = "Normalized"
-	.stressMeasure$ [2] = "Kruskal's stress-1"
-	.stressMeasure$ [3] = "Kruskal's stress-2"
-	.stressMeasure$ [4] = "Raw"
-	.tiesHandling$ [1] = "Primary approach"
-	.tiesHandling$ [2] = "Secondary approach"
-	;.stressCalculation$ [1] = "Formula1"
-	;.stressCalculation$ [2] = "Formula2"
-	.stressCalculation$ [1] = "Kruskal's stress-1"
-	.stressCalculation$ [2] = "Kruskal's stress-1"
-
-	# test kruskal's stress-1 and stress-2
-
-	for .ities to 2
-		selectObject: .dissimilarity, .randomConfiguration	
-		.stress1_random = Get stress (monotone mds): .tiesHandling$ [.ities], .stressMeasure$ [2]
-		.stress2_random = Get stress (monotone mds): .tiesHandling$ [.ities], .stressMeasure$ [3]
-		assert .stress1_random <= .stress2_random; '.stress1_random' <= '.stress2_random' ? random
-		for .i to 6
-			selectObject: .dissimilarity, .configuration [.i]
-			.stress1 = Get stress (monotone mds): .tiesHandling$ [.ities], .stressMeasure$ [2]
-			.stress2 = Get stress (monotone mds): .tiesHandling$ [.ities], .stressMeasure$ [3]
-			assert .stress1 <= .stress1_random; '.stress1' <= '.stress1_random' ? '.ities' conf['.i']
-			assert .stress2 <= .stress2_random; '.stress2' <= '.stress2_random' ? '.ities' conf['.i']
-			assert .stress1 <= .stress2; '.stress1' <= '.stress2' ? '.ities' conf['.i']
-		endfor
-	endfor
-if 0
-	for .k to 4
-		selectObject: .dissimilarity, .randomConfiguration
-		.stress0 = Get stress (i-spline mds): 1, 3, .stressMeasure$ [.k]
-		selectObject: .dissimilarity, configuration [2]
-		.stress1 = Get stress (i-spline mds): 1, 3, .stressMeasure$ [.k]
-		assert .stress1 <= .stress0
-		selectObject: .dissimilarity, .configuration [2]
-		.stress2 = Get stress (i-spline mds): 1, 3, .stressMeasure$ [.k]
-		assert .stress2 <= .stress1; '.stress2' '.stress1' '.k'
-	endfor
-	for .k from 1 to 4
-		selectObject: .dissimilarity, .randomConfiguration
-		.stress10 = Get stress (interval mds): .stressMeasure$ [.k]
-		selectObject: .dissimilarity, configuration [3]
-		.stress11 = Get stress (interval mds): .stressMeasure$ [.k]
-		assert .stress11 <= .stress10 ; '.k'
-		selectObject: .dissimilarity, .configuration [3]
-		.stress12 = Get stress (interval mds): .stressMeasure$ [.k]
-		assert .stress12 <= .stress11 ; '.k'
-		selectObject: .dissimilarity, .randomConfiguration
-		.stress20 = Get stress (ratio mds): .stressMeasure$ [.k]
-		selectObject: .dissimilarity, configuration [4]
-		.stress21 = Get stress (ratio mds): .stressMeasure$ [.k]
-		assert .stress21 <= .stress20 ; '.k'
-		selectObject: .dissimilarity, .configuration [4]
-		.stress22 = Get stress (ratio mds): .stressMeasure$ [.k]
-		assert .stress22 <= .stress21 ; '.k' '.stress22' < '.stress21' ?
-		selectObject: .dissimilarity, .randomConfiguration
-		.stress30 = Get stress (absolute mds): .stressMeasure$ [.k]
-		selectObject: .dissimilarity, configuration [5]
-		.stress31 = Get stress (absolute mds): .stressMeasure$ [.k]
-		assert .stress31 <= .stress30 ; '.k'
-		selectObject: .dissimilarity, .configuration [5]
-		.stress32 = Get stress (absolute mds): .stressMeasure$ [.k]
-		assert .stress32 <= .stress31 ; '.k'
-	endfor
-endif
-
-	for .itype to 6
-		removeObject: .configuration [.itype]
-	endfor
-
-	removeObject: .dissimilarity, .randomConfiguration
+	removeObject: .dissimilarity
+	appendInfoLine: tab$, "test interface OK"
 endproc
 
-procedure dissimilarity_to_Configurations: .dissimilarity
-
-endproc
-
-procedure testINDSCAL
-	for .i  to 6
-		selectObject: configuration [.i]
-		.distance [.i] = To Distance
-	endfor
-
-	selectObject: .distance [1]
-	for .i from 2 to 6
-		plusObject: .distance [.i]
-	endfor
-
-	To Configuration (indscal): 2, "no", 1e-5, 10, 1, "yes", "no"
-	.configuration = selected ("Configuration")
+procedure testCarrolWishExample
+	appendInfoLine: tab$, "INDSCAL Carroll Wish example"
+	Create INDSCAL Carroll Wish example: 0.0
+	.dissimilarities# = selected# ("Dissimilarity")
+	To Distance: "yes"
+	.distances# = selected# ("Distance")
+	To Configuration (indscal): 2, "no", 1e-5, 100, 1, "yes", "no"
+	.conf = selected ("Configuration")
 	.salience = selected ("Salience")
-
-	# test old interface
-	;To Configuration (indscal): "no", 1e-5, 10
-	;.configuration2 = selected ("Configuration")
-	;.salience2 = selected ("Salience")
-	;removeObject: .configuration2, .salience2
-
-	for .i from 1 to 6
-		removeObject: .distance[.i]
+	selectObject: .dissimilarities# [1], .distances# [1]
+	for .i from 2 to size (.dissimilarities#)
+		plusObject: 	.dissimilarities# [.i], .distances# [.i]
 	endfor
-
-	removeObject: .configuration, .salience
+	plusObject: .salience, .conf
+	Remove
+	appendInfoLine: tab$, "INDSCAL Carroll Wish example OK"
 endproc
 
 procedure test_additiveConstant


=====================================
dwtools/Configuration.cpp
=====================================
@@ -390,16 +390,21 @@ autoConfiguration TableOfReal_to_Configuration_pca (TableOfReal me, integer numb
 
 /********************** Examples *********************************************/
 
+/*
+	20210614: Configuration_createLetterRExample. Revert to version without initialisation lists because 
+	that version is buggy when compiled with gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0
+	It turn out that x1 and y2 will contain the same values as x2 and y2.
+*/
 autoConfiguration Configuration_createLetterRExample (int choice) {
-	const constVEC x1 = {
+	const double x1[33] = { 0,
 		-5, -5, -5, -5, -5, -5, -5,   -5, -5, -5,
 		-5, -4, -3, -2, -1,  0,  1, 2.25,  3,  3,
 		2.25,  1,  0, -1, -2, -3, -4,   -1,  0,  1, 2, 3 };
-	const constVEC y1 = {
+	const double y1[33] = { 0,
 		-6, -5, -4, -3, -2, -1, 0,   1,  2,  3,
 		4,  4,  4,  4,  4,  4, 4, 3.5,  2,  1,
 		-0.5, -1, -1, -1, -1, -1, -1, -2, -3, -4, -5, -6 };
-	const constVEC x2 = { 0.94756043346272423, 0.73504466902509913,
+	const double x2[33] = {0, 0.94756043346272423, 0.73504466902509913,
 		0.4528453515175927,    0.46311499024105723,   0.30345454816993439,
 		0.075184942115601547, -0.090010071904764719, -0.19630977381424003,
 		-0.36341509807865086,  -0.54216996409132612,  -0.68704678013309872,
@@ -410,7 +415,7 @@ autoConfiguration Configuration_createLetterRExample (int choice) {
 		0.18201798315035453,   0.048445620192953162,  0.081595930742961439,
 		0.20063623749033621,   0.28546520751183313,   0.39384438699721991,
 		0.62832258520372286,   0.78548335015622228,   1.0610707888793069 };
-	const constVEC y2 = { 0.49630791172076621, 0.53320347382055022,
+	const double y2[33] = {0, 0.49630791172076621, 0.53320347382055022,
 		0.62384637225470441,  0.47592708487655661,  0.50364353255684202,
 		0.55311720162084443,  0.55118713773007066,  0.50007736370068601,
 		0.40432332354648709,  0.49817059660482677,  0.49803436631629411,
@@ -424,14 +429,12 @@ autoConfiguration Configuration_createLetterRExample (int choice) {
 	try {
 		autoConfiguration me = Configuration_create (32, 2);
 		Thing_setName (me.get(), ( choice == 2 ? U"R_fit" : U"R" ));
-		for (integer i = 1; i <= 32; i ++)
-			TableOfReal_setRowLabel (me.get(), i, Melder_integer (i));
-		if (choice == 2) {
-			my data.column (1)  <<=  x2;
-			my data.column (2)  <<=  y2;
-		} else {
-			my data.column (1)  <<=  x1;
-			my data.column (2)  <<=  y1;
+		for (integer i = 1; i <= 32; i ++) {
+			char32 s [20];
+			Melder_sprint (s, 20, i);
+			TableOfReal_setRowLabel (me.get(), i, s);
+			my data [i] [1] = ( choice == 2 ? x2 [i] : x1 [i] );
+			my data [i] [2] = ( choice == 2 ? y2 [i] : y1 [i] );
 		}
 		return me;
 	} catch (MelderError) {


=====================================
dwtools/KlattTable.cpp
=====================================
@@ -392,9 +392,9 @@ typedef struct structKlattFrame {
 	integer Gain0;	/* Overall gain, 60 dB is unity,    0 to   60 */
 } *KlattFrame;
 
-static const constSTRVEC theColumnNames = { U"f0", U"av", U"f1", U"b1", U"f2", U"b2", U"f3", U"b3", U"f4", U"b4", U"f5", U"b5", U"f6", U"b6",
+static autoSTRVEC theColumnNames = copy_STRVEC (constSTRVEC ({ U"f0", U"av", U"f1", U"b1", U"f2", U"b2", U"f3", U"b3", U"f4", U"b4", U"f5", U"b5", U"f6", U"b6",
 	U"fnz", U"bnz", U"fnp", U"bnp", U"ah", U"kopen", U"aturb", U"tilt", U"af", U"skew",
-	U"a1", U"b1p", U"a2", U"b2p", U"a3", U"b3p", U"a4", U"b4p", U"a5", U"b5p", U"a6", U"b6p", U"anp", U"ab", U"avp", U"gain" };
+	U"a1", U"b1p", U"a2", U"b2p", U"a3", U"b3p", U"a4", U"b4p", U"a5", U"b5p", U"a6", U"b6p", U"anp", U"ab", U"avp", U"gain" }));
 
 static double DBtoLIN (integer dB) {
 	static const double amptable [88] = {
@@ -463,13 +463,13 @@ autoKlattTable KlattTable_readFromRawTextFile (MelderFile fs) {
 			U"A KlattTable needs ",  KlattTable_NPAR, U" columns.");
 
 		autoKlattTable me = Thing_new (KlattTable);
-		Table_initWithColumnNames (me.get(), thy ny, theColumnNames);
+		Table_initWithColumnNames (me.get(), thy ny, theColumnNames.get());
 		for (integer irow = 1; irow <= thy ny; irow ++) {
 			for (integer jcol = 1; jcol <= KlattTable_NPAR; jcol ++) {
 				double val = thy z [irow] [jcol];
 				if (jcol > 3 && jcol < 13 && (jcol % 2 == 0) && val <= 0) // bw == 0?
 					val = thy z [irow] [jcol - 1] / 10;
-				Table_setNumericValue ( (Table) me.get(), irow, jcol, val);
+				Table_setNumericValue (me.get(), irow, jcol, val);
 			}
 		}
 		return me;
@@ -557,7 +557,7 @@ autoKlattTable KlattTable_create (double frameDuration, double totalDuration) {
 	try {
 		autoKlattTable me = Thing_new (KlattTable);
 		const integer nrows = Melder_ifloor (totalDuration / frameDuration) + 1;
-		Table_initWithColumnNames (me.get(), nrows, theColumnNames);
+		Table_initWithColumnNames (me.get(), nrows, theColumnNames.get());
 		return me;
 	} catch (MelderError) {
 		Melder_throw (U"KlattTable not created.");
@@ -618,13 +618,13 @@ static void KlattGlobal_getFrame (KlattGlobal me, KlattFrame thee) {
 }
 
 /*
-This function adds F0 flutter, as specified in:
+	This function adds F0 flutter, as specified in:
 
-"Analysis, synthesis and perception of voice quality variations among
-female and male talkers" D.H. Klatt and L.C. Klatt JASA 87(2) February 1990.
+	"Analysis, synthesis and perception of voice quality variations among
+	female and male talkers", D.H. Klatt and L.C. Klatt, JASA 87(2), February 1990.
 
-Flutter is added by applying a quasi-random element constructed from three
-slowly varying sine waves.
+	Flutter is added by applying a quasi-random element constructed from three
+	slowly varying sine waves.
 */
 
 static void KlattFrame_flutter (KlattGlobal me) {
@@ -640,9 +640,9 @@ static void KlattFrame_flutter (KlattGlobal me) {
 }
 
 /*
-  Random number generator (return a number between -8191 and +8191)
-  Noise spectrum is tilted down by soft low-pass filter having a pole near
-    the origin in the z-plane, i.e. output = input + (0.75 * lastoutput)
+	Random number generator (return a number between -8191 and +8191)
+	Noise spectrum is tilted down by soft low-pass filter having a pole near
+	the origin in the Z-plane, i.e. output = input + (0.75 * lastoutput)
 */
 static double KlattGlobal_gen_noise (KlattGlobal me) {
 	static double nlast = 0.0;
@@ -1079,12 +1079,12 @@ static int KlattTable_checkLimits (KlattTable me) {
 		for (integer j = 1; j <= KlattTable_NPAR; j ++) {
 			if (nviolations_lower [j] > 0) {
 				if (nviolations_upper [j] > 0)
-					MelderInfo_writeLine (theColumnNames [j], U": ", nviolations_lower [j], U" frame(s) < min = ",
+					MelderInfo_writeLine (theColumnNames [j].get(), U": ", nviolations_lower [j], U" frame(s) < min = ",
 						nviolations_lower [j], U"; ", nviolations_upper [j], U" frame(s) > max = ", upper [j]);
 				else
-					MelderInfo_writeLine (theColumnNames [j], U": ", nviolations_lower [j], U" frame(s) < min = ", lower [j]);
+					MelderInfo_writeLine (theColumnNames [j].get(), U": ", nviolations_lower [j], U" frame(s) < min = ", lower [j]);
 			} else if (nviolations_upper [j] > 0) {
-				MelderInfo_writeLine (theColumnNames [j], U": ", nviolations_upper [j], U" frame(s) > max = ", upper [j]);
+				MelderInfo_writeLine (theColumnNames [j].get(), U": ", nviolations_upper [j], U" frame(s) > max = ", upper [j]);
 			}
 		}
 		MelderInfo_close ();
@@ -1114,50 +1114,50 @@ autoSound KlattTable_to_Sound (KlattTable me, double samplingFrequency, int synt
 			for (integer col = 1; col <= KlattTable_NPAR; col ++)
 				par [col] = Table_getNumericValue_Assert (me, irow, col);   // ppgb: truncatie?
 			integer jcol = 1;
-			frame ->  F0hz10 = par [jcol ++];
-			frame ->  AVdb = par [jcol ++];
-			frame ->  Fhz [1] = par [jcol ++];
-			frame ->  Bhz [1] = par [jcol ++];
-			frame ->  Fhz [2] = par [jcol ++];
-			frame ->  Bhz [2] = par [jcol ++];
-			frame ->  Fhz [3] = par [jcol ++];
-			frame ->  Bhz [3] = par [jcol ++];
-			frame ->  Fhz [4] = par [jcol ++];
-			frame ->  Bhz [4] = par [jcol ++];
-			frame ->  Fhz [5] = par [jcol ++];
-			frame ->  Bhz [5] = par [jcol ++];
-			frame ->  Fhz [6] = par [jcol ++];
-			frame ->  Bhz [6] = par [jcol ++];
-			frame ->  FNZhz = par [jcol ++];
-			frame ->  BNZhz = par [jcol ++];
-			frame ->  FNPhz = par [jcol ++];
-			frame ->  BNPhz = par [jcol ++];
-			frame ->  ah = par [jcol ++];
-			frame ->  Kopen = par [jcol ++];
-			frame ->  Aturb = par [jcol ++];
-			frame ->  TLTdb = par [jcol ++];
-			frame ->  AF = par [jcol ++];
-			frame ->  Kskew = par [jcol ++];
-			frame ->  A [1] = par [jcol ++];
-			frame ->  Bphz [1] = par [jcol ++];
-			frame ->  A [2] = par [jcol ++];
-			frame ->  Bphz [2] = par [jcol ++];
-			frame ->  A [3] = par [jcol ++];
-			frame ->  Bphz [3] = par [jcol ++];
-			frame ->  A [4] = par [jcol ++];
-			frame ->  Bphz [4] = par [jcol ++];
-			frame ->  A [5] = par [jcol ++];
-			frame ->  Bphz [5] = par [jcol ++];
-			frame ->  A [6] = par [jcol ++];
-			frame ->  Bphz [6] = par [jcol ++];
-			frame ->  ANP = par [jcol ++];
-			frame ->  AB = par [jcol ++];
-			frame ->  AVpdb = par [jcol ++];
-			frame ->  Gain0 = par [jcol ++];;
-			frame ->  Fhz [7] = 6500;
-			frame ->  Bhz [7] = 600;
-			frame ->  Fhz [8] = 7500;
-			frame ->  Bhz [8] = 600;
+			frame -> F0hz10 = par [jcol ++];
+			frame -> AVdb = par [jcol ++];
+			frame -> Fhz [1] = par [jcol ++];
+			frame -> Bhz [1] = par [jcol ++];
+			frame -> Fhz [2] = par [jcol ++];
+			frame -> Bhz [2] = par [jcol ++];
+			frame -> Fhz [3] = par [jcol ++];
+			frame -> Bhz [3] = par [jcol ++];
+			frame -> Fhz [4] = par [jcol ++];
+			frame -> Bhz [4] = par [jcol ++];
+			frame -> Fhz [5] = par [jcol ++];
+			frame -> Bhz [5] = par [jcol ++];
+			frame -> Fhz [6] = par [jcol ++];
+			frame -> Bhz [6] = par [jcol ++];
+			frame -> FNZhz = par [jcol ++];
+			frame -> BNZhz = par [jcol ++];
+			frame -> FNPhz = par [jcol ++];
+			frame -> BNPhz = par [jcol ++];
+			frame -> ah = par [jcol ++];
+			frame -> Kopen = par [jcol ++];
+			frame -> Aturb = par [jcol ++];
+			frame -> TLTdb = par [jcol ++];
+			frame -> AF = par [jcol ++];
+			frame -> Kskew = par [jcol ++];
+			frame -> A [1] = par [jcol ++];
+			frame -> Bphz [1] = par [jcol ++];
+			frame -> A [2] = par [jcol ++];
+			frame -> Bphz [2] = par [jcol ++];
+			frame -> A [3] = par [jcol ++];
+			frame -> Bphz [3] = par [jcol ++];
+			frame -> A [4] = par [jcol ++];
+			frame -> Bphz [4] = par [jcol ++];
+			frame -> A [5] = par [jcol ++];
+			frame -> Bphz [5] = par [jcol ++];
+			frame -> A [6] = par [jcol ++];
+			frame -> Bphz [6] = par [jcol ++];
+			frame -> ANP = par [jcol ++];
+			frame -> AB = par [jcol ++];
+			frame -> AVpdb = par [jcol ++];
+			frame -> Gain0 = par [jcol ++];;
+			frame -> Fhz [7] = 6500;
+			frame -> Bhz [7] = 600;
+			frame -> Fhz [8] = 7500;
+			frame -> Bhz [8] = 600;
 
 			KlattGlobal_getFrame (thee, frame);
 
@@ -1176,7 +1176,6 @@ autoSound KlattTable_to_Sound (KlattTable me, double samplingFrequency, int synt
 	}
 }
 
-
 autoKlattTable KlattTable_createExample () {
 	const integer nrows = 1376;
 	const struct klatt_params {
@@ -2561,7 +2560,7 @@ autoKlattTable KlattTable_createExample () {
 	};
 	try {
 		autoKlattTable me = Thing_new (KlattTable);
-		Table_initWithColumnNames (me.get(), nrows, theColumnNames);
+		Table_initWithColumnNames (me.get(), nrows, theColumnNames.get());
 		Melder_assert (theColumnNames.size == KlattTable_NPAR);
 		for (integer irow = 1; irow <= nrows; irow ++) {
 			for (integer jcol = 1; jcol <= KlattTable_NPAR; jcol ++) {


=====================================
dwtools/Proximity.cpp
=====================================
@@ -1,6 +1,6 @@
 /* Proximity.cpp
  *
- * Copyright (C) 1993-2019 David Weenink
+ * Copyright (C) 1993-2021 David Weenink
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by


=====================================
fon/manual_Script.cpp
=====================================
@@ -3013,7 +3013,8 @@ CODE (U"confusion\\# \\#  = {{ 3, 6, 2 }, { 8, 2, 1 }}")
 NORMAL (U"After this, the variable %%confusion\\# \\# % contains the value {{ 3, 6, 2 }, { 8, 2, 1 }}. "
 	"We say that the matrix %%confusion\\# % has two %rows and three %columns, i.e. it contains six numbers.")
 NORMAL (U"Whereas a numeric vector with five dimensions could be seen (see above) as a street that contains five houses, "
-	"the matrix %%confusion\\# \\# % can be seen as a city district with two avenues crossed by three streets.")
+	"the matrix %%confusion\\# \\# % can be seen as a city district with two avenues crossed by three streets, "
+	"where everybody lives on an intersection (the analogies start to get less realistic).")
 ENTRY (U"6. Creating a matrix")
 NORMAL (U"You can create a matrix in many ways. The first way we saw was with a ##matrix literal#, "
 	"i.e. a series of series of numbers (or numeric formulas) between nested braces.")


=====================================
fon/manual_soundFiles.cpp
=====================================
@@ -217,9 +217,9 @@ NORMAL (U"Vorbis is a general-purpose patent-free lossy audio compression format
 MAN_END
 
 MAN_BEGIN (U"Sound files 2.9. Ogg Opus files", U"djmw", 20210604)
-NORMAL (U"Opus is a general-purpose patent-free lossy audio compression format. "
-	"It is a newer and better format than @@Sound files 2.8. Ogg Vorbis files|Vorbis at . "
-	"According to the website at ##https://xiph.org/#: \"It was developed by the Xiph.Org Foundation and standardized by the Internet Engineering Task Force, designed to efficiently "
+NORMAL (U"Opus is a general-purpose patent-free lossy audio compression format, "
+	"a successor to @@Sound files 2.8. Ogg Vorbis files|Vorbis at . "
+	"According to Xiph.Org's website, this format \"was developed by the Xiph.Org Foundation and standardized by the Internet Engineering Task Force, designed to efficiently "
 	"code speech and general audio in a single format, while remaining low-latency enough for real-time interactive communication "
 	"and low-complexity enough for low-end embedded processors.\" "
 	"Praat supports Ogg Opus decoding through open source code made available at "


=====================================
fon/manual_tutorials.cpp
=====================================
@@ -22,8 +22,13 @@
 void manual_tutorials_init (ManPages me);
 void manual_tutorials_init (ManPages me) {
 
-MAN_BEGIN (U"What's new?", U"ppgb", 20210612)
+MAN_BEGIN (U"What's new?", U"ppgb", 20210620)
 INTRO (U"Latest changes in Praat.")
+NORMAL (U"##6.1.50# (20 June 2021)")
+LIST_ITEM (U"• RealTier: editing, Formula, conversion from and to Matrix and Table and other tiers such as PitchTier, "
+	"DurationTier, IntensityTier and AmplitudeTier.")
+LIST_ITEM (U"• Scripting: can now assign multiple objects to a vector.")
+LIST_ITEM (U"• Fix crashes in ##Create letter R example# and ##Create KlattTable example# introduced in 6.1.49.")
 NORMAL (U"##6.1.49# (12 June 2021)")
 LIST_ITEM (U"• @PitchTier, @IntensityTier, @DurationTier and @AmplitudeTier windows: save preferences.")
 LIST_ITEM (U"• @Manipulation window: removed unused semitone options.")


=====================================
sys/Formula.cpp
=====================================
@@ -56,6 +56,9 @@ static int ilabel, ilexan, iparse, numberOfInstructions, numberOfStringConstants
 
 enum { NO_SYMBOL_,
 
+#define DECLARE_WITH_TENSORS(symbol)  \
+	symbol, symbol##VEC_, symbol##MAT_,
+
 /* First, all symbols after which "-" is unary. */
 /* The list ends with "MINUS_" itself. */
 
@@ -87,18 +90,23 @@ enum { NO_SYMBOL_,
 
 	/* Functions of 1 variable; if you add, update the #defines. */
 	#define LOW_FUNCTION_1  ABS_
-		ABS_, ROUND_, FLOOR_, CEILING_,
-		RECTIFY_, RECTIFY_VEC_, RECTIFY_MAT_,
-		SQRT_, SIN_, COS_, TAN_, ARCSIN_, ARCCOS_, ARCTAN_, SINC_, SINCPI_,
-		EXP_, EXP_VEC_, EXP_MAT_,
-		SINH_, COSH_, TANH_, TANH_VEC_,
-		ARCSINH_, ARCCOSH_, ARCTANH_,
+		DECLARE_WITH_TENSORS (ABS_)
+		DECLARE_WITH_TENSORS (ROUND_)    DECLARE_WITH_TENSORS (FLOOR_)    DECLARE_WITH_TENSORS (CEILING_)
+		DECLARE_WITH_TENSORS (RECTIFY_)
+		DECLARE_WITH_TENSORS (SQRT_)
+		DECLARE_WITH_TENSORS (SIN_)      DECLARE_WITH_TENSORS (COS_)      DECLARE_WITH_TENSORS (TAN_)
+		DECLARE_WITH_TENSORS (ARCSIN_)   DECLARE_WITH_TENSORS (ARCCOS_)   DECLARE_WITH_TENSORS (ARCTAN_)
+		SINC_, SINCPI_,
+		DECLARE_WITH_TENSORS (EXP_)
+		DECLARE_WITH_TENSORS (SINH_)     DECLARE_WITH_TENSORS (COSH_)     DECLARE_WITH_TENSORS (TANH_)
+		DECLARE_WITH_TENSORS (ARCSINH_)  DECLARE_WITH_TENSORS (ARCCOSH_)  DECLARE_WITH_TENSORS (ARCTANH_)
 		SIGMOID_, SIGMOID_VEC_, SOFTMAX_VEC_, SOFTMAX_PER_ROW_MAT_,
 		INV_SIGMOID_, ERF_, ERFC_, GAUSS_P_, GAUSS_Q_, INV_GAUSS_Q_,
 		RANDOM_BERNOULLI_, RANDOM_BERNOULLI_VEC_,
 		RANDOM_POISSON_, TRANSPOSE_MAT_,
 		ROW_SUMS_VEC_, COLUMN_SUMS_VEC_,
-		LOG2_, LN_, LOG10_, LN_GAMMA_,
+		DECLARE_WITH_TENSORS (LOG2_)     DECLARE_WITH_TENSORS (LN_)       DECLARE_WITH_TENSORS (LOG10_)
+		LN_GAMMA_,
 		HERTZ_TO_BARK_, BARK_TO_HERTZ_, PHON_TO_DIFFERENCE_LIMENS_, DIFFERENCE_LIMENS_TO_PHON_,
 		HERTZ_TO_MEL_, MEL_TO_HERTZ_, HERTZ_TO_SEMITONES_, SEMITONES_TO_HERTZ_,
 		ERB_, HERTZ_TO_ERB_, ERB_TO_HERTZ_,
@@ -223,6 +231,9 @@ enum { NO_SYMBOL_,
 /* they are used in error messages and in debugging (see Formula_print). */
 
 static const conststring32 Formula_instructionNames [1 + highestSymbol] = { U"",
+	#define NAME_WITH_TENSORS(name)  \
+		U"" #name, U"" #name "#", U"" #name "##",
+
 	U"if", U"then", U"else", U"(", U"[", U"{", U",", U":", U"from", U"to",
 	U"or", U"and", U"not", U"=", U"<>", U"<=", U"<", U">=", U">",
 	U"+", U"-", U"*", U"/", U"div", U"mod", U"^", U"_call", U"_neg",
@@ -232,18 +243,23 @@ static const conststring32 Formula_instructionNames [1 + highestSymbol] = { U"",
 	U"row", U"col", U"nrow", U"ncol", U"row$", U"col$", U"y", U"x",
 	U"self", U"self$", U"object", U"object$", U"_matrix", U"_matrix$",
 	U"stopwatch",
-	U"abs", U"round", U"floor", U"ceiling",
-	U"rectify", U"rectify#", U"rectify##",
-	U"sqrt", U"sin", U"cos", U"tan", U"arcsin", U"arccos", U"arctan", U"sinc", U"sincpi",
-	U"exp", U"exp#", U"exp##",
-	U"sinh", U"cosh", U"tanh", U"tanh#",
-	U"arcsinh", U"arccosh", U"arctanh",
+	NAME_WITH_TENSORS (abs)
+	NAME_WITH_TENSORS (round)    NAME_WITH_TENSORS (floor)    NAME_WITH_TENSORS (ceiling)
+	NAME_WITH_TENSORS (rectify)
+	NAME_WITH_TENSORS (sqrt)
+	NAME_WITH_TENSORS (sin)      NAME_WITH_TENSORS (cos)      NAME_WITH_TENSORS (tan)
+	NAME_WITH_TENSORS (arcsin)   NAME_WITH_TENSORS (arccos)   NAME_WITH_TENSORS (arctan)
+	U"sinc", U"sincpi",
+	NAME_WITH_TENSORS (exp)
+	NAME_WITH_TENSORS (sinh)     NAME_WITH_TENSORS (cosh)     NAME_WITH_TENSORS (tanh)
+	NAME_WITH_TENSORS (arcsinh)  NAME_WITH_TENSORS (arccosh)  NAME_WITH_TENSORS (arctanh)
 	U"sigmoid", U"sigmoid#", U"softmax#", U"softmaxPerRow##",
 	U"invSigmoid", U"erf", U"erfc", U"gaussP", U"gaussQ", U"invGaussQ",
 	U"randomBernoulli", U"randomBernoulli#",
 	U"randomPoisson", U"transpose##",
 	U"rowSums#", U"columnSums#",
-	U"log2", U"ln", U"log10", U"lnGamma",
+	NAME_WITH_TENSORS (log2)     NAME_WITH_TENSORS (ln)       NAME_WITH_TENSORS (log10)
+	U"lnGamma",
 	U"hertzToBark", U"barkToHertz", U"phonToDifferenceLimens", U"differenceLimensToPhon",
 	U"hertzToMel", U"melToHertz", U"hertzToSemitones", U"semitonesToHertz",
 	U"erb", U"hertzToErb", U"erbToHertz",
@@ -3331,230 +3347,99 @@ static void do_softmaxPerRow_MAT () {
 			U" requires a numeric matrix argument, not ", x->whichText(), U".");
 	}
 }
-static void do_abs () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : fabs (x->number));
-	} else {
-		Melder_throw (U"Cannot take the absolute value (abs) of ", x->whichText(), U".");
-	}
-}
-static void do_round () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : floor (x->number + 0.5));
-	} else {
-		Melder_throw (U"Cannot round ", x->whichText(), U".");
-	}
-}
-static void do_floor () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : Melder_roundDown (x->number));
-	} else {
-		Melder_throw (U"Cannot round down (floor) ", x->whichText(), U".");
-	}
-}
-static void do_ceiling () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : Melder_roundUp (x->number));
-	} else {
-		Melder_throw (U"Cannot round up (ceiling) ", x->whichText(), U".");
-	}
-}
-static void do_rectify () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : x->number > 0.0 ? x->number : 0.0);
-	} else {
-		Melder_throw (U"Cannot rectify ", x->whichText(), U".");
-	}
-}
-static void do_rectify_VEC () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMERIC_VECTOR) {
-		integer nelm = x->numericVector.size;
-		autoVEC result = raw_VEC (nelm);
-		for (integer i = 1; i <= nelm; i ++) {
-			double xvalue = x->numericVector [i];
-			result [i] = isundef (xvalue) ? undefined : xvalue > 0.0 ? xvalue : 0.0;
-		}
-		pushNumericVector (result.move());
-	} else {
-		Melder_throw (U"Cannot rectify ", x->whichText(), U".");
-	}
-}
-static void do_rectify_MAT () {
-	Stackel x = topOfStack;
-	if (x->which == Stackel_NUMERIC_MATRIX) {
-		if (x->owned) {
-			integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol;
-			for (integer irow = 1; irow <= nrow; irow ++) {
-				for (integer icol = 1; icol <= ncol; icol ++) {
-					double xvalue = x->numericMatrix [irow] [icol];
-					x->numericMatrix [irow] [icol] = isundef (xvalue) ? undefined : xvalue > 0.0 ? xvalue : 0.0;
-				}
-			}
-		} else {
-			pop;
-			integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol;
-			autoMAT result = raw_MAT (nrow, ncol);
-			for (integer irow = 1; irow <= nrow; irow ++) {
-				for (integer icol = 1; icol <= ncol; icol ++) {
-					double xvalue = x->numericMatrix [irow] [icol];
-					result [irow] [icol] = isundef (xvalue) ? undefined : xvalue > 0.0 ? xvalue : 0.0;
-				}
-			}
-			pushNumericMatrix (result.move());
-		}
-	} else {
-		Melder_throw (U"Cannot rectify ", x->whichText(), U".");
-	}
-}
-static void do_sqrt () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined :
-			x->number < 0.0 ? undefined : sqrt (x->number));
-	} else {
-		Melder_throw (U"Cannot take the square root (sqrt) of ", x->whichText(), U".");
-	}
-}
-static void do_sin () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : sin (x->number));
-	} else {
-		Melder_throw (U"Cannot take the sine (sin) of ", x->whichText(), U".");
-	}
-}
-static void do_cos () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : cos (x->number));
-	} else {
-		Melder_throw (U"Cannot take the cosine (cos) of ", x->whichText(), U".");
-	}
-}
-static void do_tan () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : tan (x->number));
-	} else {
-		Melder_throw (U"Cannot take the tangent (tan) of ", x->whichText(), U".");
-	}
-}
-static void do_arcsin () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined :
-			fabs (x->number) > 1.0 ? undefined : asin (x->number));
-	} else {
-		Melder_throw (U"Cannot take the arcsine (arcsin) of ", x->whichText(), U".");
-	}
-}
-static void do_arccos () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined :
-			fabs (x->number) > 1.0 ? undefined : acos (x->number));
-	} else {
-		Melder_throw (U"Cannot take the arccosine (arccos) of ", x->whichText(), U".");
-	}
-}
-static void do_arctan () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : atan (x->number));
-	} else {
-		Melder_throw (U"Cannot take the arctangent (arctan) of ", x->whichText(), U".");
-	}
-}
-static void do_exp () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : exp (x->number));
-	} else {
-		Melder_throw (U"Cannot exponentiate (exp) ", x->whichText(), U".");
-	}
-}
-static void do_exp_VEC () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMERIC_VECTOR) {
-		integer nelm = x->numericVector.size;
-		autoVEC result = raw_VEC (nelm);
-		for (integer i = 1; i <= nelm; i ++)
-			result [i] = exp (x->numericVector [i]);
-		pushNumericVector (result.move());
-	} else {
-		Melder_throw (U"Cannot exponentiate (exp) ", x->whichText(), U".");
-	}
-}
-static void do_exp_MAT () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMERIC_MATRIX) {
-		integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol;
-		autoMAT result = raw_MAT (nrow, ncol);
-		for (integer irow = 1; irow <= nrow; irow ++)
-			for (integer icol = 1; icol <= ncol; icol ++)
-				result [irow] [icol] = exp (x->numericMatrix [irow] [icol]);
-		pushNumericMatrix (result.move());
-	} else {
-		Melder_throw (U"Cannot exponentiate (exp) ", x->whichText(), U".");
-	}
-}
-static void do_sinh () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : sinh (x->number));
-	} else {
-		Melder_throw (U"Cannot take the hyperbolic sine (sinh) of ", x->whichText(), U".");
-	}
-}
-static void do_cosh () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : cosh (x->number));
-	} else {
-		Melder_throw (U"Cannot take the hyperbolic cosine (cosh) of ", x->whichText(), U".");
-	}
-}
-static void do_tanh () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined : tanh (x->number));
-	} else {
-		Melder_throw (U"Cannot take the hyperbolic tangent (tanh) of ", x->whichText(), U".");
-	}
-}
-static void do_log2 () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined :
-			x->number <= 0.0 ? undefined : log (x->number) * NUMlog2e);
-	} else {
-		Melder_throw (U"Cannot take the base-2 logarithm (log2) of ", x->whichText(), U".");
-	}
-}
-static void do_ln () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined :
-			x->number <= 0.0 ? undefined : log (x->number));
-	} else {
-		Melder_throw (U"Cannot take the natural logarithm (ln) of ", x->whichText(), U".");
-	}
-}
-static void do_log10 () {
-	Stackel x = pop;
-	if (x->which == Stackel_NUMBER) {
-		pushNumber (isundef (x->number) ? undefined :
-			x->number <= 0.0 ? undefined : log10 (x->number));
-	} else {
-		Melder_throw (U"Cannot take the base-10 logarithm (log10) of ", x->whichText(), U".");
-	}
-}
+
+#define DO_NUM_WITH_TENSORS(function, formula, message)  \
+static void do_##function () { \
+	Stackel x = pop; \
+	if (x->which == Stackel_NUMBER) { \
+		const double xvalue = x->number; \
+		pushNumber (formula); \
+	} else if (x->which == Stackel_NUMERIC_VECTOR) { \
+		Melder_throw (U"The function " #function " requires a numeric argument, " \
+				"not a vector. Did you mean to use " #function "# instead?"); \
+	} else if (x->which == Stackel_NUMERIC_MATRIX) { \
+		Melder_throw (U"The function " #function " requires a numeric argument, " \
+				"not a matrix. Did you mean to use " #function "## instead?"); \
+	} else { \
+		Melder_throw (message, x->whichText(), \
+				U". The function " #function " requires a numeric argument"); \
+	} \
+} \
+static void do_##function##_VEC () { \
+	Stackel x = topOfStack; \
+	if (x->which == Stackel_NUMERIC_VECTOR) { \
+		if (x->owned) { \
+			const integer numberOfElements = x->numericVector.size; \
+			for (integer i = 1; i <= numberOfElements; i ++) { \
+				const double xvalue = x->numericVector [i]; \
+				x->numericVector [i] = isundef (xvalue) ? undefined : formula; \
+			} \
+		} else { \
+			pop; \
+			const integer numberOfElements = x->numericVector.size; \
+			autoVEC result = raw_VEC (numberOfElements); \
+			for (integer i = 1; i <= numberOfElements; i ++) { \
+				const double xvalue = x->numericVector [i]; \
+				result [i] = isundef (xvalue) ? undefined : formula; \
+			} \
+			pushNumericVector (result.move()); \
+		} \
+	} else { \
+		Melder_throw (message, x->whichText(), \
+				U". The function " #function " requires a vector argument"); \
+	} \
+} \
+static void do_##function##_MAT () { \
+	Stackel x = topOfStack; \
+	if (x->which == Stackel_NUMERIC_MATRIX) { \
+		if (x->owned) { \
+			const integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol; \
+			for (integer irow = 1; irow <= nrow; irow ++) { \
+				for (integer icol = 1; icol <= ncol; icol ++) { \
+					const double xvalue = x->numericMatrix [irow] [icol]; \
+					x->numericMatrix [irow] [icol] = isundef (xvalue) ? undefined : formula; \
+				} \
+			} \
+		} else { \
+			pop; \
+			const integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol; \
+			autoMAT result = raw_MAT (nrow, ncol); \
+			for (integer irow = 1; irow <= nrow; irow ++) { \
+				for (integer icol = 1; icol <= ncol; icol ++) { \
+					const double xvalue = x->numericMatrix [irow] [icol]; \
+					result [irow] [icol] = isundef (xvalue) ? undefined : formula; \
+				} \
+			} \
+			pushNumericMatrix (result.move()); \
+		} \
+	} else { \
+		Melder_throw (message, x->whichText(), \
+				U". The function " #function " requires a matrix argument"); \
+	} \
+}
+DO_NUM_WITH_TENSORS (abs, fabs (xvalue), U"Cannot take the absolute value (abs) of ")
+DO_NUM_WITH_TENSORS (round, floor (xvalue + 0.5), U"Cannot round ")
+DO_NUM_WITH_TENSORS (floor, Melder_roundDown (xvalue), U"Cannot round down (floor) ")
+DO_NUM_WITH_TENSORS (ceiling, Melder_roundUp (xvalue), U"Cannot round up (ceiling) ")
+DO_NUM_WITH_TENSORS (rectify, xvalue < 0.0 ? 0.0 : xvalue, U"Cannot rectify ")   // NaN-safe
+DO_NUM_WITH_TENSORS (sqrt, /*xvalue < 0.0 ? undefined :*/ sqrt (xvalue), U"Cannot take the square root (sqrt) of ")
+DO_NUM_WITH_TENSORS (sin, sin (xvalue), U"Cannot take the sine (sin) of ")
+DO_NUM_WITH_TENSORS (cos, cos (xvalue), U"Cannot take the cosine (cos) of ")
+DO_NUM_WITH_TENSORS (tan, tan (xvalue), U"Cannot take the tangent (tan) of ")
+DO_NUM_WITH_TENSORS (arcsin, /*fabs (xvalue) > 1.0 ? undefined :*/ asin (xvalue), U"Cannot take the arcsine (arcsin) of ")
+DO_NUM_WITH_TENSORS (arccos, /*fabs (xvalue) > 1.0 ? undefined :*/ acos (xvalue), U"Cannot take the arccosine (arccos) of ")
+DO_NUM_WITH_TENSORS (arctan, atan (xvalue), U"Cannot take the arctangent (arctan) of ")
+DO_NUM_WITH_TENSORS (exp, exp (xvalue), U"Cannot exponentiate (exp) ")
+DO_NUM_WITH_TENSORS (sinh, sinh (xvalue), U"Cannot take the hyperbolic sine (sinh) of ")
+DO_NUM_WITH_TENSORS (cosh, cosh (xvalue), U"Cannot take the hyperbolic cosine (cosh) of ")
+DO_NUM_WITH_TENSORS (tanh, tanh (xvalue), U"Cannot take the hyperbolic tangent (tanh) of ")
+DO_NUM_WITH_TENSORS (arcsinh, asinh (xvalue), U"Cannot take the hyperbolic arcsine (arcsinh) of ")
+DO_NUM_WITH_TENSORS (arccosh, acosh (xvalue), U"Cannot take the hyperbolic arccosine (arccosh) of ")
+DO_NUM_WITH_TENSORS (arctanh, atanh (xvalue), U"Cannot take the hyperbolic arctangent (arctanh) of ")
+DO_NUM_WITH_TENSORS (log2, /*xvalue <= 0.0 ? undefined :*/ log (xvalue) * NUMlog2e, U"Cannot take the base-2 logarithm (log2) of ")
+DO_NUM_WITH_TENSORS (ln, /*xvalue <= 0.0 ? undefined :*/ log (xvalue), U"Cannot take the natural logarithm (ln) of ")
+DO_NUM_WITH_TENSORS (log10, /*xvalue <= 0.0 ? undefined :*/ log10 (xvalue), U"Cannot take the base-10 logarithm (log10) of ")
+
 static void do_sum () {
 	Stackel x = pop;
 	if (x->which == Stackel_NUMERIC_VECTOR) {
@@ -7273,32 +7158,32 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case MINUS_: { do_minus ();
 } break; case POWER_: { do_power ();
 /********** Functions of 1 variable: **********/
-} break; case ABS_: { do_abs ();
-} break; case ROUND_: { do_round ();
-} break; case FLOOR_: { do_floor ();
-} break; case CEILING_: { do_ceiling ();
-} break; case RECTIFY_: { do_rectify ();
-} break; case RECTIFY_VEC_: { do_rectify_VEC ();
-} break; case RECTIFY_MAT_: { do_rectify_MAT ();
-} break; case SQRT_: { do_sqrt ();
-} break; case SIN_: { do_sin ();
-} break; case COS_: { do_cos ();
-} break; case TAN_: { do_tan ();
-} break; case ARCSIN_: { do_arcsin ();
-} break; case ARCCOS_: { do_arccos ();
-} break; case ARCTAN_: { do_arctan ();
+
+#define CASE_NUM_WITH_TENSORS(label, function)  \
+} break; case label: { function (); \
+} break; case label##VEC_: { function##_VEC (); \
+} break; case label##MAT_: { function##_MAT ();
+CASE_NUM_WITH_TENSORS (ABS_, do_abs)
+CASE_NUM_WITH_TENSORS (ROUND_, do_round)
+CASE_NUM_WITH_TENSORS (FLOOR_, do_floor)
+CASE_NUM_WITH_TENSORS (CEILING_, do_ceiling)
+CASE_NUM_WITH_TENSORS (RECTIFY_, do_rectify)
+CASE_NUM_WITH_TENSORS (SQRT_, do_sqrt)
+CASE_NUM_WITH_TENSORS (SIN_, do_sin)
+CASE_NUM_WITH_TENSORS (COS_, do_cos)
+CASE_NUM_WITH_TENSORS (TAN_, do_tan)
+CASE_NUM_WITH_TENSORS (ARCSIN_, do_arcsin)
+CASE_NUM_WITH_TENSORS (ARCCOS_, do_arccos)
+CASE_NUM_WITH_TENSORS (ARCTAN_, do_arctan)
 } break; case SINC_: { do_function_n_n (NUMsinc);
 } break; case SINCPI_: { do_function_n_n (NUMsincpi);
-} break; case EXP_: { do_exp ();
-} break; case EXP_VEC_: { do_exp_VEC ();
-} break; case EXP_MAT_: { do_exp_MAT ();
-} break; case SINH_: { do_sinh ();
-} break; case COSH_: { do_cosh ();
-} break; case TANH_: { do_tanh ();
-} break; case TANH_VEC_: { do_functionvec_n_n (tanh);
-} break; case ARCSINH_: { do_function_n_n (NUMarcsinh);
-} break; case ARCCOSH_: { do_function_n_n (NUMarccosh);
-} break; case ARCTANH_: { do_function_n_n (NUMarctanh);
+CASE_NUM_WITH_TENSORS (EXP_, do_exp)
+CASE_NUM_WITH_TENSORS (SINH_, do_sinh)
+CASE_NUM_WITH_TENSORS (COSH_, do_cosh)
+CASE_NUM_WITH_TENSORS (TANH_, do_tanh)
+CASE_NUM_WITH_TENSORS (ARCSINH_, do_arcsinh)
+CASE_NUM_WITH_TENSORS (ARCCOSH_, do_arccosh)
+CASE_NUM_WITH_TENSORS (ARCTANH_, do_arctanh)
 } break; case SIGMOID_: { do_function_n_n (NUMsigmoid);
 } break; case SIGMOID_VEC_: { do_functionvec_n_n (NUMsigmoid);
 } break; case SOFTMAX_VEC_: { do_softmax_VEC ();
@@ -7315,9 +7200,9 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case TRANSPOSE_MAT_: { do_transpose_MAT ();
 } break; case ROW_SUMS_VEC_: { do_rowSums_VEC ();
 } break; case COLUMN_SUMS_VEC_: { do_columnSums_VEC ();
-} break; case LOG2_: { do_log2 ();
-} break; case LN_: { do_ln ();
-} break; case LOG10_: { do_log10 ();
+CASE_NUM_WITH_TENSORS (LOG2_, do_log2)
+CASE_NUM_WITH_TENSORS (LN_, do_ln)
+CASE_NUM_WITH_TENSORS (LOG10_, do_log10)
 } break; case LN_GAMMA_: { do_function_n_n (NUMlnGamma);
 } break; case HERTZ_TO_BARK_: { do_function_n_n (NUMhertzToBark);
 } break; case BARK_TO_HERTZ_: { do_function_n_n (NUMbarkToHertz);


=====================================
sys/Interpreter.cpp
=====================================
@@ -1374,14 +1374,15 @@ static void assignToNumericVectorElement (Interpreter me, char32 *& p, const cha
 		MelderString_empty (& valueString);
 		autoMelderDivertInfo divert (& valueString);
 		MelderString_appendCharacter (& valueString, 1);   // will be overwritten by something totally different if any MelderInfo function is called....
-		int status = praat_executeCommand (me, p);
-		if (status == 0) {
+		bool status = praat_executeCommand (me, p);
+		if (! status) {
 			value = undefined;
-		} else if (valueString.string [0] == 1) {   // ...not overwritten by any MelderInfo function? then the return value will be the selected object
+		} else if (my returnType == kInterpreter_ReturnType::OBJECT_) {
 			int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
 			WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
 			if (numberOfSelectedObjects > 1)
-				Melder_throw (U"Multiple objects selected. Cannot assign object ID to vector element.");
+				Melder_throw (U"Multiple objects selected. Cannot assign object IDs to vector element. "
+						"Perhaps use a vector variable instead.");
 			if (numberOfSelectedObjects == 0)
 				Melder_throw (U"No objects selected. Cannot assign object ID to vector element.");
 			value = theCurrentPraatObjects -> list [selectedObject]. id;
@@ -1509,23 +1510,25 @@ static void assignToNumericMatrixElement (Interpreter me, char32 *& p, const cha
 		MelderString_empty (& valueString);
 		autoMelderDivertInfo divert (& valueString);
 		MelderString_appendCharacter (& valueString, 1);   // will be overwritten by something totally different if any MelderInfo function is called....
-		int status = praat_executeCommand (me, p);
-		if (status == 0) {
+		bool status = praat_executeCommand (me, p);
+		if (! status) {
 			value = undefined;
-		} else if (valueString.string [0] == 1) {   // ...not overwritten by any MelderInfo function? then the return value will be the selected object
+		} else if (my returnType == kInterpreter_ReturnType::OBJECT_) {
 			int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
 			WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
-			if (numberOfSelectedObjects > 1) {
-				Melder_throw (U"Multiple objects selected. Cannot assign object ID to matrix element.");
-			} else if (numberOfSelectedObjects == 0) {
+			if (numberOfSelectedObjects > 1)
+				Melder_throw (U"Multiple objects selected. Cannot assign object IDs to matrix element. "
+						"Perhaps use a vector variable instead.");
+			if (numberOfSelectedObjects == 0)
 				Melder_throw (U"No objects selected. Cannot assign object ID to matrix element.");
-			} else {
-				value = theCurrentPraatObjects -> list [selectedObject]. id;
-			}
+			value = theCurrentPraatObjects -> list [selectedObject]. id;
 		} else {
 			value = Melder_atof (valueString.string);   // including --undefined--
 		}
 	} else {
+		/*
+			Get the value of the formula.
+		*/
 		Interpreter_numericExpression (me, p, & value);
 	}
 	InterpreterVariable var = Interpreter_hasVariable (me, matrixName);
@@ -1599,8 +1602,11 @@ static void assignToStringArrayElement (Interpreter me, char32 *& p, const char3
 		*/
 		MelderString_empty (& valueString);
 		autoMelderDivertInfo divert (& valueString);
-		int status = praat_executeCommand (me, p);
-		value = ( status == 0 ? autostring32 () : Melder_dup (valueString.string) );
+		bool status = praat_executeCommand (me, p);
+		if (! status)
+			value = autostring32();
+		else
+			value = Melder_dup (valueString.string);
 	} else {
 		/*
 			Get the value of the formula.
@@ -1832,8 +1838,9 @@ void Interpreter_run (Interpreter me, char32 *text) {
 				trace (U"resume");
 				c0 = command2.string [0];   // resume in order to allow things like 'c$' = 5
 				if ((! Melder_isLetter (c0) || Melder_isUpperCaseLetter (c0)) && c0 != U'@' &&
-						! (c0 == U'.' && Melder_isLetter (command2.string [1]) && ! Melder_isUpperCaseLetter (command2.string [1]))) {
-					praat_executeCommand (me, command2.string);
+						! (c0 == U'.' && Melder_isLetter (command2.string [1]) && ! Melder_isUpperCaseLetter (command2.string [1])))
+				{
+					(void) praat_executeCommand (me, command2.string);
 				/*
 				 * Interpret control flow and variables.
 				 */
@@ -2045,7 +2052,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							/*
 								Make sure that lines like "echo = 3" will not be regarded as assignments.
 							*/
-							praat_executeCommand (me, command2.string);
+							(void) praat_executeCommand (me, command2.string);
 						} else
 							fail = true;
 						break;
@@ -2239,7 +2246,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							 * Make sure that lines like "print = 3" will not be regarded as assignments.
 							 */
 							if (command2.string [5] == U' ' || (str32nequ (command2.string + 5, U"line", 4) && (command2.string [9] == U' ' || command2.string [9] == U'\0'))) {
-								praat_executeCommand (me, command2.string);
+								(void) praat_executeCommand (me, command2.string);
 							} else
 								fail = true;
 						} else
@@ -2378,20 +2385,26 @@ void Interpreter_run (Interpreter me, char32 *text) {
 									p ++;   // go to first token after assignment
 								if (*p == U'\0')
 									Melder_throw (U"Missing right-hand expression in assignment to string array ", arrayName.string, U".");
+								InterpreterVariable var = Interpreter_lookUpVariable (me, arrayName.string);
 								if (isCommand (p)) {
 									/*
 										Statement like: lines$# = Get all strings
 									*/
-									praat_executeCommand (me, p);
-									if (my returnType != kInterpreter_ReturnType::STRINGARRAY_)
-										Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p), U"; not assigned to the string array variable \"", arrayName.string, U"\".");
-									InterpreterVariable var = Interpreter_lookUpVariable (me, arrayName.string);
-									var -> stringArrayValue = my returnedStringArray.move();
+									bool status = praat_executeCommand (me, p);
+									if (! status)
+										var -> stringArrayValue = autoSTRVEC();   // anything can have happened, including an incorrect returnType
+									else if (my returnType == kInterpreter_ReturnType::STRINGARRAY_)
+										var -> stringArrayValue = my returnedStringArray.move();
+									else
+										Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p),
+												U"; not assigned to the string array variable \"", arrayName.string, U"\".");
 								} else {
+									/*
+										Statement like: files$# = fileNames$# ("*.wav")
+									 */
 									STRVEC value;
 									bool owned;
 									Interpreter_stringArrayExpression (me, p, & value, & owned);
-									InterpreterVariable var = Interpreter_lookUpVariable (me, arrayName.string);
 									StringArrayVariable_move (var, value, owned);
 								}
 							} else if (*p == U'[') {
@@ -2506,12 +2519,18 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								*/
 								MelderString_empty (& valueString);   // empty because command may print nothing; also makes sure that valueString.string exists
 								autoMelderDivertInfo divert (& valueString);
-								int status = praat_executeCommand (me, p);
-								if (my returnType == kInterpreter_ReturnType::STRING_ || my returnType == kInterpreter_ReturnType::REAL_ || my returnType == kInterpreter_ReturnType::INTEGER_) {
-									InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
-									var -> stringValue = Melder_dup (status ? valueString.string : U"");
-								} else
-									Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p), U"; not assigned to the string variable \"", variableName, U"\".");
+								bool status = praat_executeCommand (me, p);
+								InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
+								if (! status)
+									var -> stringValue = Melder_dup (U"");   // anything can have happened, including an incorrect returnType
+								else if (my returnType == kInterpreter_ReturnType::STRING_ ||
+									my returnType == kInterpreter_ReturnType::REAL_ ||
+									my returnType == kInterpreter_ReturnType::INTEGER_
+								)
+									var -> stringValue = Melder_dup (valueString.string);
+								else
+									Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p),
+											U"; not assigned to the string variable \"", variableName, U"\".");
 							} else {
 								/*
 									Evaluate a string expression and assign the result to the variable.
@@ -2560,20 +2579,23 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								while (Melder_isHorizontalSpace (*p)) p ++;   // go to first token after assignment
 								if (*p == U'\0')
 									Melder_throw (U"Missing right-hand expression in assignment to matrix ", matrixName.string, U".");
+								InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
 								if (isCommand (p)) {
 									/*
 										Statement like: values## = Get all values
 									*/
-									praat_executeCommand (me, p);
-									if (my returnType != kInterpreter_ReturnType::REALMATRIX_)
-										Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p), U"; not assigned to the matrix variable \"", matrixName.string, U"\".");
-									InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
-									var -> numericMatrixValue = my returnedRealMatrix.move();
+									bool status = praat_executeCommand (me, p);
+									if (! status)
+										var -> numericMatrixValue = autoMAT();   // anything can have happened, including an incorrect returnType
+									else if (my returnType == kInterpreter_ReturnType::REALMATRIX_)
+										var -> numericMatrixValue = my returnedRealMatrix.move();
+									else
+										Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p),
+												U"; not assigned to the matrix variable \"", matrixName.string, U"\".");
 								} else {
 									MAT value;
 									bool owned;
 									Interpreter_numericMatrixExpression (me, p, & value, & owned);
-									InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
 									NumericMatrixVariable_move (var, value, owned);
 								}
 							} else if (*p == U'[') {
@@ -2679,20 +2701,26 @@ void Interpreter_run (Interpreter me, char32 *text) {
 									p ++;   // go to first token after assignment
 								if (*p == U'\0')
 									Melder_throw (U"Missing right-hand expression in assignment to vector ", vectorName.string, U".");
+								InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
 								if (isCommand (p)) {
 									/*
 										Statement like: times# = Get all times
 									*/
-									praat_executeCommand (me, p);
-									if (my returnType != kInterpreter_ReturnType::REALVECTOR_)
+									bool status = praat_executeCommand (me, p);
+									if (! status)
+										var -> numericVectorValue = autoVEC();   // anything can have happened, including an incorrect returnType
+									else if (my returnType == kInterpreter_ReturnType::OBJECT_) {
+										var -> numericVectorValue = autoVEC();
+										int IOBJECT;
+										WHERE (SELECTED) *var -> numericVectorValue. append() = ID;
+									} else if (my returnType == kInterpreter_ReturnType::REALVECTOR_)
+										var -> numericVectorValue = my returnedRealVector.move();
+									else
 										Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p), U"; not assigned to the vector variable \"", vectorName.string, U"\".");
-									InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
-									var -> numericVectorValue = my returnedRealVector.move();
 								} else {
 									VEC value;
 									bool owned;
 									Interpreter_numericVectorExpression (me, p, & value, & owned);
-									InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
 									NumericVectorVariable_move (var, value, owned);
 								}
 							} else if (*p == U'[') {
@@ -2790,7 +2818,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							/*
 								Command ends here: it may be a PraatShell command.
 							*/
-							praat_executeCommand (me, command2.string);
+							(void) praat_executeCommand (me, command2.string);
 							continue;   // next line
 						}
 						char32 *endOfVariable = p;
@@ -2833,7 +2861,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								Melder_assert (! result. stringResult);
 								Interpreter_anyExpression (me, index.string, & result);
 								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
-									double numericIndexValue = result. numericResult;
+									const double numericIndexValue = result. numericResult;
 									MelderString_append (& indexedVariableName, numericIndexValue);
 								} else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
 									MelderString_append (& indexedVariableName, U"\"", result. stringResult.get(), U"\"");
@@ -2853,7 +2881,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							/*
 								Not an assignment: perhaps a PraatShell command (select, echo, execute, pause ...).
 							*/
-							praat_executeCommand (me, variableName);
+							(void) praat_executeCommand (me, variableName);
 							continue;   // next line
 						}
 						p += ( typeOfAssignment == 0 ? 1 : 2 );
@@ -2874,24 +2902,23 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							MelderString_empty (& valueString);
 							autoMelderDivertInfo divert (& valueString);
 							MelderString_appendCharacter (& valueString, 1);   // will be overwritten by something totally different if any MelderInfo function is called...
-							int status = praat_executeCommand (me, p);
-							if (status == 0) {
-								value = undefined;
+							bool status = praat_executeCommand (me, p);
+							if (! status) {
+								value = undefined;   // anything can have happened, including an incorrect return type
 							} else if (my returnType == kInterpreter_ReturnType::OBJECT_) {
 								int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
 								WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
-								if (numberOfSelectedObjects > 1) {
-									Melder_throw (U"Multiple objects selected. Cannot assign object ID to variable.");
-								} else if (numberOfSelectedObjects == 0) {
+								if (numberOfSelectedObjects > 1)
+									Melder_throw (U"Multiple objects selected. Cannot assign object IDs to numeric variable. "
+											"Perhaps use a vector variable instead.");
+								if (numberOfSelectedObjects == 0)
 									Melder_throw (U"No objects selected. Cannot assign object ID to variable.");
-								} else {
-									value = theCurrentPraatObjects -> list [selectedObject]. id;
-								}
-							} else if (my returnType != kInterpreter_ReturnType::REAL_ && my returnType != kInterpreter_ReturnType::INTEGER_) {
-								Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p), U"; not assigned to the numeric variable \"", variableName, U"\".");
-							} else {
+								value = theCurrentPraatObjects -> list [selectedObject]. id;
+							} else if (my returnType == kInterpreter_ReturnType::REAL_ || my returnType == kInterpreter_ReturnType::INTEGER_) {
 								value = Melder_atof (valueString.string);   // including --undefined--
-							}
+							} else
+								Melder_throw (kInterpreter_ReturnType_errorMessage (my returnType, p),
+										U"; not assigned to the numeric variable \"", variableName, U"\".");
 						} else {
 							/*
 								Get the value of the formula.


=====================================
sys/praat.cpp
=====================================
@@ -1898,7 +1898,7 @@ void praat_run () {
 						char32 *newline = str32chr (line, U'\n');
 						if (newline) *newline = U'\0';
 						try {
-							praat_executeCommand (nullptr, line);
+							(void) praat_executeCommand (nullptr, line);   // should contain no cases of "nocheck"
 						} catch (MelderError) {
 							Melder_clearError ();   // ignore this line, but not necessarily the next
 						}


=====================================
sys/praat_script.cpp
=====================================
@@ -180,7 +180,7 @@ static int parseCommaSeparatedArguments (Interpreter interpreter, char32 *argume
 	return narg;
 }
 
-int praat_executeCommand (Interpreter interpreter, char32 *command) {
+bool praat_executeCommand (Interpreter interpreter, char32 *command) {
 	if (interpreter)
 		interpreter -> returnType = kInterpreter_ReturnType::VOID_;   // clear return type to its default
 
@@ -274,26 +274,26 @@ int praat_executeCommand (Interpreter interpreter, char32 *command) {
 			}
 		} else if (str32nequ (command, U"nowarn ", 7)) {
 			autoMelderWarningOff nowarn;
-			praat_executeCommand (interpreter, command + 7);
+			return praat_executeCommand (interpreter, command + 7);
 		} else if (str32nequ (command, U"noprogress ", 11)) {
 			autoMelderProgressOff noprogress;
-			praat_executeCommand (interpreter, command + 11);
+			return praat_executeCommand (interpreter, command + 11);
 		} else if (str32nequ (command, U"nocheck ", 8)) {
 			try {
-				praat_executeCommand (interpreter, command + 8);
+				return praat_executeCommand (interpreter, command + 8);
 			} catch (MelderError) {
 				Melder_clearError ();
-				return 0;
+				return false;
 			}
 		} else if (str32nequ (command, U"demo ", 5)) {
 			autoDemoOpen demo;
-			praat_executeCommand (interpreter, command + 5);
+			return praat_executeCommand (interpreter, command + 5);
 		} else if (str32nequ (command, U"asynchronous ", 13)) {
 			autoMelderAsynchronous asynchronous;
-			praat_executeCommand (interpreter, command + 13);
+			return praat_executeCommand (interpreter, command + 13);
 		} else if (str32nequ (command, U"pause ", 6) || str32equ (command, U"pause")) {
 			if (theCurrentPraatApplication -> batch)
-				return 1;   // in batch we ignore pause statements
+				return true;   // in batch we ignore pause statements
 			UiPause_begin (theCurrentPraatApplication -> topShell, U"stop or continue", interpreter);
 			UiPause_comment (str32equ (command, U"pause") ? U"..." : command + 6);
 			UiPause_end (1, 1, 0, U"Continue", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, interpreter);
@@ -532,7 +532,7 @@ int praat_executeCommand (Interpreter interpreter, char32 *command) {
 		}
 		praat_updateSelection ();
 	}
-	return 1;
+	return true;
 }
 
 void praat_executeCommandFromStandardInput (conststring32 programName) {
@@ -549,7 +549,7 @@ void praat_executeCommandFromStandardInput (conststring32 programName) {
 			*newLine = '\0';
 		autostring32 command32 = Melder_8to32 (command8);
 		try {
-			praat_executeCommand (nullptr, command32.get());
+			(void) praat_executeCommand (nullptr, command32.get());
 		} catch (MelderError) {
 			Melder_flushError (programName, U": Command \"", Melder_peek8to32 (command8), U"\" not executed.");
 		}


=====================================
sys/praat_script.h
=====================================
@@ -2,7 +2,7 @@
 #define _praat_script_h_
 /* praat_script.h
  *
- * Copyright (C) 1992-2005,2007,2009-2016,2018 Paul Boersma
+ * Copyright (C) 1992-2005,2007,2009-2016,2018,2021 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
 
 #include "Interpreter.h"
 
-int praat_executeCommand (Interpreter me, char32 *command);
+bool praat_executeCommand (Interpreter me, char32 *command);   // returns false only if nocheck cancelled an error
 void praat_executeCommandFromStandardInput (conststring32 programName);
 void praat_executeScriptFromFile (MelderFile file, conststring32 arguments);
 void praat_executeScriptFromFileName (conststring32 fileName, integer narg, Stackel args);


=====================================
sys/praat_version.h
=====================================
@@ -1,5 +1,5 @@
-#define PRAAT_VERSION_STR 6.1.49
-#define PRAAT_VERSION_NUM 6149
+#define PRAAT_VERSION_STR 6.1.50
+#define PRAAT_VERSION_NUM 6150
 #define PRAAT_YEAR 2021
 #define PRAAT_MONTH June
-#define PRAAT_DAY 12
+#define PRAAT_DAY 20


=====================================
test/dwtools/KlattTable.praat
=====================================
@@ -0,0 +1,17 @@
+# KlattTable.praat
+# Paul Boersma 2021-06-16
+
+klattTable = Create KlattTable example
+table = To Table
+
+#
+# Test whether list initialization worked correctly.
+#
+numberOfColumns = Get number of columns
+assert numberOfColumns = 40
+firstColumnLabel$ = Get column label: 1
+assert firstColumnLabel$ = "f0"
+lastColumnLabel$ = Get column label: numberOfColumns
+assert lastColumnLabel$ = "gain"
+
+removeObject: klattTable, table


=====================================
test/fon/Spectrum.praat
=====================================
@@ -0,0 +1,12 @@
+s1 = Create Sound from formula: "s1", 1, 0, 1, 44100, "1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)"
+s2 = Copy: "s2"
+plusObject: s1
+asserterror Multiple objects selected. Cannot assign object IDs to numeric variable. Perhaps use a vector variable instead.
+a = To Spectrum: "yes"
+removeObject: "Spectrum s1", "Spectrum s2"
+selectObject: s1, s2
+a# = To Spectrum: "yes"
+assert a# = { s1, s2 } + 4
+writeInfoLine: a#
+plusObject: s1, s2
+Remove
\ No newline at end of file


=====================================
test/script/undefined.praat
=====================================
@@ -40,6 +40,31 @@ assert 0 * undefined = undefined
 assert 1 / undefined = undefined
 assert 0 ^ undefined = undefined
 assert undefined ^ undefined = undefined
+assert abs (undefined) = undefined
+assert round (undefined) = undefined
+assert floor (undefined) = undefined
+assert ceiling (undefined) = undefined
+assert rectify (undefined) = undefined
+assert sin (undefined) = undefined
+assert cos (undefined) = undefined
+assert tan (undefined) = undefined
+assert arcsin (undefined) = undefined
+assert arccos (undefined) = undefined
+assert arctan (undefined) = undefined
+assert sinh (undefined) = undefined
+assert cosh (undefined) = undefined
+assert tanh (undefined) = undefined
+assert arcsinh (undefined) = undefined
+assert arccosh (undefined) = undefined
+assert arctanh (undefined) = undefined
+assert exp (undefined) = undefined
+assert log2 (undefined) = undefined
+assert ln (undefined) = undefined
+assert log10 (undefined) = undefined
+assert exp (1e6) = undefined
+assert 0 * exp (1e6) = undefined
+assert sqrt (-1) = undefined
+assert 0 * sqrt (-1) = undefined
 
 #
 # Propagation within larger expressions.
@@ -58,4 +83,5 @@ assert 0 ^ 0 = 1
 ;assert undefined ^ 0 = 1
 assert undefined ^ 0 = undefined
 ;assert sqrt (-1) ^ 0 = 1
-assert sqrt (-1) ^ 0 = undefined
\ No newline at end of file
+assert sqrt (-1) ^ 0 = undefined
+



View it on GitLab: https://salsa.debian.org/med-team/praat/-/commit/eb03c933f751a2ce1ec787c296ce8325ae50b258

-- 
View it on GitLab: https://salsa.debian.org/med-team/praat/-/commit/eb03c933f751a2ce1ec787c296ce8325ae50b258
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20210704/7a952065/attachment-0001.htm>


More information about the debian-med-commit mailing list