Guest User

Untitled

a guest
Aug 27th, 2017
65
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ##
  2. # Node class definition. The class methods simply wrap the
  3. # low level extension functions which work on a "ghost" handle to a
  4. # SGPropertyNode object stored in the _g field.
  5. #
  6. # Not all of the features of SGPropertyNode are supported. There is
  7. # no support for ties, obviously, as that wouldn't make much sense
  8. # from a Nasal context. The various get/set methods work only on the
  9. # local node, there is no equivalent of the "relative path" variants
  10. # available in C++; just use node.getNode(path).whatever() instead.
  11. #
  12. var Node = {
  13. getNode : func wrap(_getNode(me._g, arg)),
  14. getParent : func wrap(_getParent(me._g, arg)),
  15. getChild : func wrap(_getChild(me._g, arg)),
  16. getChildren : func wrap(_getChildren(me._g, arg)),
  17. setChildren : func wrap(_setChildren(me._g, arg)),
  18. addChild : func wrap(_addChild(me._g, arg)),
  19. addChildren : func wrap(_addChildren(me._g, arg)),
  20. removeChild : func wrap(_removeChild(me._g, arg)),
  21. removeChildren : func wrap(_removeChildren(me._g, arg)),
  22. removeAllChildren: func wrap(_removeAllChildren(me._g, arg)),
  23. getAliasTarget : func wrap(_getAliasTarget(me._g, arg)),
  24.  
  25. getName : func _getName(me._g, arg),
  26. getIndex : func _getIndex(me._g, arg),
  27. getType : func _getType(me._g, arg),
  28. getAttribute : func _getAttribute(me._g, arg),
  29. setAttribute : func _setAttribute(me._g, arg),
  30. getValue : func _getValue(me._g, arg),
  31. setValue : func _setValue(me._g, arg),
  32. setValues : func _setValues(me._g, arg),
  33. setIntValue : func _setIntValue(me._g, arg),
  34. setBoolValue : func _setBoolValue(me._g, arg),
  35. setDoubleValue : func _setDoubleValue(me._g, arg),
  36. unalias : func _unalias(me._g, arg),
  37. alias : func(n) _alias(me._g, [isa(n, Node) ? n._g : n]),
  38. equals : func(n) _equals(me._g, [isa(n, Node) ? n._g : n]),
  39. clearValue : func _alias(me._g, [_globals()]) and me.unalias(),
  40.  
  41. getPath : func {
  42. var (name, index, parent) = (me.getName(), me.getIndex(), me.getParent());
  43. if(index != 0) { name ~= "[" ~ index ~ "]"; }
  44. if(parent != nil) { name = parent.getPath() ~ "/" ~ name; }
  45. return name;
  46. },
  47.  
  48. getBoolValue : func {
  49. var val = me.getValue();
  50. var mytype = me.getType();
  51. if((mytype == "STRING" or mytype == "UNSPECIFIED") and val == "false") return 0;
  52. return !!val;
  53. },
  54.  
  55. remove : func {
  56. if((var p = me.getParent()) == nil) return nil;
  57. p.removeChild(me.getName(), me.getIndex());
  58. },
  59. };
  60.  
  61. ##
  62. # Static constructor for a Node object. Accepts a Nasal hash
  63. # expression to initialize the object a-la setValues().
  64. #
  65. Node.new = func(values = nil) {
  66. var result = wrapNode(_new());
  67. if(typeof(values) == "hash")
  68. result.setValues(values);
  69. return result;
  70. }
  71.  
  72. ##
  73. # Counter piece of setValues(). Returns a hash with all values
  74. # in the subtree. Nodes with same name are returned as vector,
  75. # where the original node indices are lost. The function should
  76. # only be used if all or almost all values are needed, and never
  77. # in performance-critical code paths. If it's called on a node
  78. # without children, then the result is equivalent to getValue().
  79. #
  80. Node.getValues = func {
  81. var children = me.getChildren();
  82. if(!size(children)) return me.getValue();
  83. var val = {};
  84. var numchld = {};
  85. foreach(var c; children) {
  86. var name = c.getName();
  87. if(contains(numchld, name)) { var nc = numchld[name]; }
  88. else {
  89. var nc = size(me.getChildren(name));
  90. numchld[name] = nc;
  91. if(nc > 1 and !contains(val, name)) val[name] = [];
  92. }
  93. if(nc > 1) append(val[name], c.getValues());
  94. else val[name] = c.getValues();
  95. }
  96. return val;
  97. }
  98.  
  99. ##
  100. # Initializes property if it's still undefined. First argument
  101. # is a property name/path. It can also be nil or an empty string,
  102. # in which case the node itself gets initialized, rather than one
  103. # of its children. Second argument is the default value. The third,
  104. # optional argument is a property type (one of "STRING", "DOUBLE",
  105. # "INT", or "BOOL"). If it is omitted, then "DOUBLE" is used for
  106. # numbers, and STRING for everything else. Returns the property
  107. # as props.Node. The fourth optional argument enforces a type if
  108. # non-zero.
  109. #
  110. Node.initNode = func(path = nil, value = 0, type = nil, force = 0) {
  111. var prop = me.getNode(path or "", 1);
  112. if(prop.getType() != "NONE") value = prop.getValue();
  113. if(force) prop.clearValue();
  114. if(type == nil) prop.setValue(value);
  115. elsif(type == "DOUBLE") prop.setDoubleValue(value);
  116. elsif(type == "INT") prop.setIntValue(value);
  117. elsif(type == "BOOL") prop.setBoolValue(value);
  118. elsif(type == "STRING") prop.setValue("" ~ value);
  119. else die("initNode(): unsupported type '" ~ type ~ "'");
  120. return prop;
  121. }
  122.  
  123. ##
  124. # Useful debugging utility. Recursively dumps the full state of a
  125. # Node object to the console. Try binding "props.dump(props.globals)"
  126. # to a key for a fun hack.
  127. #
  128. var dump = func {
  129. if(size(arg) == 1) { prefix = ""; node = arg[0]; }
  130. else { prefix = arg[0]; node = arg[1]; }
  131.  
  132. index = node.getIndex();
  133. type = node.getType();
  134. name = node.getName();
  135. val = node.getValue();
  136.  
  137. if(val == nil) { val = "nil"; }
  138. name = prefix ~ name;
  139. if(index > 0) { name = name ~ "[" ~ index ~ "]"; }
  140. print(name, " {", type, "} = ", val);
  141.  
  142. # Don't recurse into aliases, lest we get stuck in a loop
  143. if(type != "ALIAS") {
  144. children = node.getChildren();
  145. foreach(c; children) { dump(name ~ "/", c); }
  146. }
  147. }
  148.  
  149. ##
  150. # Recursively copy property branch from source Node to
  151. # destination Node. Doesn't copy aliases. Copies attributes
  152. # if optional third argument is set and non-zero.
  153. #
  154. var copy = func(src, dest, attr = 0) {
  155. foreach(var c; src.getChildren()) {
  156. var name = c.getName() ~ "[" ~ c.getIndex() ~ "]";
  157. copy(src.getNode(name), dest.getNode(name, 1), attr);
  158. }
  159. var type = src.getType();
  160. var val = src.getValue();
  161. if(type == "ALIAS" or type == "NONE") return;
  162. elsif(type == "BOOL") dest.setBoolValue(val);
  163. elsif(type == "INT" or type == "LONG") dest.setIntValue(val);
  164. elsif(type == "FLOAT" or type == "DOUBLE") dest.setDoubleValue(val);
  165. else dest.setValue(val);
  166. if(attr) dest.setAttribute(src.getAttribute());
  167. }
  168.  
  169. ##
  170. # Utility. Turns any ghosts it finds (either solo, or in an
  171. # array) into Node objects.
  172. #
  173. var wrap = func(node) {
  174. var argtype = typeof(node);
  175. if(argtype == "ghost") {
  176. return wrapNode(node);
  177. } elsif(argtype == "vector") {
  178. var v = node;
  179. var n = size(v);
  180. for(var i=0; i<n; i+=1) { v[i] = wrapNode(v[i]); }
  181. return v;
  182. }
  183. return node;
  184. }
  185.  
  186. ##
  187. # Utility. Returns a new object with its superclass/parent set to the
  188. # Node object and its _g (ghost) field set to the specified object.
  189. # Nasal's literal syntax can be pleasingly terse. I like that. :)
  190. #
  191. var wrapNode = func(node) { { parents : [Node], _g : node } }
  192.  
  193. ##
  194. # Global property tree. Set once at initialization. Is that OK?
  195. # Does anything ever call globals.set_props() from C++? May need to
  196. # turn this into a function if so.
  197. #
  198. var globals = wrapNode(_globals());
  199.  
  200. ##
  201. # Shortcut for props.globals.getNode().
  202. #
  203. var getNode = func return call(props.globals.getNode, arg, props.globals);
  204.  
  205. ##
  206. # Sets all indexed property children to a single value. arg[0]
  207. # specifies a property name (e.g. /controls/engines/engine), arg[1] a
  208. # path under each node of that name to set (e.g. "throttle"), arg[2]
  209. # is the value.
  210. #
  211. var setAll = func(base, child, value) {
  212. var node = props.globals.getNode(base);
  213. if(node == nil) return;
  214. var name = node.getName();
  215. node = node.getParent();
  216. if(node == nil) return;
  217. var children = node.getChildren();
  218. foreach(var c; children)
  219. if(c.getName() == name)
  220. c.getNode(child, 1).setValue(value);
  221. }
  222.  
  223. ##
  224. # Turns about anything into a list of props.Nodes, including ghosts,
  225. # path strings, vectors or hashes containing, as well as functions
  226. # returning any of the former and in arbitrary nesting. This is meant
  227. # to be used in functions whose main purpose is to handle collections
  228. # of properties.
  229. #
  230. var nodeList = func {
  231. var list = [];
  232. foreach(var a; arg) {
  233. var t = typeof(a);
  234. if(isa(a, Node))
  235. append(list, a);
  236. elsif(t == "scalar")
  237. append(list, props.globals.getNode(a, 1));
  238. elsif(t == "vector")
  239. foreach(var i; a)
  240. list ~= nodeList(i);
  241. elsif(t == "hash")
  242. foreach(var i; keys(a))
  243. list ~= nodeList(a[i]);
  244. elsif(t == "func")
  245. list ~= nodeList(a());
  246. elsif(t == "ghost" and ghosttype(a) == "prop")
  247. append(list, wrapNode(a));
  248. else
  249. die("nodeList: invalid nil property");
  250. }
  251. return list;
  252. }
  253.  
  254. ##
  255. # Compiles a <condition> property branch according to the rules
  256. # set out in $FG_ROOT/Docs/README.conditions into a Condition object.
  257. # The 'test' method of the returend object can be used to evaluate
  258. # the condition.
  259. # The function returns nil on error.
  260. #
  261. var compileCondition = func(p) {
  262. if(p == nil) return nil;
  263. if(!isa(p, Node)) p = props.globals.getNode(p);
  264. return _createCondition(p._g);
  265. }
  266.  
  267. ##
  268. # Evaluates a <condition> property branch according to the rules
  269. # set out in $FG_ROOT/Docs/README.conditions. Undefined conditions
  270. # and a nil argument are "true". The function dumps the condition
  271. # branch and returns nil on error.
  272. #
  273. var condition = func(p) {
  274. if(p == nil) return 1;
  275. if(!isa(p, Node)) p = props.globals.getNode(p);
  276. return _cond_and(p)
  277. }
  278.  
  279. var _cond_and = func(p) {
  280. foreach(var c; p.getChildren())
  281. if(!_cond(c)) return 0;
  282. return 1;
  283. }
  284.  
  285. var _cond_or = func(p) {
  286. foreach(var c; p.getChildren())
  287. if(_cond(c)) return 1;
  288. return 0;
  289. }
  290.  
  291. var _cond = func(p) {
  292. var n = p.getName();
  293. if(n == "or") return _cond_or(p);
  294. if(n == "and") return _cond_and(p);
  295. if(n == "not") return !_cond_and(p);
  296. if(n == "equals") return _cond_cmp(p, 0);
  297. if(n == "not-equals") return !_cond_cmp(p, 0);
  298. if(n == "less-than") return _cond_cmp(p, -1);
  299. if(n == "greater-than") return _cond_cmp(p, 1);
  300. if(n == "less-than-equals") return !_cond_cmp(p, 1);
  301. if(n == "greater-than-equals") return !_cond_cmp(p, -1);
  302. if(n == "property") return !!getprop(p.getValue());
  303. printlog("alert", "condition: invalid operator ", n);
  304. dump(p);
  305. return nil;
  306. }
  307.  
  308. var _cond_cmp = func(p, op) {
  309. var left = p.getChild("property", 0, 0);
  310. if(left != nil) { left = getprop(left.getValue()); }
  311. else {
  312. printlog("alert", "condition: no left value");
  313. dump(p);
  314. return nil;
  315. }
  316. var right = p.getChild("property", 1, 0);
  317. if(right != nil) { right = getprop(right.getValue()); }
  318. else {
  319. right = p.getChild("value", 0, 0);
  320. if(right != nil) { right = right.getValue(); }
  321. else {
  322. printlog("alert", "condition: no right value");
  323. dump(p);
  324. return nil;
  325. }
  326. }
  327. if(left == nil or right == nil) {
  328. printlog("alert", "condition: comparing with nil");
  329. dump(p);
  330. return nil;
  331. }
  332. if(op < 0) return left < right;
  333. if(op > 0) return left > right;
  334. return left == right;
  335. }
  336.  
  337. ##
  338. # Runs <binding> as described in $FG_ROOT/Docs/README.commands using
  339. # a given module by default, and returns 1 if fgcommand() succeeded,
  340. # or 0 otherwise. The module name won't override a <module> defined
  341. # in the binding.
  342. #
  343. var runBinding = func(node, module = nil) {
  344. if(module != nil and node.getNode("module") == nil)
  345. node.getNode("module", 1).setValue(module);
  346. var cmd = node.getNode("command", 1).getValue() or "null";
  347. condition(node.getNode("condition")) ? fgcommand(cmd, node) : 0;
  348. }
RAW Paste Data