PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sys/js/fanx/ObjDecoder.js

https://bitbucket.org/bedlaczech/fan-1.0
JavaScript | 707 lines | 422 code | 86 blank | 199 comment | 139 complexity | 0dcb7f5f327ac07c96899ea82c0fcd1e MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //
  2. // Copyright (c) 2009, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 9 May 09 Andy Frank Creation
  7. // 20 May 09 Andy Frank Refactor to new OO model
  8. //
  9. /**
  10. * ObjDecoder parses an object tree from an input stream.
  11. */
  12. function fanx_ObjDecoder(input, options)
  13. {
  14. this.tokenizer = new fanx_Tokenizer(input);
  15. this.options = options;
  16. this.curt = null;
  17. this.usings = [];
  18. this.numUsings = 0;
  19. this.consume();
  20. }
  21. //////////////////////////////////////////////////////////////////////////
  22. // Parse
  23. //////////////////////////////////////////////////////////////////////////
  24. /**
  25. * Read an object from the stream.
  26. */
  27. fanx_ObjDecoder.prototype.readObj = function()
  28. {
  29. this.readHeader();
  30. return this.$readObj(null, null, true);
  31. }
  32. /**
  33. * header := [using]*
  34. */
  35. fanx_ObjDecoder.prototype.readHeader = function()
  36. {
  37. while (this.curt == fanx_Token.USING)
  38. this.usings[this.numUsings++] = this.readUsing();
  39. }
  40. /**
  41. * using := usingPod | usingType | usingAs
  42. * usingPod := "using" podName
  43. * usingType := "using" podName::typeName
  44. * usingAs := "using" podName::typeName "as" name
  45. */
  46. fanx_ObjDecoder.prototype.readUsing = function()
  47. {
  48. var line = this.tokenizer.line;
  49. this.consume();
  50. var podName = this.consumeId("Expecting pod name");
  51. var pod = fan.sys.Pod.find(podName, false);
  52. if (pod == null) throw this.err("Unknown pod: " + podName);
  53. if (this.curt != fanx_Token.DOUBLE_COLON)
  54. {
  55. this.endOfStmt(line);
  56. return new fanx_UsingPod(pod);
  57. }
  58. this.consume();
  59. var typeName = this.consumeId("Expecting type name");
  60. var t = pod.type(typeName, false);
  61. if (t == null) throw this.err("Unknown type: " + podName + "::" + typeName);
  62. if (this.curt == fanx_Token.AS)
  63. {
  64. this.consume();
  65. typeName = this.consumeId("Expecting using as name");
  66. }
  67. this.endOfStmt(line);
  68. return new fanx_UsingType(t, typeName);
  69. }
  70. /**
  71. * obj := literal | simple | complex
  72. */
  73. fanx_ObjDecoder.prototype.$readObj = function(curField, peekType, root)
  74. {
  75. // literals are stand alone
  76. if (fanx_Token.isLiteral(this.curt))
  77. {
  78. var val = this.tokenizer.val;
  79. this.consume();
  80. return val;
  81. }
  82. // [ is always list/map collection
  83. if (this.curt == fanx_Token.LBRACKET)
  84. return this.readCollection(curField, peekType);
  85. // at this point all remaining options must start
  86. // with a type signature - if peekType is non-null
  87. // then we've already read the type signature
  88. var line = this.tokenizer.line;
  89. var t = (peekType != null) ? peekType : this.readType();
  90. // type: type#
  91. // simple: type(
  92. // list/map: type[
  93. // complex: type || type{
  94. if (this.curt == fanx_Token.LPAREN)
  95. return this.readSimple(line, t);
  96. else if (this.curt == fanx_Token.POUND)
  97. return this.readTypeOrSlotLiteral(line, t);
  98. else if (this.curt == fanx_Token.LBRACKET)
  99. return this.readCollection(curField, t);
  100. else
  101. return this.readComplex(line, t, root);
  102. }
  103. /**
  104. * typeLiteral := type "#"
  105. * slotLiteral := type "#" id
  106. */
  107. fanx_ObjDecoder.prototype.readTypeOrSlotLiteral = function(line, t)
  108. {
  109. this.consume(fanx_Token.POUND, "Expected '#' for type literal");
  110. if (this.curt == fanx_Token.ID && !this.isEndOfStmt(line))
  111. {
  112. var slotName = this.consumeId("slot literal name");
  113. return t.slot(slotName);
  114. }
  115. else
  116. {
  117. return t;
  118. }
  119. }
  120. /**
  121. * simple := type "(" str ")"
  122. */
  123. fanx_ObjDecoder.prototype.readSimple = function(line, t)
  124. {
  125. // parse: type(str)
  126. this.consume(fanx_Token.LPAREN, "Expected ( in simple");
  127. var str = this.consumeStr("Expected string literal for simple");
  128. this.consume(fanx_Token.RPAREN, "Expected ) in simple");
  129. // TEMP
  130. try
  131. {
  132. var script = "fan." + t.pod().$name() + "." + t.$name() + ".fromStr('" + str + "')";
  133. var val = eval(script);
  134. return val;
  135. }
  136. catch (e)
  137. {
  138. throw fan.sys.ParseErr.make(e.toString() + " [Line " + this.line + "]", e);
  139. }
  140. // lookup the fromString method
  141. // TODO
  142. // t.finish();
  143. // Method m = t.method("fromStr", false);
  144. // if (m == null)
  145. // throw err("Missing method: " + t.qname() + ".fromStr", line);
  146. //
  147. // // invoke parse method to translate into instance
  148. // try
  149. // {
  150. // return m.invoke(null, new Object[] { str });
  151. // }
  152. // catch (ParseErr.Val e)
  153. // {
  154. // throw ParseErr.make(e.err().msg() + " [Line " + line + "]").val;
  155. // }
  156. // catch (Throwable e)
  157. // {
  158. // throw ParseErr.make(e.toString() + " [Line " + line + "]", e).val;
  159. // }
  160. }
  161. //////////////////////////////////////////////////////////////////////////
  162. // Complex
  163. //////////////////////////////////////////////////////////////////////////
  164. /**
  165. * complex := type [fields]
  166. * fields := "{" field (eos field)* "}"
  167. * field := name "=" obj
  168. */
  169. fanx_ObjDecoder.prototype.readComplex = function(line, t, root)
  170. {
  171. var toSet = fan.sys.Map.make(fan.sys.Field.$type, fan.sys.Obj.$type.toNullable());
  172. var toAdd = fan.sys.List.make(fan.sys.Obj.$type.toNullable());
  173. // read fields/collection into toSet/toAdd
  174. this.readComplexFields(t, toSet, toAdd);
  175. // get the make constructor
  176. var makeCtor = t.method("make", false);
  177. if (makeCtor == null || !makeCtor.isPublic())
  178. throw this.err("Missing public constructor " + t.qname() + ".make", line);
  179. // get argument lists
  180. var args = null;
  181. if (root && this.options != null)
  182. args = this.options.get("makeArgs");
  183. // construct object
  184. var obj = null;
  185. var setAfterCtor = true;
  186. try
  187. {
  188. // if first parameter is an function then pass toSet
  189. // as an it-block for setting the fields
  190. var p = makeCtor.params().first();
  191. if (args == null && p != null && p.type().fits(fan.sys.Func.$type))
  192. {
  193. args = fan.sys.List.make(fan.sys.Obj.$type).add(fan.sys.Field.makeSetFunc(toSet));
  194. setAfterCtor = false;
  195. }
  196. // invoke make to construct object
  197. obj = makeCtor.callList(args);
  198. }
  199. catch (e)
  200. {
  201. throw this.err("Cannot make " + t + ": " + e, line, e);
  202. }
  203. // set fields (if not passed to ctor as it-block)
  204. if (setAfterCtor && toSet.size() > 0)
  205. {
  206. var keys = toSet.keys();
  207. for (var i=0; i<keys.size(); i++)
  208. {
  209. var field = keys.get(i);
  210. var val = toSet.get(field);
  211. this.complexSet(obj, field, val, line);
  212. }
  213. }
  214. // add
  215. if (toAdd.size() > 0)
  216. {
  217. var addMethod = t.method("add", false);
  218. if (addMethod == null) throw this.err("Method not found: " + t.qname() + ".add", line);
  219. for (var i=0; i<toAdd.size(); ++i)
  220. this.complexAdd(t, obj, addMethod, toAdd.get(i), line);
  221. }
  222. return obj;
  223. }
  224. fanx_ObjDecoder.prototype.readComplexFields = function(t, toSet, toAdd)
  225. {
  226. if (this.curt != fanx_Token.LBRACE) return;
  227. this.consume();
  228. // fields and/or collection items
  229. while (this.curt != fanx_Token.RBRACE)
  230. {
  231. // try to read "id =" to see if we have a field
  232. var line = this.tokenizer.line;
  233. var readField = false;
  234. if (this.curt == fanx_Token.ID)
  235. {
  236. var name = this.consumeId("Expected field name");
  237. if (this.curt == fanx_Token.EQ)
  238. {
  239. // we have "id =" so read field
  240. this.consume();
  241. this.readComplexSet(t, line, name, toSet);
  242. readField = true;
  243. }
  244. else
  245. {
  246. // pushback to reset on start of collection item
  247. this.tokenizer.undo(this.tokenizer.type, this.tokenizer.val, this.tokenizer.line);
  248. this.curt = this.tokenizer.reset(fanx_Token.ID, name, line);
  249. }
  250. }
  251. // if we didn't read a field, we assume a collection item
  252. if (!readField) this.readComplexAdd(t, line, toAdd);
  253. if (this.curt == fanx_Token.COMMA) this.consume();
  254. else this.endOfStmt(line);
  255. }
  256. this.consume(fanx_Token.RBRACE, "Expected '}'");
  257. }
  258. fanx_ObjDecoder.prototype.readComplexSet = function(t, line, name, toSet)
  259. {
  260. // resolve field
  261. var field = t.field(name, false);
  262. if (field == null) throw this.err("Field not found: " + t.qname() + "." + name, line);
  263. // parse value
  264. var val = this.$readObj(field, null, false);
  265. try
  266. {
  267. // if const field, then make val immutable
  268. if (field.isConst()) val = fan.sys.ObjUtil.toImmutable(val);
  269. }
  270. catch (ex)
  271. {
  272. throw this.err("Cannot make object const for " + field.qname() + ": " + ex, line, ex);
  273. }
  274. // add to map
  275. toSet.set(field, val);
  276. }
  277. fanx_ObjDecoder.prototype.complexSet = function(obj, field, val, line)
  278. {
  279. try
  280. {
  281. if (field.isConst())
  282. field.set(obj, fan.sys.ObjUtil.toImmutable(val), false);
  283. else
  284. field.set(obj, val);
  285. }
  286. catch (ex)
  287. {
  288. throw this.err("Cannot set field " + field.qname() + ": " + ex, line, ex);
  289. }
  290. }
  291. fanx_ObjDecoder.prototype.readComplexAdd = function(t, line, toAdd)
  292. {
  293. var val = this.$readObj(null, null, false);
  294. // add to list
  295. toAdd.add(val);
  296. }
  297. fanx_ObjDecoder.prototype.complexAdd = function(t, obj, addMethod, val, line)
  298. {
  299. try
  300. {
  301. addMethod.invoke(obj, fan.sys.List.make(fan.sys.Obj.$type, [val]));
  302. }
  303. catch (ex)
  304. {
  305. throw this.err("Cannot call " + t.qname() + ".add: " + ex, line, ex);
  306. }
  307. }
  308. //////////////////////////////////////////////////////////////////////////
  309. // Collection
  310. //////////////////////////////////////////////////////////////////////////
  311. /**
  312. * collection := list | map
  313. */
  314. fanx_ObjDecoder.prototype.readCollection = function(curField, t)
  315. {
  316. // opening [
  317. this.consume(fanx_Token.LBRACKET, "Expecting '['");
  318. // if this could be a map type signature:
  319. // [qname:qname]
  320. // [qname:qname][]
  321. // [qname:qname][][] ...
  322. // or it could just be the type signature of
  323. // of a embedded simple, complex, or list
  324. var peekType = null;
  325. if (this.curt == fanx_Token.ID && t == null)
  326. {
  327. // peek at the type
  328. peekType = this.readType();
  329. // if we have [mapType] then this is non-inferred type signature
  330. if (this.curt == fanx_Token.RBRACKET && peekType instanceof fan.sys.MapType)
  331. {
  332. t = peekType; peekType = null;
  333. this.consume();
  334. while (this.curt == fanx_Token.LRBRACKET) { this.consume(); t = t.toListOf(); }
  335. if (this.curt == fanx_Token.QUESTION) { this.consume(); t = t.toNullable(); }
  336. if (this.curt == fanx_Token.POUND) { this.consume(); return t; }
  337. this.consume(fanx_Token.LBRACKET, "Expecting '['");
  338. }
  339. // if the type was a FFI JavaType, this isn't a collection
  340. // if (peekType != null && peekType.isJava())
  341. // return this.$readObj(curField, peekType, false);
  342. }
  343. // handle special case of [,]
  344. if (this.curt == fanx_Token.COMMA && peekType == null)
  345. {
  346. this.consume();
  347. this.consume(fanx_Token.RBRACKET, "Expecting ']'");
  348. return fan.sys.List.make(this.toListOfType(t, curField, false), []);
  349. }
  350. // handle special case of [:]
  351. if (this.curt == fanx_Token.COLON && peekType == null)
  352. {
  353. this.consume();
  354. this.consume(fanx_Token.RBRACKET, "Expecting ']'");
  355. return fan.sys.Map.make(this.toMapType(t, curField, false));
  356. }
  357. // read first list item or first map key
  358. var first = this.$readObj(null, peekType, false);
  359. // now we can distinguish b/w list and map
  360. if (this.curt == fanx_Token.COLON)
  361. return this.readMap(this.toMapType(t, curField, true), first);
  362. else
  363. return this.readList(this.toListOfType(t, curField, true), first);
  364. }
  365. /**
  366. * list := "[" obj ("," obj)* "]"
  367. */
  368. fanx_ObjDecoder.prototype.readList = function(of, first)
  369. {
  370. // setup accumulator
  371. var acc = [];
  372. acc.push(first)
  373. // parse list items
  374. while (this.curt != fanx_Token.RBRACKET)
  375. {
  376. this.consume(fanx_Token.COMMA, "Expected ','");
  377. if (this.curt == fanx_Token.RBRACKET) break;
  378. acc.push(this.$readObj(null, null, false));
  379. }
  380. this.consume(fanx_Token.RBRACKET, "Expected ']'");
  381. // infer type if needed
  382. if (of == null) of = fan.sys.Type.common(acc);
  383. return fan.sys.List.make(of, acc);
  384. }
  385. /**
  386. * map := "[" mapPair ("," mapPair)* "]"
  387. * mapPair := obj ":" + obj
  388. */
  389. fanx_ObjDecoder.prototype.readMap = function(mapType, firstKey)
  390. {
  391. // create map
  392. var map = mapType == null
  393. ? fan.sys.Map.make(fan.sys.Obj.$type, fan.sys.Obj.$type.toNullable())
  394. : fan.sys.Map.make(mapType);
  395. // finish first pair
  396. this.consume(fanx_Token.COLON, "Expected ':'");
  397. map.set(firstKey, this.$readObj(null, null, false));
  398. // parse map pairs
  399. while (this.curt != fanx_Token.RBRACKET)
  400. {
  401. this.consume(fanx_Token.COMMA, "Expected ','");
  402. if (this.curt == fanx_Token.RBRACKET) break;
  403. var key = this.$readObj(null, null, false);
  404. this.consume(fanx_Token.COLON, "Expected ':'");
  405. var val = this.$readObj(null, null, false);
  406. map.set(key, val);
  407. }
  408. this.consume(fanx_Token.RBRACKET, "Expected ']'");
  409. // infer type if necessary
  410. if (mapType == null)
  411. {
  412. var size = map.size();
  413. var k = fan.sys.Type.common(map.keys().m_values);
  414. var v = fan.sys.Type.common(map.vals().m_values);
  415. map.m_type = new fan.sys.MapType(k, v);
  416. }
  417. return map;
  418. }
  419. /**
  420. * Figure out the type of the list:
  421. * 1) if t was explicit then use it
  422. * 2) if we have field typed as a list, then use its definition
  423. * 3) if inferred is false, then drop back to list of Obj
  424. * 4) If inferred is true then return null and we'll infer the common type
  425. */
  426. fanx_ObjDecoder.prototype.toListOfType = function(t, curField, infer)
  427. {
  428. if (t != null) return t;
  429. if (curField != null)
  430. {
  431. var ft = curField.type().toNonNullable();
  432. if (ft instanceof fan.sys.ListType) return ft.v;
  433. }
  434. if (infer) return null;
  435. return fan.sys.Obj.$type.toNullable();
  436. }
  437. /**
  438. * Figure out the map type:
  439. * 1) if t was explicit then use it (check that it was a map type)
  440. * 2) if we have field typed as a map , then use its definition
  441. * 3) if inferred is false, then drop back to Obj:Obj
  442. * 4) If inferred is true then return null and we'll infer the common key/val types
  443. */
  444. fanx_ObjDecoder.prototype.toMapType = function(t, curField, infer)
  445. {
  446. if (t instanceof fan.sys.MapType)
  447. return t;
  448. if (curField != null)
  449. {
  450. var ft = curField.type().toNonNullable();
  451. if (ft instanceof fan.sys.MapType) return ft;
  452. }
  453. if (infer) return null;
  454. if (fanx_ObjDecoder.defaultMapType == null)
  455. fanx_ObjDecoder.defaultMapType =
  456. new fan.sys.MapType(fan.sys.Obj.$type, fan.sys.Obj.$type.toNullable());
  457. return fanx_ObjDecoder.defaultMapType;
  458. }
  459. //////////////////////////////////////////////////////////////////////////
  460. // Type
  461. //////////////////////////////////////////////////////////////////////////
  462. /**
  463. * type := listSig | mapSig1 | mapSig2 | qname
  464. * listSig := type "[]"
  465. * mapSig1 := type ":" type
  466. * mapSig2 := "[" type ":" type "]"
  467. *
  468. * Note: the mapSig2 with brackets is handled by the
  469. * method succinctly named readMapTypeOrCollection().
  470. */
  471. fanx_ObjDecoder.prototype.readType = function(lbracket)
  472. {
  473. if (lbracket === undefined) lbracket = false;
  474. var t = this.readSimpleType();
  475. if (this.curt == fanx_Token.QUESTION)
  476. {
  477. this.consume();
  478. t = t.toNullable();
  479. }
  480. if (this.curt == fanx_Token.COLON)
  481. {
  482. this.consume();
  483. t = new fan.sys.MapType(t, this.readType());
  484. }
  485. while (this.curt == fanx_Token.LRBRACKET)
  486. {
  487. this.consume();
  488. t = t.toListOf();
  489. }
  490. if (this.curt == fanx_Token.QUESTION)
  491. {
  492. this.consume();
  493. t = t.toNullable();
  494. }
  495. return t;
  496. }
  497. /**
  498. * qname := [podName "::"] typeName
  499. */
  500. fanx_ObjDecoder.prototype.readSimpleType = function()
  501. {
  502. // parse identifier
  503. var line = this.tokenizer.line;
  504. var n = this.consumeId("Expected type signature");
  505. // check for using imported name
  506. if (this.curt != fanx_Token.DOUBLE_COLON)
  507. {
  508. for (var i=0; i<this.numUsings; ++i)
  509. {
  510. var t = this.usings[i].resolve(n);
  511. if (t != null) return t;
  512. }
  513. throw this.err("Unresolved type name: " + n);
  514. }
  515. // must be fully qualified
  516. this.consume(fanx_Token.DOUBLE_COLON, "Expected ::");
  517. var typeName = this.consumeId("Expected type name");
  518. // resolve pod
  519. var pod = fan.sys.Pod.find(n, false);
  520. if (pod == null) throw this.err("Pod not found: " + n, line);
  521. // resolve type
  522. var type = pod.type(typeName, false);
  523. if (type == null) throw this.err("Type not found: " + n + "::" + typeName, line);
  524. return type;
  525. }
  526. //////////////////////////////////////////////////////////////////////////
  527. // Error Handling
  528. //////////////////////////////////////////////////////////////////////////
  529. /**
  530. * Create exception based on tokenizers current line.
  531. */
  532. fanx_ObjDecoder.prototype.err = function(msg)
  533. {
  534. return fanx_ObjDecoder.err(msg, this.tokenizer.line);
  535. }
  536. //////////////////////////////////////////////////////////////////////////
  537. // Tokens
  538. //////////////////////////////////////////////////////////////////////////
  539. /**
  540. * Consume the current token as a identifier.
  541. */
  542. fanx_ObjDecoder.prototype.consumeId = function(expected)
  543. {
  544. this.verify(fanx_Token.ID, expected);
  545. var id = this.tokenizer.val;
  546. this.consume();
  547. return id;
  548. }
  549. /**
  550. * Consume the current token as a String literal.
  551. */
  552. fanx_ObjDecoder.prototype.consumeStr = function(expected)
  553. {
  554. this.verify(fanx_Token.STR_LITERAL, expected);
  555. var id = this.tokenizer.val;
  556. this.consume();
  557. return id;
  558. }
  559. /**
  560. * Check that the current token matches the
  561. * specified type, and then consume it.
  562. */
  563. fanx_ObjDecoder.prototype.consume = function(type, expected)
  564. {
  565. if (type != undefined)
  566. this.verify(type, expected);
  567. this.curt = this.tokenizer.next();
  568. }
  569. /**
  570. * Check that the current token matches the specified
  571. * type, but do not consume it.
  572. */
  573. fanx_ObjDecoder.prototype.verify = function(type, expected)
  574. {
  575. if (this.curt != type)
  576. throw this.err(expected + ", not '" + fanx_Token.toString(this.curt) + "'");
  577. }
  578. /**
  579. * Is current token part of the next statement?
  580. */
  581. fanx_ObjDecoder.prototype.isEndOfStmt = function(lastLine)
  582. {
  583. if (this.curt == fanx_Token.EOF) return true;
  584. if (this.curt == fanx_Token.SEMICOLON) return true;
  585. return lastLine < this.tokenizer.line;
  586. }
  587. /**
  588. * Statements can be terminated with a semicolon, end of line or } end of block.
  589. */
  590. fanx_ObjDecoder.prototype.endOfStmt = function(lastLine)
  591. {
  592. if (this.curt == fanx_Token.SEMICOLON) { this.consume(); return; }
  593. if (lastLine < this.tokenizer.line) return;
  594. if (this.curt == fanx_Token.RBRACE) return;
  595. throw this.err("Expected end of statement: semicolon, newline, or end of block; not '" + fanx_Token.toString(this.curt) + "'");
  596. }
  597. //////////////////////////////////////////////////////////////////////////
  598. // Static
  599. //////////////////////////////////////////////////////////////////////////
  600. fanx_ObjDecoder.decode = function(s)
  601. {
  602. return new fanx_ObjDecoder(fan.sys.InStream.makeForStr(s), null).readObj();
  603. }
  604. fanx_ObjDecoder.err = function(msg, line)
  605. {
  606. return fan.sys.IOErr.make(msg + " [Line " + line + "]");
  607. }
  608. fanx_ObjDecoder.defaultMapType = null;
  609. //////////////////////////////////////////////////////////////////////////
  610. // Using
  611. //////////////////////////////////////////////////////////////////////////
  612. function fanx_UsingPod(p) { this.pod = p; }
  613. fanx_UsingPod.prototype.resolve = function(n)
  614. {
  615. return this.pod.type(n, false);
  616. }
  617. function fanx_UsingType(t,n) { this.type = t; this.name = n; }
  618. fanx_UsingType.prototype.resolve = function(n)
  619. {
  620. return this.name.equals(n) ? this.type : null;
  621. }