GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
jsondecode.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2020-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include "defun.h"
31 #include "error.h"
32 #include "errwarn.h"
33 #include "oct-string.h"
34 #include "ovl.h"
35 #include "utils.h"
36 
37 #if defined (HAVE_RAPIDJSON)
38 # include <rapidjson/document.h>
39 # include <rapidjson/error/en.h>
40 #endif
41 
43 
44 #if defined (HAVE_RAPIDJSON)
45 
46 static octave_value
47 decode (const rapidjson::Value& val,
48  const octave::make_valid_name_options *options);
49 
50 //! Decodes a numerical JSON value into a scalar number.
51 //!
52 //! @param val JSON value that is guaranteed to be a numerical value.
53 //!
54 //! @return @ref octave_value that contains the numerical value of @p val.
55 //!
56 //! @b Example:
57 //!
58 //! @code{.cc}
59 //! rapidjson::Document d;
60 //! d.Parse ("123");
61 //! octave_value num = decode_number (d);
62 //! @endcode
63 
64 static octave_value
65 decode_number (const rapidjson::Value& val)
66 {
67  if (val.IsUint ())
68  return octave_value (val.GetUint ());
69  else if (val.IsInt ())
70  return octave_value (val.GetInt ());
71  else if (val.IsUint64 ())
72  return octave_value (val.GetUint64 ());
73  else if (val.IsInt64 ())
74  return octave_value (val.GetInt64 ());
75  else if (val.IsDouble ())
76  return octave_value (val.GetDouble ());
77  else
78  error ("jsondecode: unidentified type");
79 }
80 
81 //! Decodes a JSON object into a scalar struct.
82 //!
83 //! @param val JSON value that is guaranteed to be a JSON object.
84 //! @param options @c ReplacementStyle and @c Prefix options with their values.
85 //!
86 //! @return @ref octave_value that contains the equivalent scalar struct of @p val.
87 //!
88 //! @b Example:
89 //!
90 //! @code{.cc}
91 //! rapidjson::Document d;
92 //! d.Parse ("{\"a\": 1, \"b\": 2}");
93 //! octave_value struct = decode_object (d, octave_value_list ());
94 //! @endcode
95 
96 static octave_value
97 decode_object (const rapidjson::Value& val,
98  const octave::make_valid_name_options *options)
99 {
100  octave_scalar_map retval;
101 
102  for (const auto& pair : val.GetObject ())
103  {
104  // Validator function "matlab.lang.makeValidName" to guarantee legitimate
105  // variable name.
106  std::string varname = pair.name.GetString ();
107  if (options != nullptr)
108  octave::make_valid_name (varname, *options);
109  retval.assign (varname, decode (pair.value, options));
110  }
111 
112  return retval;
113 }
114 
115 //! Decodes a JSON array that contains only numerical or null values
116 //! into an NDArray.
117 //!
118 //! @param val JSON value that is guaranteed to be a numeric array.
119 //!
120 //! @return @ref octave_value that contains the equivalent NDArray of @p val.
121 //!
122 //! @b Example:
123 //!
124 //! @code{.cc}
125 //! rapidjson::Document d;
126 //! d.Parse ("[1, 2, 3, 4]");
127 //! octave_value numeric_array = decode_numeric_array (d);
128 //! @endcode
129 
130 static octave_value
131 decode_numeric_array (const rapidjson::Value& val)
132 {
133  NDArray retval (dim_vector (val.Size (), 1));
134  octave_idx_type index = 0;
135  for (const auto& elem : val.GetArray ())
136  retval(index++) = elem.IsNull () ? octave_NaN
137  : decode_number (elem).double_value ();
138  return retval;
139 }
140 
141 //! Decodes a JSON array that contains only boolean values into a boolNDArray.
142 //!
143 //! @param val JSON value that is guaranteed to be a boolean array.
144 //!
145 //! @return @ref octave_value that contains the equivalent boolNDArray of @p val.
146 //!
147 //! @b Example:
148 //!
149 //! @code{.cc}
150 //! rapidjson::Document d;
151 //! d.Parse ("[true, false, true]");
152 //! octave_value boolean_array = decode_boolean_array (d);
153 //! @endcode
154 
155 static octave_value
156 decode_boolean_array (const rapidjson::Value& val)
157 {
158  boolNDArray retval (dim_vector (val.Size (), 1));
159  octave_idx_type index = 0;
160  for (const auto& elem : val.GetArray ())
161  retval(index++) = elem.GetBool ();
162  return retval;
163 }
164 
165 //! Decodes a JSON array that contains different types
166 //! or string values only into a Cell.
167 //!
168 //! @param val JSON value that is guaranteed to be a mixed or string array.
169 //! @param options @c ReplacementStyle and @c Prefix options with their values.
170 //!
171 //! @return @ref octave_value that contains the equivalent Cell of @p val.
172 //!
173 //! @b Example (decoding a string array):
174 //!
175 //! @code{.cc}
176 //! rapidjson::Document d;
177 //! d.Parse ("[\"foo\", \"bar\", \"baz\"]");
178 //! octave_value cell = decode_string_and_mixed_array (d, octave_value_list ());
179 //! @endcode
180 //!
181 //! @b Example (decoding a mixed array):
182 //!
183 //! @code{.cc}
184 //! rapidjson::Document d;
185 //! d.Parse ("[\"foo\", 123, true]");
186 //! octave_value cell = decode_string_and_mixed_array (d, octave_value_list ());
187 //! @endcode
188 
189 static octave_value
190 decode_string_and_mixed_array (const rapidjson::Value& val,
191  const octave::make_valid_name_options *options)
192 {
193  Cell retval (dim_vector (val.Size (), 1));
194  octave_idx_type index = 0;
195  for (const auto& elem : val.GetArray ())
196  retval(index++) = decode (elem, options);
197  return retval;
198 }
199 
200 //! Decodes a JSON array that contains only objects into a Cell or struct array
201 //! depending on the similarity of the objects' keys.
202 //!
203 //! @param val JSON value that is guaranteed to be an object array.
204 //! @param options @c ReplacementStyle and @c Prefix options with their values.
205 //!
206 //! @return @ref octave_value that contains the equivalent Cell
207 //! or struct array of @p val.
208 //!
209 //! @b Example (returns a struct array):
210 //!
211 //! @code{.cc}
212 //! rapidjson::Document d;
213 //! d.Parse ("[{\"a\":1,\"b\":2},{\"a\":3,\"b\":4}]");
214 //! octave_value object_array = decode_object_array (d, octave_value_list ());
215 //! @endcode
216 //!
217 //! @b Example (returns a Cell):
218 //!
219 //! @code{.cc}
220 //! rapidjson::Document d;
221 //! d.Parse ("[{\"a\":1,\"b\":2},{\"b\":3,\"a\":4}]");
222 //! octave_value object_array = decode_object_array (d, octave_value_list ());
223 //! @endcode
224 
225 static octave_value
226 decode_object_array (const rapidjson::Value& val,
227  const octave::make_valid_name_options *options)
228 {
229  Cell struct_cell = decode_string_and_mixed_array (val, options).cell_value ();
230  string_vector field_names = struct_cell(0).scalar_map_value ().fieldnames ();
231 
232  bool same_field_names = true;
233  for (octave_idx_type i = 1; i < struct_cell.numel (); ++i)
234  if (field_names.std_list ()
235  != struct_cell(i).scalar_map_value ().fieldnames ().std_list ())
236  {
237  same_field_names = false;
238  break;
239  }
240 
241  if (same_field_names)
242  {
243  octave_map struct_array;
244  dim_vector struct_array_dims = dim_vector (struct_cell.numel (), 1);
245 
246  if (field_names.numel ())
247  {
248  Cell value (struct_array_dims);
249  for (octave_idx_type i = 0; i < field_names.numel (); ++i)
250  {
251  for (octave_idx_type k = 0; k < struct_cell.numel (); ++k)
252  value(k) = struct_cell(k).scalar_map_value ()
253  .getfield (field_names(i));
254  struct_array.assign (field_names(i), value);
255  }
256  }
257  else
258  struct_array.resize (struct_array_dims, true);
259 
260  return struct_array;
261  }
262  else
263  return struct_cell;
264 }
265 
266 //! Decodes a JSON array that contains only arrays into a Cell or an NDArray
267 //! depending on the dimensions and element types of the sub-arrays.
268 //!
269 //! @param val JSON value that is guaranteed to be an array of arrays.
270 //! @param options @c ReplacementStyle and @c Prefix options with their values.
271 //!
272 //! @return @ref octave_value that contains the equivalent Cell
273 //! or NDArray of @p val.
274 //!
275 //! @b Example (returns an NDArray):
276 //!
277 //! @code{.cc}
278 //! rapidjson::Document d;
279 //! d.Parse ("[[1, 2], [3, 4]]");
280 //! octave_value array = decode_array_of_arrays (d, octave_value_list ());
281 //! @endcode
282 //!
283 //! @b Example (returns a Cell):
284 //!
285 //! @code{.cc}
286 //! rapidjson::Document d;
287 //! d.Parse ("[[1, 2], [3, 4, 5]]");
288 //! octave_value cell = decode_array_of_arrays (d, octave_value_list ());
289 //! @endcode
290 
291 static octave_value
292 decode_array_of_arrays (const rapidjson::Value& val,
293  const octave::make_valid_name_options *options)
294 {
295  // Some arrays should be decoded as NDArrays and others as cell arrays
296  Cell cell = decode_string_and_mixed_array (val, options).cell_value ();
297 
298  // Only arrays with sub-arrays of booleans and numericals will return NDArray
299  bool is_bool = cell(0).is_bool_matrix ();
300  bool is_struct = cell(0).isstruct ();
301  string_vector field_names = is_struct ? cell(0).map_value ().fieldnames ()
302  : string_vector ();
303  dim_vector sub_array_dims = cell(0).dims ();
304  octave_idx_type sub_array_ndims = cell(0).ndims ();
305  octave_idx_type cell_numel = cell.numel ();
306  for (octave_idx_type i = 0; i < cell_numel; ++i)
307  {
308  // If one element is cell return the cell array as at least one of the
309  // sub-arrays area either an array of: strings, objects or mixed array
310  if (cell(i).iscell ())
311  return cell;
312  // If not the same dim of elements or dim = 0, return cell array
313  if (cell(i).dims () != sub_array_dims || sub_array_dims == dim_vector ())
314  return cell;
315  // If not numeric sub-arrays only or bool sub-arrays only,
316  // return cell array
317  if (cell(i).is_bool_matrix () != is_bool)
318  return cell;
319  // If not struct arrays only, return cell array
320  if (cell(i).isstruct () != is_struct)
321  return cell;
322  // If struct arrays have different fields, return cell array
323  if (is_struct && (field_names.std_list ()
324  != cell(i).map_value ().fieldnames ().std_list ()))
325  return cell;
326  }
327 
328  // Calculate the dims of the output array
329  dim_vector array_dims;
330  array_dims.resize (sub_array_ndims + 1);
331  array_dims(0) = cell_numel;
332  for (auto i = 1; i < sub_array_ndims + 1; i++)
333  array_dims(i) = sub_array_dims(i-1);
334 
335  if (is_struct)
336  {
337  octave_map struct_array;
338  array_dims.chop_trailing_singletons ();
339 
340  if (field_names.numel ())
341  {
342  Cell value (array_dims);
343  octave_idx_type sub_array_numel = sub_array_dims.numel ();
344 
345  for (octave_idx_type j = 0; j < field_names.numel (); ++j)
346  {
347  // Populate the array with specific order to generate
348  // MATLAB-identical output.
349  for (octave_idx_type k = 0; k < cell_numel; ++k)
350  {
351  Cell sub_array_value = cell(k).map_value ()
352  .getfield (field_names(j));
353  for (octave_idx_type i = 0; i < sub_array_numel; ++i)
354  value(k + i * cell_numel) = sub_array_value(i);
355  }
356  struct_array.assign (field_names(j), value);
357  }
358  }
359  else
360  struct_array.resize(array_dims, true);
361 
362  return struct_array;
363  }
364  else
365  {
366  NDArray array (array_dims);
367 
368  // Populate the array with specific order to generate MATLAB-identical
369  // output.
370  octave_idx_type sub_array_numel = array.numel () / cell_numel;
371  for (octave_idx_type k = 0; k < cell_numel; ++k)
372  {
373  NDArray sub_array_value = cell(k).array_value ();
374  for (octave_idx_type i = 0; i < sub_array_numel; ++i)
375  array(k + i * cell_numel) = sub_array_value(i);
376  }
377 
378  if (is_bool)
379  return boolNDArray (array);
380  else
381  return array;
382  }
383 }
384 
385 //! Decodes any type of JSON arrays. This function only serves as an interface
386 //! by choosing which function to call from the previous functions.
387 //!
388 //! @param val JSON value that is guaranteed to be an array.
389 //! @param options @c ReplacementStyle and @c Prefix options with their values.
390 //!
391 //! @return @ref octave_value that contains the output of decoding @p val.
392 //!
393 //! @b Example:
394 //!
395 //! @code{.cc}
396 //! rapidjson::Document d;
397 //! d.Parse ("[[1, 2], [3, 4, 5]]");
398 //! octave_value array = decode_array (d, octave_value_list ());
399 //! @endcode
400 
401 static octave_value
402 decode_array (const rapidjson::Value& val,
403  const octave::make_valid_name_options *options)
404 {
405  // Handle empty arrays
406  if (val.Empty ())
407  return NDArray ();
408 
409  // Compare with other elements to know if the array has multiple types
410  rapidjson::Type array_type = val[0].GetType ();
411  // Check if the array is numeric and if it has multiple types
412  bool same_type = true;
413  bool is_numeric = true;
414  for (const auto& elem : val.GetArray ())
415  {
416  rapidjson::Type current_elem_type = elem.GetType ();
417  if (is_numeric && ! (current_elem_type == rapidjson::kNullType
418  || current_elem_type == rapidjson::kNumberType))
419  is_numeric = false;
420  if (same_type && (current_elem_type != array_type))
421  // RapidJSON doesn't have kBoolean Type it has kTrueType and kFalseType
422  if (! ((current_elem_type == rapidjson::kTrueType
423  && array_type == rapidjson::kFalseType)
424  || (current_elem_type == rapidjson::kFalseType
425  && array_type == rapidjson::kTrueType)))
426  same_type = false;
427  }
428 
429  if (is_numeric)
430  return decode_numeric_array (val);
431 
432  if (same_type && (array_type != rapidjson::kStringType))
433  {
434  if (array_type == rapidjson::kTrueType
435  || array_type == rapidjson::kFalseType)
436  return decode_boolean_array (val);
437  else if (array_type == rapidjson::kObjectType)
438  return decode_object_array (val, options);
439  else if (array_type == rapidjson::kArrayType)
440  return decode_array_of_arrays (val, options);
441  else
442  error ("jsondecode: unidentified type");
443  }
444  else
445  return decode_string_and_mixed_array (val, options);
446 }
447 
448 //! Decodes any JSON value. This function only serves as an interface
449 //! by choosing which function to call from the previous functions.
450 //!
451 //! @param val JSON value.
452 //! @param options @c ReplacementStyle and @c Prefix options with their values.
453 //!
454 //! @return @ref octave_value that contains the output of decoding @p val.
455 //!
456 //! @b Example:
457 //!
458 //! @code{.cc}
459 //! rapidjson::Document d;
460 //! d.Parse ("[{\"a\":1,\"b\":2},{\"b\":3,\"a\":4}]");
461 //! octave_value value = decode (d, octave_value_list ());
462 //! @endcode
463 
464 static octave_value
465 decode (const rapidjson::Value& val,
466  const octave::make_valid_name_options *options)
467 {
468  if (val.IsBool ())
469  return val.GetBool ();
470  else if (val.IsNumber ())
471  return decode_number (val);
472  else if (val.IsString ())
473  return val.GetString ();
474  else if (val.IsObject ())
475  return decode_object (val, options);
476  else if (val.IsNull ())
477  return NDArray ();
478  else if (val.IsArray ())
479  return decode_array (val, options);
480  else
481  error ("jsondecode: unidentified type");
482 }
483 
484 #endif
485 
486 DEFUN (jsondecode, args, ,
487  doc: /* -*- texinfo -*-
488 @deftypefn {} {@var{object} =} jsondecode (@var{JSON_txt})
489 @deftypefnx {} {@var{object} =} jsondecode (@dots{}, "ReplacementStyle", @var{rs})
490 @deftypefnx {} {@var{object} =} jsondecode (@dots{}, "Prefix", @var{pfx})
491 @deftypefnx {} {@var{object} =} jsondecode (@dots{}, "makeValidName", @var{TF})
492 
493 Decode text that is formatted in JSON.
494 
495 The input @var{JSON_txt} is a string that contains JSON text.
496 
497 The output @var{object} is an Octave object that contains the result of
498 decoding @var{JSON_txt}.
499 
500 For more information about the options @qcode{"ReplacementStyle"} and
501 @qcode{"Prefix"},
502 @pxref{XREFmatlab_lang_makeValidName,,@code{matlab.lang.makeValidName}}.
503 
504 If the value of the option @qcode{"makeValidName"} is false then names
505 will not be changed by @code{matlab.lang.makeValidName} and the
506 @qcode{"ReplacementStyle"} and @qcode{"Prefix"} options will be ignored.
507 
508 NOTE: Decoding and encoding JSON text is not guaranteed to reproduce the
509 original text as some names may be changed by @code{matlab.lang.makeValidName}.
510 
511 This table shows the conversions from JSON data types to Octave data types:
512 
513 @multitable @columnfractions 0.50 0.50
514 @headitem JSON data type @tab Octave data type
515 @item Boolean @tab scalar logical
516 @item Number @tab scalar double
517 @item String @tab vector of characters
518 @item Object @tab scalar struct (field names of the struct may be different from the keys of the JSON object due to @code{matlab_lang_makeValidName}
519 @item null, inside a numeric array @tab @code{NaN}
520 @item null, inside a non-numeric array @tab empty double array @code{[]}
521 @item Array, of different data types @tab cell array
522 @item Array, of Booleans @tab logical array
523 @item Array, of Numbers @tab double array
524 @item Array, of Strings @tab cell array of character vectors (@code{cellstr})
525 @item Array of Objects, same field names @tab struct array
526 @item Array of Objects, different field names @tab cell array of scalar structs
527 @end multitable
528 
529 Examples:
530 
531 @example
532 @group
533 jsondecode ('[1, 2, null, 3]')
534  @result{} ans =
535 
536  1
537  2
538  NaN
539  3
540 @end group
541 
542 @group
543 jsondecode ('["foo", "bar", ["foo", "bar"]]')
544  @result{} ans =
545  @{
546  [1,1] = foo
547  [2,1] = bar
548  [3,1] =
549  @{
550  [1,1] = foo
551  [2,1] = bar
552  @}
553 
554  @}
555 @end group
556 
557 @group
558 jsondecode ('@{"nu#m#ber": 7, "s#tr#ing": "hi"@}', ...
559  'ReplacementStyle', 'delete')
560  @result{} scalar structure containing the fields:
561 
562  number = 7
563  string = hi
564 @end group
565 
566 @group
567 jsondecode ('@{"nu#m#ber": 7, "s#tr#ing": "hi"@}', ...
568  'makeValidName', false)
569  @result{} scalar structure containing the fields:
570 
571  nu#m#ber = 7
572  s#tr#ing = hi
573 @end group
574 
575 @group
576 jsondecode ('@{"1": "one", "2": "two"@}', 'Prefix', 'm_')
577  @result{} scalar structure containing the fields:
578 
579  m_1 = one
580  m_2 = two
581 @end group
582 @end example
583 
584 @seealso{jsonencode, matlab.lang.makeValidName}
585 @end deftypefn */)
586 {
587 #if defined (HAVE_RAPIDJSON)
588 
589  int nargin = args.length ();
590 
591  // makeValidName options are pairs, the number of arguments must be odd.
592  if (! (nargin % 2))
593  print_usage ();
594 
595  // Detect if the user wants to use makeValidName
596  bool use_makeValidName = true;
597  octave_value_list make_valid_name_params;
598  for (auto i = 1; i < nargin; i = i + 2)
599  {
600  std::string parameter = args(i).xstring_value ("jsondecode: "
601  "option argument must be a string");
602  if (string::strcmpi (parameter, "makeValidName"))
603  {
604  use_makeValidName = args(i + 1).xbool_value ("jsondecode: "
605  "'makeValidName' value must be a bool");
606  }
607  else
608  make_valid_name_params.append (args.slice(i, 2));
609  }
610 
611  make_valid_name_options *options
612  = use_makeValidName ? new make_valid_name_options (make_valid_name_params)
613  : nullptr;
614 
615  unwind_action del_opts ([options] () { if (options) delete options; });
616 
617  if (! args(0).is_string ())
618  error ("jsondecode: JSON_TXT must be a character string");
619 
620  std::string json = args(0).string_value ();
621  rapidjson::Document d;
622  // DOM is chosen instead of SAX as SAX publishes events to a handler that
623  // decides what to do depending on the event only. This will cause a
624  // problem in decoding JSON arrays as the output may be an array or a cell
625  // and that doesn't only depend on the event (startArray) but also on the
626  // types of the elements inside the array.
627  d.Parse <rapidjson::kParseNanAndInfFlag> (json.c_str ());
628 
629  if (d.HasParseError ())
630  error ("jsondecode: parse error at offset %u: %s\n",
631  static_cast<unsigned int> (d.GetErrorOffset ()) + 1,
632  rapidjson::GetParseError_En (d.GetParseError ()));
633 
634  return decode (d, options);
635 
636 #else
637 
638  octave_unused_parameter (args);
639 
640  err_disabled_feature ("jsondecode", "JSON decoding through RapidJSON");
641 
642 #endif
643 }
644 
645 /*
646 Functional BIST tests are located in test/json/jsondecode_BIST.tst
647 
648 ## Input validation tests
649 %!testif HAVE_RAPIDJSON
650 %! fail ("jsondecode ()");
651 %! fail ("jsondecode ('1', 2)");
652 %! fail ("jsondecode (1)", "JSON_TXT must be a character string");
653 %! fail ("jsondecode ('12-')", "parse error at offset 3");
654 
655 */
656 
657 OCTAVE_END_NAMESPACE(octave)
int ndims() const
Size of the specified dimension.
Definition: Array.h:671
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:503
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:335
void chop_trailing_singletons()
Definition: dim-vector.h:164
void resize(int n, int fill_value=0)
Definition: dim-vector.h:272
Helper class for make_valid_name function calls.
Definition: utils.h:57
void resize(const dim_vector &dv, bool fill=false)
Definition: oct-map.cc:574
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:344
void assign(const std::string &k, const octave_value &val)
Definition: oct-map.h:230
octave_value_list & append(const octave_value &val)
Definition: ovl.cc:98
octave_idx_type numel() const
Definition: str-vec.h:100
std::list< std::string > std_list() const
Definition: str-vec.cc:172
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void() error(const char *fmt,...)
Definition: error.cc:988
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
#define octave_NaN
Definition: lo-ieee.h:46
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
bool make_valid_name(std::string &str, const make_valid_name_options &options)
Definition: utils.cc:137
bool strcmpi(const T &str_a, const T &str_b)
True if strings are the same, ignoring case.
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))