FlightGear next
nasal-props.cxx
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#include "config.h"
4
5#include <cstring>
6
7#include <simgear/nasal/nasal.h>
8#include <simgear/props/props.hxx>
9#include <simgear/props/vectorPropTemplates.hxx>
10
11#include <Main/globals.hxx>
12
13#include "NasalSys.hxx"
14#include "NasalSys_private.hxx"
15
16using namespace std;
17
18// Implementation of a Nasal wrapper for the SGPropertyNode class,
19// using the Nasal "ghost" (er... Garbage collection Handle for
20// OutSide Thingy) facility.
21//
22// Note that these functions appear in Nasal with prepended
23// underscores. They work on the low-level "ghost" objects and aren't
24// intended for use from user code, but from Nasal code you will find
25// in props.nas. That is where the Nasal props.Node class is defined,
26// which provides a saner interface along the lines of SGPropertyNode.
27
28static void propNodeGhostDestroy(void* ghost)
29{
30 SGPropertyNode* prop = static_cast<SGPropertyNode*>(ghost);
31 if (!SGPropertyNode::put(prop)) delete prop;
32}
33
34naGhostType PropNodeGhostType = { propNodeGhostDestroy, "prop", nullptr, nullptr };
35
36naRef propNodeGhostCreate(naContext c, SGPropertyNode* ghost)
37{
38 if(!ghost) return naNil();
39 SGPropertyNode::get(ghost);
40 return naNewGhost(c, &PropNodeGhostType, ghost);
41}
42
43naRef FGNasalSys::propNodeGhost(SGPropertyNode* handle)
44{
45 return propNodeGhostCreate(d->_context, handle);
46}
47
48SGPropertyNode* ghostToPropNode(naRef ref)
49{
50 if (!naIsGhost(ref) || (naGhost_type(ref) != &PropNodeGhostType))
51 return NULL;
52
53 return static_cast<SGPropertyNode*>(naGhost_ptr(ref));
54}
55
56#define NASTR(s) s ? naStr_fromdata(naNewString(c),(char*)(s),strlen(s)) : naNil()
57
58//
59// Standard header for the extension functions. It turns the "ghost"
60// found in arg[0] into a SGPropertyNode_ptr, and then "unwraps" the
61// vector found in the second argument into a normal-looking args
62// array. This allows the Nasal handlers to do things like:
63// Node.getChild = func { _getChild(me.ghost, arg) }
64//
65#define NODENOARG() \
66 if(argc < 2 || !naIsGhost(args[0]) || \
67 naGhost_type(args[0]) != &PropNodeGhostType) \
68 naRuntimeError(c, "bad argument to props function"); \
69 SGPropertyNode_ptr node = static_cast<SGPropertyNode*>(naGhost_ptr(args[0]));
70
71#define NODEARG() \
72 NODENOARG(); \
73 naRef argv = args[1]
74
75//
76// Pops the first argument as a relative path if the first condition
77// is true (e.g. argc > 1 for getAttribute) and if it is a string.
78// If the second confition is true, then another is popped to specify
79// if the node should be created (i.e. like the second argument to
80// getNode())
81//
82// Note that this makes the function return nil if the node doesn't
83// exist, so all functions with a relative_path parameter will
84// return nil if the specified node does not exist.
85//
86#define MOVETARGET(cond1, create) \
87 if(cond1) { \
88 naRef name = naVec_get(argv, 0); \
89 if(naIsString(name)) { \
90 try { \
91 node = node->getNode(naStr_data(name), create); \
92 } catch(const string& err) { \
93 naRuntimeError(c, (char *)err.c_str()); \
94 return naNil(); \
95 } \
96 if(!node) return naNil(); \
97 naVec_removefirst(argv); /* pop only if we were successful */ \
98 } \
99 }
100
101
102// Get the type of a property (returns a string).
103// Forms:
104// props.Node.getType(string relative_path);
105// props.Node.getType();
106static naRef f_getType(naContext c, naRef me, int argc, naRef* args)
107{
108 using namespace simgear;
109 NODEARG();
110 MOVETARGET(naVec_size(argv) > 0, false);
111 const char* t = "unknown";
112 switch(node->getType()) {
113 case props::NONE: t = "NONE"; break;
114 case props::ALIAS: t = "ALIAS"; break;
115 case props::BOOL: t = "BOOL"; break;
116 case props::INT: t = "INT"; break;
117 case props::LONG: t = "LONG"; break;
118 case props::FLOAT: t = "FLOAT"; break;
119 case props::DOUBLE: t = "DOUBLE"; break;
120 case props::STRING: t = "STRING"; break;
121 case props::UNSPECIFIED: t = "UNSPECIFIED"; break;
122 case props::VEC3D: t = "VEC3D"; break;
123 case props::VEC4D: t = "VEC4D"; break;
124 case props::EXTENDED: t = "EXTENDED"; break; // shouldn't happen
125 }
126 return NASTR(t);
127}
128
129// Check if type of a property is numeric (returns 0 or 1).
130// Forms:
131// props.Node.isNumeric(string relative_path);
132// props.Node.isNumeric();
133static naRef f_isNumeric(naContext c, naRef me, int argc, naRef* args)
134{
135 using namespace simgear;
136 NODEARG();
137 MOVETARGET(naVec_size(argv) > 0, false);
138 switch(node->getType()) {
139 case props::INT:
140 case props::LONG:
141 case props::FLOAT:
142 case props::DOUBLE: return naNum(true);
143 default:
144 break;
145 }
146 return naNum(false);
147}
148
149// Check if type of a property is integer (returns 0 or 1).
150// Forms:
151// props.Node.isInt(string relative_path);
152// props.Node.isInt();
153static naRef f_isInt(naContext c, naRef me, int argc, naRef* args)
154{
155 using namespace simgear;
156 NODEARG();
157 MOVETARGET(naVec_size(argv) > 0, false);
158 if ((node->getType() == props::INT) || (node->getType() == props::LONG)) {
159 return naNum(true);
160 }
161 return naNum(false);
162}
163
164
165// Get an attribute of a property by name (returns true/false).
166// Forms:
167// props.Node.getType(string relative_path,
168// string attribute_name);
169// props.Node.getType(string attribute_name);
170static naRef f_getAttribute(naContext c, naRef me, int argc, naRef* args)
171{
172 NODEARG();
173 if(naVec_size(argv) == 0) return naNum(unsigned(node->getAttributes()));
174 MOVETARGET(naVec_size(argv) > 1, false);
175 naRef val = naVec_get(argv, 0);
176 const char *a = naStr_data(val);
177 SGPropertyNode::Attribute attr;
178 if(!a) a = "";
179 if(!strcmp(a, "last")) return naNum(SGPropertyNode::LAST_USED_ATTRIBUTE);
180 else if(!strcmp(a, "children")) return naNum(node->nChildren());
181 else if(!strcmp(a, "listeners")) return naNum(node->nListeners());
182 // Number of references without instance used in this function
183 else if(!strcmp(a, "references")) return naNum(node.getNumRefs() - 1);
184 else if(!strcmp(a, "tied")) return naNum(node->isTied());
185 else if(!strcmp(a, "alias")) return naNum(node->isAlias());
186 else if(!strcmp(a, "readable")) attr = SGPropertyNode::READ;
187 else if(!strcmp(a, "writable")) attr = SGPropertyNode::WRITE;
188 else if(!strcmp(a, "archive")) attr = SGPropertyNode::ARCHIVE;
189 else if(!strcmp(a, "trace-read")) attr = SGPropertyNode::TRACE_READ;
190 else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
191 else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
192 else if(!strcmp(a, "preserve")) attr = SGPropertyNode::PRESERVE;
193 else if(!strcmp(a, "protected")) attr = SGPropertyNode::PROTECTED;
194 else if(!strcmp(a, "listener-safe")) attr = SGPropertyNode::LISTENER_SAFE;
195 else if(!strcmp(a, "value-changed-up")) attr = SGPropertyNode::VALUE_CHANGED_UP;
196 else if(!strcmp(a, "value-changed-down")) attr = SGPropertyNode::VALUE_CHANGED_DOWN;
197 else if(!strcmp(a, "translate")) attr = SGPropertyNode::TRANSLATE;
198
199 else {
200 naRuntimeError(c, "props.getAttribute() with invalid attribute: %s", a);
201 return naNil();
202 }
203 return naNum(node->getAttribute(attr));
204}
205
206
207// Set an attribute by name and boolean value or raw (bitmasked) number.
208// Forms:
209// props.Node.setAttribute(string relative_path,
210// string attribute_name,
211// bool value);
212// props.Node.setAttribute(string attribute_name,
213// bool value);
214// props.Node.setArtribute(int attributes);
215static naRef f_setAttribute(naContext c, naRef me, int argc, naRef* args)
216{
217 NODEARG();
218 if (node->getAttribute(SGPropertyNode::PROTECTED)) {
219 naRuntimeError(c, "props.setAttribute() called on protected property %s",
220 node->getPath().c_str());
221 return naNil();
222 }
223
224 MOVETARGET(naVec_size(argv) > 2, false);
225 naRef val = naVec_get(argv, 0);
226 if(naVec_size(argv) == 1 && naIsNum(val)) {
227 naRef ret = naNum(node->getAttributes());
228 // prevent Nasal modifying PROTECTED
229 int attrs = static_cast<int>(val.num) & (~SGPropertyNode::PROTECTED);
230 node->setAttributes(attrs);
231 return ret;
232 }
233 SGPropertyNode::Attribute attr;
234 const char *a = naStr_data(val);
235 if(!a) a = "";
236 if(!strcmp(a, "readable")) attr = SGPropertyNode::READ;
237 else if(!strcmp(a, "writable")) attr = SGPropertyNode::WRITE;
238 else if(!strcmp(a, "archive")) attr = SGPropertyNode::ARCHIVE;
239 else if(!strcmp(a, "trace-read")) attr = SGPropertyNode::TRACE_READ;
240 else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
241 else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
242 else if(!strcmp(a, "preserve")) attr = SGPropertyNode::PRESERVE;
243 // explicitly don't allow "protected" to be modified here
244 else {
245 naRuntimeError(c, "props.setAttribute() with invalid attribute");
246 return naNil();
247 }
248 naRef ret = naNum(node->getAttribute(attr));
249 node->setAttribute(attr, naTrue(naVec_get(argv, 1)) ? true : false);
250 return ret;
251}
252
253
254// Get the simple name of this node.
255// Forms:
256// props.Node.getName();
257static naRef f_getName(naContext c, naRef me, int argc, naRef* args)
258{
259 NODENOARG();
260 return NASTR(node->getNameString().c_str());
261}
262
263
264// Get the index of this node.
265// Forms:
266// props.Node.getIndex();
267static naRef f_getIndex(naContext c, naRef me, int argc, naRef* args)
268{
269 NODENOARG();
270 return naNum(node->getIndex());
271}
272
273// Check if other_node refers to the same as this node.
274// Forms:
275// props.Node.equals(other_node);
276static naRef f_equals(naContext c, naRef me, int argc, naRef* args)
277{
278 NODEARG();
279
280 naRef rhs = naVec_get(argv, 0);
281 if( !naIsGhost(rhs) || naGhost_type(rhs) != &PropNodeGhostType )
282 return naNum(false);
283
284 SGPropertyNode* node_rhs = static_cast<SGPropertyNode*>(naGhost_ptr(rhs));
285 return naNum(node.ptr() == node_rhs);
286}
287
288template<typename T>
289naRef makeVectorFromVec(naContext c, const T& vec)
290{
291 const int num_components
292 = sizeof(vec.data()) / sizeof(typename T::value_type);
293 naRef vector = naNewVector(c);
294 naVec_setsize(c, vector, num_components);
295 for (int i = 0; i < num_components; ++i)
296 naVec_set(vector, i, naNum(vec[i]));
297 return vector;
298}
299
300
301// Get the value of a node, with or without a relative path.
302// Forms:
303// props.Node.getValue(string relative_path);
304// props.Node.getValue();
305static naRef f_getValue(naContext c, naRef me, int argc, naRef* args)
306{
307 using namespace simgear;
308 NODEARG();
309 MOVETARGET(naVec_size(argv) > 0, false);
310 return FGNasalSys::getPropertyValue(c, node);
311}
312
313template<typename T>
314T makeVecFromVector(naRef vector)
315{
316 T vec;
317 const int num_components
318 = sizeof(vec.data()) / sizeof(typename T::value_type);
319 int size = naVec_size(vector);
320
321 for (int i = 0; i < num_components && i < size; ++i) {
322 naRef element = naVec_get(vector, i);
323 naRef n = naNumValue(element);
324 if (!naIsNil(n))
325 vec[i] = n.num;
326 }
327 return vec;
328}
329
330static std::string s_val_description(naRef val)
331{
332 std::ostringstream message;
333 if (naIsNil(val)) message << "nil";
334 else if (naIsNum(val)) message << "num:" << naNumValue(val).num;
335 else if (naIsString(val)) message << "string:" << naStr_data(val);
336 else if (naIsScalar(val)) message << "scalar";
337 else if (naIsVector(val)) message << "vector";
338 else if (naIsHash(val)) message << "hash";
339 else if (naIsFunc(val)) message << "func";
340 else if (naIsCode(val)) message << "code";
341 else if (naIsCCode(val)) message << "ccode";
342 else if (naIsGhost(val)) message << "ghost";
343 else message << "?";
344 return message.str();
345}
346
347// Helper function to set the value of a node; returns true if it succeeded or
348// false if it failed. <val> can be a string, number, or a
349// vector or numbers (for SGVec3D/4D types).
350static naRef f_setValueHelper(naContext c, SGPropertyNode_ptr node, naRef val) {
351 bool result = false;
352 if(naIsString(val)) {
353 result = node->setStringValue(naStr_data(val));
354 } else if(naIsVector(val)) {
355 if(naVec_size(val) == 3)
356 result = node->setValue(makeVecFromVector<SGVec3d>(val));
357 else if(naVec_size(val) == 4)
358 result = node->setValue(makeVecFromVector<SGVec4d>(val));
359 else
360 naRuntimeError(c, "props.setValue() vector value has wrong size");
361 } else if(naIsNum(val)) {
362 double d = naNumValue(val).num;
363 if (SGMisc<double>::isNaN(d)) {
364 naRuntimeError(c, "props.setValue() passed a NaN");
365 }
366
367 result = node->setDoubleValue(d);
368 } else if (naIsNil(val)) {
369 node->clearValue();
370 result = true;
371 } else {
372 naRuntimeError(c, "props.setValue() called with unsupported value %s", s_val_description(val).c_str());
373 }
374 return naNum(result);
375}
376
377
378// Set the value of a node; returns true if it succeeded or
379// false if it failed. <val> can be a string, number, or a
380// vector or numbers (for SGVec3D/4D types).
381// Forms:
382// props.Node.setValue(string relative_path,
383// val);
384// props.Node.setValue(val);
385static naRef f_setValue(naContext c, naRef me, int argc, naRef* args)
386{
387 NODEARG();
388 MOVETARGET(naVec_size(argv) > 1, true);
389 naRef val = naVec_get(argv, 0);
390 return f_setValueHelper(c, node, val);
391}
392
393static naRef f_setIntValue(naContext c, naRef me, int argc, naRef* args)
394{
395 NODEARG();
396 MOVETARGET(naVec_size(argv) > 1, true);
397 // Original code:
398 // int iv = (int)naNumValue(naVec_get(argv, 0)).num;
399
400 // Junk to pacify the gcc-2.95.3 optimizer:
401 naRef tmp0 = naVec_get(argv, 0);
402 naRef tmp1 = naNumValue(tmp0);
403 if(naIsNil(tmp1))
404 naRuntimeError(c, "props.setIntValue() with non-number");
405 double tmp2 = tmp1.num;
406 int iv = (int)tmp2;
407
408 return naNum(node->setIntValue(iv));
409}
410
411static naRef f_setBoolValue(naContext c, naRef me, int argc, naRef* args)
412{
413 NODEARG();
414 MOVETARGET(naVec_size(argv) > 1, true);
415 naRef val = naVec_get(argv, 0);
416 return naNum(node->setBoolValue(naTrue(val) ? true : false));
417}
418
419static naRef f_toggleBoolValue(naContext c, naRef me, int argc, naRef* args)
420{
421 using namespace simgear;
422
423 NODEARG();
424 MOVETARGET(naVec_size(argv) > 0, false);
425 if (node->getType() != props::BOOL) {
426 naRuntimeError(c, "props.toggleBoolValue() on non-bool prop");
427 }
428
429 const auto val = node->getBoolValue();
430 return naNum(node->setBoolValue(val ? false : true));
431}
432
433static naRef f_setDoubleValue(naContext c, naRef me, int argc, naRef* args)
434{
435 NODEARG();
436 MOVETARGET(naVec_size(argv) > 1, true);
437 naRef r = naNumValue(naVec_get(argv, 0));
438 if (naIsNil(r))
439 naRuntimeError(c, "props.setDoubleValue() with non-number");
440
441 if (SGMisc<double>::isNaN(r.num)) {
442 naRuntimeError(c, "props.setDoubleValue() passed a NaN");
443 }
444
445 return naNum(node->setDoubleValue(r.num));
446}
447
448static naRef f_adjustValue(naContext c, naRef me, int argc, naRef* args)
449{
450 using namespace simgear;
451
452 NODEARG();
453 MOVETARGET(naVec_size(argv) > 1, false);
454 naRef r = naNumValue(naVec_get(argv, 0));
455 if (naIsNil(r))
456 naRuntimeError(c, "props.adjustValue() with non-number");
457
458 if (SGMisc<double>::isNaN(r.num)) {
459 naRuntimeError(c, "props.adjustValue() passed a NaN");
460 }
461
462 switch(node->getType()) {
463 case props::BOOL:
464 case props::INT:
465 case props::LONG:
466 case props::FLOAT:
467 case props::DOUBLE:
468 // fall through
469 break;
470
471 default:
472 naRuntimeError(c, "props.adjustValue() called on non-numeric type");
473 return naNil();
474 }
475
476 const auto dv = node->getDoubleValue();
477 return naNum(node->setDoubleValue(dv + r.num));
478}
479
480// Forward declaration
481static naRef f_setChildrenHelper(naContext c, SGPropertyNode_ptr node, char* name, naRef val);
482
483static naRef f_setValuesHelper(naContext c, SGPropertyNode_ptr node, naRef hash)
484{
485 if (!naIsHash(hash)) {
486 naRuntimeError(c, "props.setValues() with non-hash");
487 }
488
489 naRef keyvec = naNewVector(c);
490 naHash_keys(keyvec, hash);
491 naRef ret;
492
493 for (int i = 0; i < naVec_size(keyvec); i++) {
494 naRef key = naVec_get(keyvec, i);
495 if (! naIsScalar(key)) {
496 naRuntimeError(c, "props.setValues() with non-scalar key value");
497 }
498 char* keystr = naStr_data(naStringValue(c, key));
499 ret = f_setChildrenHelper(c, node, keystr, naHash_cget(hash, keystr));
500 }
501
502 return ret;
503}
504
505static naRef f_setValues(naContext c, naRef me, int argc, naRef* args)
506{
507 NODEARG();
508 MOVETARGET(naVec_size(argv) > 1, true);
509 naRef val = naVec_get(argv, 0);
510 return f_setValuesHelper(c, node, val);
511}
512
513static naRef f_setChildrenHelper(naContext c, SGPropertyNode_ptr node, char* name, naRef val)
514{
515 naRef ret;
516 try {
517 SGPropertyNode_ptr subnode = node->getNode(name, true);
518
519 if (naIsScalar(val)) {
520 ret = f_setValueHelper(c, subnode, val);
521 } else if (naIsHash(val)) {
522 ret = f_setValuesHelper(c, subnode, val);
523 } else if (naIsVector(val)) {
524 char nameBuf[1024];
525 for (int i = 0; i < naVec_size(val); i++) {
526 const auto len = ::snprintf(nameBuf, sizeof(nameBuf), "%s[%i]", name, i);
527 if ((len < 0) || (len >= (int) sizeof(nameBuf))) {
528 naRuntimeError(c, "Failed to create buffer for property name in setChildren");
529 }
530 ret = f_setChildrenHelper(c, node, nameBuf, naVec_get(val, i));
531 }
532 } else if (naIsNil(val)) {
533 // Nil value OK - no-op
534 } else {
535 // We have an error, but throwing a runtime error will prevent certain things from
536 // working (such as the pilot list)
537 // The nasal version would fail silently with invalid data - a runtime error will dump the stack and
538 // stop execution.
539 // Overall to be safer the new method should be functionally equivalent to keep compatibility.
540 //
541 //REMOVED: naRuntimeError(c, "props.setChildren() with unknown type");
542 }
543 } catch(const string& err) {
544 naRuntimeError(c, (char *)err.c_str());
545 return naNil();
546 }
547
548 return ret;
549}
550
551static naRef f_setChildren(naContext c, naRef me, int argc, naRef* args)
552{
553 NODEARG();
554 if(! naIsString(naVec_get(argv, 0))) {
555 naRuntimeError(c, "props.setChildren() with non-string first argument");
556 }
557
558 char* name = naStr_data(naVec_get(argv, 0));
559 naRef val = naVec_get(argv, 1);
560 return f_setChildrenHelper(c, node, name, val);
561}
562
563// Get the parent of this node as a ghost.
564// Forms:
565// props.Node.getParent();
566static naRef f_getParent(naContext c, naRef me, int argc, naRef* args)
567{
568 NODENOARG();
569 SGPropertyNode* n = node->getParent();
570 if(!n) return naNil();
571 return propNodeGhostCreate(c, n);
572}
573
574
575// Get a child by name and optional index=0, creating if specified (by default it
576// does not create it). If the node does not exist and create is false, then it
577// returns nil, else it returns a (possibly new) property ghost.
578// Forms:
579// props.Node.getChild(string relative_path,
580// int index=0,
581// bool create=false);
582static naRef f_getChild(naContext c, naRef me, int argc, naRef* args)
583{
584 NODEARG();
585 naRef child = naVec_get(argv, 0);
586 if(!naIsString(child)) return naNil();
587 naRef idx = naNumValue(naVec_get(argv, 1));
588 bool create = naTrue(naVec_get(argv, 2)) != 0;
589 SGPropertyNode* n;
590 try {
591 if(naIsNil(idx)) {
592 n = node->getChild(naStr_data(child), create);
593 } else {
594 n = node->getChild(naStr_data(child), (int)idx.num, create);
595 }
596 } catch (const string& err) {
597 naRuntimeError(c, (char *)err.c_str());
598 return naNil();
599 }
600 if(!n) return naNil();
601 return propNodeGhostCreate(c, n);
602}
603
604
605// Get all children with a specified name as a vector of ghosts.
606// Forms:
607// props.Node.getChildren(string relative_path);
608// props.Node.getChildren(); #get all children
609static naRef f_getChildren(naContext c, naRef me, int argc, naRef* args)
610{
611 NODEARG();
612 naRef result = naNewVector(c);
613 if(naIsNil(argv) || naVec_size(argv) == 0) {
614 // Get all children
615 for(int i=0; i<node->nChildren(); i++)
616 naVec_append(result, propNodeGhostCreate(c, node->getChild(i)));
617 } else {
618 // Get all children of a specified name
619 naRef name = naVec_get(argv, 0);
620 if(!naIsString(name)) return naNil();
621 try {
622 vector<SGPropertyNode_ptr> children
623 = node->getChildren(naStr_data(name));
624 for(unsigned int i=0; i<children.size(); i++)
625 naVec_append(result, propNodeGhostCreate(c, children[i]));
626 } catch (const string& err) {
627 naRuntimeError(c, (char *)err.c_str());
628 return naNil();
629 }
630 }
631 return result;
632}
633
634
635// Append a named child at the first unused index...
636// Forms:
637// props.Node.addChild(string name,
638// int min_index=0,
639// bool append=true);
640static naRef f_addChild(naContext c, naRef me, int argc, naRef* args)
641{
642 NODEARG();
643 naRef child = naVec_get(argv, 0);
644 if(!naIsString(child)) return naNil();
645 naRef ref_min_index = naNumValue(naVec_get(argv, 1));
646 naRef ref_append = naVec_get(argv, 2);
647 SGPropertyNode* n;
648 try
649 {
650 int min_index = 0;
651 if(!naIsNil(ref_min_index))
652 min_index = ref_min_index.num;
653
654 bool append = true;
655 if(!naIsNil(ref_append))
656 append = naTrue(ref_append) != 0;
657
658 n = node->addChild(naStr_data(child), min_index, append);
659 }
660 catch (const string& err)
661 {
662 naRuntimeError(c, (char *)err.c_str());
663 return naNil();
664 }
665
666 return propNodeGhostCreate(c, n);
667}
668
669static naRef f_addChildren(naContext c, naRef me, int argc, naRef* args)
670{
671 NODEARG();
672 naRef child = naVec_get(argv, 0);
673 if(!naIsString(child)) return naNil();
674 naRef ref_count = naNumValue(naVec_get(argv, 1));
675 naRef ref_min_index = naNumValue(naVec_get(argv, 2));
676 naRef ref_append = naVec_get(argv, 3);
677 try
678 {
679 size_t count = 0;
680 if( !naIsNum(ref_count) )
681 throw string("props.addChildren() missing number of children");
682 count = ref_count.num;
683
684 int min_index = 0;
685 if(!naIsNil(ref_min_index))
686 min_index = ref_min_index.num;
687
688 bool append = true;
689 if(!naIsNil(ref_append))
690 append = naTrue(ref_append) != 0;
691
692 const simgear::PropertyList& nodes =
693 node->addChildren(naStr_data(child), count, min_index, append);
694
695 naRef result = naNewVector(c);
696 for( size_t i = 0; i < nodes.size(); ++i )
697 naVec_append(result, propNodeGhostCreate(c, nodes[i]));
698 return result;
699 }
700 catch (const string& err)
701 {
702 naRuntimeError(c, (char *)err.c_str());
703 }
704
705 return naNil();
706}
707
708
709// Remove a child by name and index. Returns it as a ghost.
710// Forms:
711// props.Node.removeChild(string relative_path,
712// int index);
713static naRef f_removeChild(naContext c, naRef me, int argc, naRef* args)
714{
715 NODEARG();
716 naRef child = naVec_get(argv, 0);
717 naRef index = naVec_get(argv, 1);
718 if(!naIsString(child) || !naIsNum(index)) return naNil();
719 SGPropertyNode_ptr n;
720 try {
721 n = node->getChild(naStr_data(child), (int)index.num);
722 if (n && n->getAttribute(SGPropertyNode::PROTECTED)) {
723 naRuntimeError(c, "props.Node.removeChild() called on protected child %s of %s",
724 naStr_data(child), node->getPath().c_str());
725 return naNil();
726 }
727 n = node->removeChild(naStr_data(child), (int)index.num);
728 } catch (const string& err) {
729 naRuntimeError(c, (char *)err.c_str());
730 }
731 return propNodeGhostCreate(c, n);
732}
733
734
735// Remove all children with specified name. Returns a vector of all the nodes
736// removed as ghosts.
737// Forms:
738// props.Node.removeChildren(string relative_path);
739// props.Node.removeChildren(); #remove all children
740static naRef f_removeChildren(naContext c, naRef me, int argc, naRef* args)
741{
742 NODEARG();
743 naRef result = naNewVector(c);
744 if(naIsNil(argv) || naVec_size(argv) == 0) {
745 // Remove all children
746 for(int i = node->nChildren() - 1; i >=0; i--) {
747 SGPropertyNode_ptr n = node->getChild(i);
748 if (n->getAttribute(SGPropertyNode::PROTECTED)) {
749 SG_LOG(SG_NASAL, SG_ALERT, "props.Node.removeChildren: node " <<
750 n->getPath() << " is protected");
751 continue;
752 }
753
754 node->removeChild(i);
755 naVec_append(result, propNodeGhostCreate(c, n));
756 }
757 } else {
758 // Remove all children of a specified name
759 naRef name = naVec_get(argv, 0);
760 if(!naIsString(name)) return naNil();
761 try {
762 auto children = node->getChildren(naStr_data(name));
763 for (auto cn : children) {
764 if (cn->getAttribute(SGPropertyNode::PROTECTED)) {
765 SG_LOG(SG_NASAL, SG_ALERT, "props.Node.removeChildren: node " <<
766 cn->getPath() << " is protected");
767 continue;
768 }
769 node->removeChild(cn);
770 naVec_append(result, propNodeGhostCreate(c, cn));
771 }
772 } catch (const string& err) {
773 naRuntimeError(c, (char *)err.c_str());
774 return naNil();
775 }
776 }
777 return result;
778}
779
780// Remove all children of a property node.
781// Forms:
782// props.Node.removeAllChildren();
783static naRef f_removeAllChildren(naContext c, naRef me, int argc, naRef* args)
784{
785 NODENOARG();
786 node->removeAllChildren();
787 return propNodeGhostCreate(c, node);
788}
789
790// Alias this property to another one; returns 1 on success or 0 on failure
791// (only applicable to tied properties).
792// Forms:
793// props.Node.alias(string global_path);
794// props.Node.alias(prop_ghost node);
795// props.Node.alias(props.Node node); #added by props.nas
796static naRef f_alias(naContext c, naRef me, int argc, naRef* args)
797{
798 NODEARG();
799 if (node->getAttribute(SGPropertyNode::PROTECTED)) {
800 naRuntimeError(c, "props.Node.alias() called on protected property %s",
801 node->getPath().c_str());
802 return naNil();
803 }
804 SGPropertyNode* al;
805 naRef prop = naVec_get(argv, 0);
806 try {
807 if(naIsString(prop)) al = globals->get_props()->getNode(naStr_data(prop), true);
808 else if(naIsGhost(prop)) al = static_cast<SGPropertyNode*>(naGhost_ptr(prop));
809 else
810 throw sg_exception("props.alias() with bad argument");
811 } catch (sg_exception& err) {
812 naRuntimeError(c, err.what());
813 return naNil();
814 }
815
816 bool withListeners = false;
817 if (naVec_size(argv) > 1) {
818 withListeners = static_cast<int>(naVec_get(argv, 1).num) != 0;
819 }
820 return naNum(node->alias(al, withListeners));
821}
822
823
824// Un-alias this property. Returns 1 on success or 0 on failure (only
825// applicable to tied properties).
826// Forms:
827// props.Node.unalias();
828static naRef f_unalias(naContext c, naRef me, int argc, naRef* args)
829{
830 NODENOARG();
831 return naNum(node->unalias());
832}
833
834
835static naRef f_location(naContext c, naRef me, int argc, naRef* args)
836{
837 NODENOARG();
838 const auto ls = node->getLocation().str();
839 return NASTR(ls.c_str());
840}
841
842
843// Get the alias of this node as a ghost.
844// Forms:
845// props.Node.getAliasTarget();
846static naRef f_getAliasTarget(naContext c, naRef me, int argc, naRef* args)
847{
848 NODENOARG();
849 return propNodeGhostCreate(c, node->getAliasTarget());
850}
851
852
853// Get a relative node. Returns nil if it does not exist and create is false,
854// or a ghost object otherwise (wrapped into a props.Node object by props.nas).
855// Forms:
856// props.Node.getNode(string relative_path,
857// bool create=false);
858static naRef f_getNode(naContext c, naRef me, int argc, naRef* args)
859{
860 NODEARG();
861 naRef path = naVec_get(argv, 0);
862 bool create = naTrue(naVec_get(argv, 1)) != 0;
863 if(!naIsString(path)) return naNil();
864 SGPropertyNode* n;
865 try {
866 n = node->getNode(naStr_data(path), create);
867 } catch (const string& err) {
868 naRuntimeError(c, (char *)err.c_str());
869 return naNil();
870 }
871 return propNodeGhostCreate(c, n);
872}
873
874
875// Create a new property node.
876// Forms:
877// props.Node.new();
878static naRef f_new(naContext c, naRef me, int argc, naRef* args)
879{
880 return propNodeGhostCreate(c, new SGPropertyNode());
881}
882
883
884// Get the global root node (cached by props.nas so that it does
885// not require a function call).
886// Forms:
887// props._globals()
888// props.globals
889static naRef f_globals(naContext c, naRef me, int argc, naRef* args)
890{
891 return propNodeGhostCreate(c, globals->get_props());
892}
893
894static struct {
895 naCFunction func;
896 const char* name;
897} propfuncs[] = {
898 { f_getType, "_getType" },
899 { f_getAttribute, "_getAttribute" },
900 { f_setAttribute, "_setAttribute" },
901 { f_getName, "_getName" },
902 { f_getIndex, "_getIndex" },
903 { f_equals, "_equals" },
904 { f_getValue, "_getValue" },
905 { f_setValue, "_setValue" },
906 { f_setValues, "_setValues" },
907 { f_setIntValue, "_setIntValue" },
908 { f_setBoolValue, "_setBoolValue" },
909 { f_toggleBoolValue, "_toggleBoolValue" },
910 { f_setDoubleValue, "_setDoubleValue" },
911 { f_getParent, "_getParent" },
912 { f_getChild, "_getChild" },
913 { f_getChildren, "_getChildren" },
914 { f_addChild, "_addChild" },
915 { f_addChildren, "_addChildren" },
916 { f_removeChild, "_removeChild" },
917 { f_removeChildren, "_removeChildren" },
918 { f_removeAllChildren, "_removeAllChildren" },
919 { f_setChildren, "_setChildren" },
920 { f_alias, "_alias" },
921 { f_unalias, "_unalias" },
922 { f_getAliasTarget, "_getAliasTarget" },
923 { f_getNode, "_getNode" },
924 { f_new, "_new" },
925 { f_globals, "_globals" },
926 { f_isNumeric, "_isNumeric" },
927 { f_isInt, "_isInt" },
928 { f_adjustValue, "_adjustValue" },
929 { f_location, "_location" },
930 { 0, 0 }
932
933naRef FGNasalSys::genPropsModule()
934{
935 naRef namespc = naNewHash(d->_context);
936 for(int i=0; propfuncs[i].name; i++)
937 hashset(namespc, propfuncs[i].name,
938 naNewFunc(d->_context, naNewCCode(d->_context, propfuncs[i].func)));
939 return namespc;
940}
941
942naRef FGNasalSys::getPropertyValue(naContext c, SGPropertyNode* node)
943{
944 using namespace simgear;
945 if (!node)
946 return naNil();
947
948 switch(node->getType()) {
949 case props::BOOL: case props::INT:
950 case props::LONG: case props::FLOAT:
951 case props::DOUBLE:
952 {
953 double dv = node->getDoubleValue();
954 if (SGMisc<double>::isNaN(dv)) {
955 SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << node->getPath() << " is NaN");
956 return naNil();
957 }
958
959 return naNum(dv);
960 }
961
962 case props::STRING:
963 case props::UNSPECIFIED:
964 return NASTR(node->getStringValue().c_str());
965 case props::VEC3D:
966 return makeVectorFromVec(c, node->getValue<SGVec3d>());
967 case props::VEC4D:
968 return makeVectorFromVec(c, node->getValue<SGVec4d>());
969 default:
970 return naNil();
971 }
972}
naRef propNodeGhostCreate(naContext c, SGPropertyNode *n)
#define i(x)
#define iv(x)
static naRef getPropertyValue(naContext c, SGPropertyNode *node)
Convert the value of an SGPropertyNode to its Nasal representation.
void hashset(naRef hash, const char *key, naRef val)
Set member of specified hash to given value.
Definition NasalSys.cxx:302
naRef propNodeGhost(SGPropertyNode *handle)
const char * name
FGGlobals * globals
Definition globals.cxx:142
#define NASTR(s)
Definition nasal-bin.cxx:61
static naRef f_getNode(naContext c, naRef me, int argc, naRef *args)
static naRef f_getName(naContext c, naRef me, int argc, naRef *args)
static naRef f_unalias(naContext c, naRef me, int argc, naRef *args)
naRef makeVectorFromVec(naContext c, const T &vec)
#define NASTR(s)
static naRef f_setChildrenHelper(naContext c, SGPropertyNode_ptr node, char *name, naRef val)
static naRef f_removeAllChildren(naContext c, naRef me, int argc, naRef *args)
static naRef f_addChildren(naContext c, naRef me, int argc, naRef *args)
static void propNodeGhostDestroy(void *ghost)
static std::string s_val_description(naRef val)
static naRef f_getParent(naContext c, naRef me, int argc, naRef *args)
static naRef f_getIndex(naContext c, naRef me, int argc, naRef *args)
static naRef f_getAttribute(naContext c, naRef me, int argc, naRef *args)
static naRef f_toggleBoolValue(naContext c, naRef me, int argc, naRef *args)
static naRef f_location(naContext c, naRef me, int argc, naRef *args)
static naRef f_removeChild(naContext c, naRef me, int argc, naRef *args)
static naRef f_removeChildren(naContext c, naRef me, int argc, naRef *args)
#define NODEARG()
static naRef f_equals(naContext c, naRef me, int argc, naRef *args)
static naRef f_globals(naContext c, naRef me, int argc, naRef *args)
#define NODENOARG()
static naRef f_setValueHelper(naContext c, SGPropertyNode_ptr node, naRef val)
static naRef f_getAliasTarget(naContext c, naRef me, int argc, naRef *args)
T makeVecFromVector(naRef vector)
static naRef f_setDoubleValue(naContext c, naRef me, int argc, naRef *args)
static naRef f_getChild(naContext c, naRef me, int argc, naRef *args)
SGPropertyNode * ghostToPropNode(naRef ref)
static naRef f_isInt(naContext c, naRef me, int argc, naRef *args)
static naRef f_getChildren(naContext c, naRef me, int argc, naRef *args)
static naRef f_new(naContext c, naRef me, int argc, naRef *args)
naRef propNodeGhostCreate(naContext c, SGPropertyNode *ghost)
static naRef f_alias(naContext c, naRef me, int argc, naRef *args)
static struct @240013025306215324344374206305351343354363373265 propfuncs[]
static naRef f_setBoolValue(naContext c, naRef me, int argc, naRef *args)
static naRef f_setValuesHelper(naContext c, SGPropertyNode_ptr node, naRef hash)
naGhostType PropNodeGhostType
static naRef f_addChild(naContext c, naRef me, int argc, naRef *args)
static naRef f_getValue(naContext c, naRef me, int argc, naRef *args)
static naRef f_setAttribute(naContext c, naRef me, int argc, naRef *args)
static naRef f_setValue(naContext c, naRef me, int argc, naRef *args)
static naRef f_setChildren(naContext c, naRef me, int argc, naRef *args)
naCFunction func
static naRef f_getType(naContext c, naRef me, int argc, naRef *args)
#define MOVETARGET(cond1, create)
static naRef f_isNumeric(naContext c, naRef me, int argc, naRef *args)
static naRef f_adjustValue(naContext c, naRef me, int argc, naRef *args)
static naRef f_setValues(naContext c, naRef me, int argc, naRef *args)
static naRef f_setIntValue(naContext c, naRef me, int argc, naRef *args)