vrml.pegjs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * VRML Grammar
  3. * ============
  4. *
  5. * VRML Grammar for pegjs, inspired by JSON grammar found here:
  6. * https://github.com/pegjs/pegjs/blob/master/examples/json.pegjs
  7. *
  8. * @author Bart McLeod [email protected]
  9. * @since 2016-05-04
  10. *
  11. * The primary goal of this grammar is to enable you to use a full VRML node tree with modern JavaScript
  12. * 3D libraries, such as ThreeJS.If this grammar is used with Pegjs, it will produce a node tree that
  13. * you can use with a converter, to produce valid ThreeJS code, or direct output in ThreeJS (or another 3D
  14. * JavaScript library of your choice.
  15. *
  16. * This grammar is currently experimental. Is has been built by trial and error, based on an old
  17. * VRML model that I used as a source for the ThreeJs VRML loader example. The ThreeJs example
  18. * can be found here, but it currently uses the old line by line parsing method, which has very
  19. * limited awareness of the node tree.
  20. *
  21. * When used with Pegjs (https://github.com/pegjs), it can be used to parse a node tree, so
  22. * that full awareness of the node tree will exist. This will allow to do more with VRML in JavaScript,
  23. * such as animations.
  24. *
  25. *
  26. */
  27. {
  28. var nodeDefinitions = {};
  29. var routes = [];
  30. }
  31. vrml
  32. = '#VRML V2.0 utf8' vrml:(nodeDefinition / node / comment / route)*
  33. {
  34. // before returning the root vrml object, enricht it with routes and nodeDefinitions
  35. vrml.nodeDefinitions = nodeDefinitions;
  36. vrml.routes = routes;
  37. return vrml;
  38. }
  39. /* ----- Node ------ */
  40. nodeDefinition
  41. = ws name:(def ws name:identifier ws { return name; }) n:node
  42. {
  43. n.name = name;
  44. n.isDefinition = true;
  45. // store node for later re-use
  46. nodeDefinitions[name] = n;
  47. //console.log('Registered as ' + name + ' in nodeDefinitions:');
  48. //console.log(n);
  49. return n;
  50. }
  51. node
  52. = ws t:identifier ws begin_node ws pp:( nodeDefinition / route / property / node / comment )* ws end_node ws
  53. {
  54. var n = {node: t};
  55. // node properties are in pp, if pp is not an Inline node, if pp is an inline node, it should be read from the url
  56. for (var i=0; i < pp.length; i++) {
  57. var p = pp[i];
  58. // is p a node?
  59. if (undefined !== p.node) {
  60. //console.log(p.node + ' node found');
  61. // if the node does not already have children, create children here
  62. if (undefined === n.children) {
  63. n.children = [];
  64. }
  65. // @todo for an Inline node, we could use the parser (named 'parser') and fs here, to fetch the inline file and parse it
  66. // on the other hand, it could be left up to the renderers what to do with the inline node.
  67. /*
  68. @see http://pegjs.org/documentation#grammar-syntax-and-semantics
  69. The code inside the predicate can also access the parser object using the parser variable and options passed to the parser using the options variable.
  70. */
  71. n.children.push(p);
  72. } else if (undefined !== p.name) {
  73. // p is a property
  74. n[p.name] = p.value;
  75. if (undefined !== p.comment) {
  76. if (undefined === n.comments) { n.comments = {}; }
  77. if (undefined === n.comments[p.name]) { n.comments[p.name] = []; }
  78. n.comments[p.name].push(p.comment);
  79. }
  80. } else if (undefined !== p.src) {
  81. // p is a route
  82. // move it to global scope
  83. routes.push(p);
  84. } else {
  85. // p is a comment
  86. if (undefined === n.nodeComments) {
  87. n.nodeComments = [];
  88. }
  89. n.nodeComments.push(p);
  90. }
  91. }
  92. return n;
  93. }
  94. property
  95. = ws name:identifier ws value:value ws comment:comment?
  96. {
  97. var p = { name:name, value:value };
  98. // you could change a color property here by returning r g b instead of x y z
  99. if (null !== comment) {
  100. p.comment = comment;
  101. }
  102. return p;
  103. }
  104. identifier "identifier"
  105. = o:[A-Za-z0-9_]+ { return o.join(''); }
  106. /* ----- Arrays (The VRML way) ----- */
  107. array "array"
  108. = begin_array
  109. it:(c:comment / r:route / v:(v:value ws value_separator? { return v; } ) )*
  110. end_array
  111. {
  112. var a = [];
  113. for (var i=0; i < it.length; i++) {
  114. var value = it[i];
  115. if (undefined !== value.src) {
  116. // value is a route, add to global routes
  117. routes.push(value);
  118. } else if (undefined !== value.comment) {
  119. // value is a comment
  120. if (undefined === a.comments) {
  121. a.comments = [];
  122. }
  123. a.comments.push(value);
  124. } else {
  125. // this is what we are looking for: a value for in our array!
  126. a.push(value);
  127. }
  128. }
  129. return a;
  130. }
  131. /* ----- Values ----- */
  132. value "value"
  133. = false
  134. / face
  135. / null
  136. / true
  137. / nodeDefinition
  138. / node
  139. / rotation
  140. / point
  141. / vector
  142. / use_statement
  143. / array
  144. / number
  145. / float
  146. / identifier
  147. / url
  148. / quoted_string
  149. false = "false" / "FALSE" { return false; }
  150. null = "null" / "NULL" { return null; }
  151. true = "true" / "TRUE" { return true; }
  152. /* ----- Numbers ----- */
  153. number "number"
  154. = minus? int frac? exp? { return parseFloat(text()); }
  155. decimal_point = "."
  156. digit1_9 = [1-9]
  157. e = [eE]
  158. exp = e (minus / plus)? DIGIT+
  159. frac = decimal_point DIGIT+
  160. int = zero / (digit1_9 DIGIT*)
  161. minus = "-"
  162. plus = "+"
  163. zero = "0"
  164. /* ----- VRML Grammar ----- */
  165. comment
  166. = ws "#" text:[^\n]* ws { return { comment: text.join('').trim() }; }
  167. route
  168. = ws "ROUTE" ws src:route_part ws "TO" ws target:route_part ws
  169. {
  170. var route = { source: src, target: target };
  171. // put it in the global routes collection
  172. routes.push(route);
  173. return route;
  174. }
  175. route_part
  176. = name:identifier "." property:identifier
  177. { return { name: name, property: property }; }
  178. begin_array = ws "[" ws
  179. begin_node = ws "{" ws
  180. end_array = ws "]" ws
  181. end_node = ws "}" ws
  182. value_separator = ws "," ws
  183. name_separator = ws
  184. ws "whitespace"
  185. = ws:[ \t\n\r]*
  186. { return ws.join('');}
  187. space
  188. = " "
  189. point
  190. = p:vector "," ws comment? { return p; }
  191. vector
  192. = ws x:number ws y:number ws z:number ws comment?
  193. { return {x:x, y:y, z:z}; }
  194. def
  195. = "DEF"
  196. { return true; }
  197. use_statement
  198. = ws use ws name:identifier
  199. {
  200. var obj = nodeDefinitions[name];
  201. if (undefined === obj) {
  202. console.log(name + ' not found in nodeDefinitions');
  203. return obj; // undefined obj
  204. }
  205. if ('function' === typeof obj.clone) {
  206. return obj.clone();
  207. }
  208. return obj;
  209. }
  210. use
  211. = "USE"
  212. { return true; }
  213. face
  214. = points:index+ "-1" ws
  215. { return points; }
  216. index
  217. = ws i:int ws value_separator ws
  218. { if (i==0) { return i; } return i.join(''); }
  219. rotation
  220. = ws x:number ws y:number ws z:number ws radians:number ws
  221. { return {x:x, y:y, z:z, radians:radians}; }
  222. url
  223. = ws begin_array ws quote uri:uri quote ws end_array ws
  224. { return uri; }
  225. uri
  226. = i:[^"]* dot:"." ext:("jpg" / "jpeg" / "gif" / "wrl")
  227. { return i + dot + ext + "BOOOO"; }
  228. quoted_string
  229. = ws quote s:[^"]* quote ws
  230. { return '"' + s.join('') + '"'; }
  231. quote
  232. = '"'
  233. /* This is a special case, because in VRML it is allowed to write a float as .66 for example, meaning 0.66 */
  234. float
  235. = int ? frac
  236. { return parseFloat(text()); }
  237. /* ----- Core ABNF Rules ----- */
  238. /* See RFC 4234, Appendix B (http://tools.ietf.org/html/rfc4627). */
  239. DIGIT = [0-9]
  240. HEXDIG = [0-9a-f]i