/jsmeter/parse.js

http://jsmeter.googlecode.com/ · JavaScript · 782 lines · 682 code · 60 blank · 40 comment · 114 complexity · f6beebda37eb0020a7b12cc969525cc6 MD5 · raw file

  1. // parse.js
  2. // Parser for Simplified JavaScript written in Simplified JavaScript
  3. // From Top Down Operator Precedence
  4. // http://javascript.crockford.com/tdop/index.html
  5. // Douglas Crockford
  6. // 2008-07-07
  7. exports.make_parse = function () {
  8. var scope;
  9. var symbol_table = {};
  10. var token;
  11. var tokens;
  12. var token_nr;
  13. var nextComments = [ ];
  14. var itself = function () {
  15. return this;
  16. };
  17. var original_scope = {
  18. define: function (n) {
  19. var t = this.def[n.value];
  20. /*if (typeof t === "object") {
  21. n.error(t.reserved ? "Already reserved." : "Already defined.");
  22. }*/
  23. this.def[n.value] = n;
  24. n.reserved = false;
  25. n.nud = itself;
  26. n.led = null;
  27. n.std = null;
  28. n.lbp = 0;
  29. n.scope = scope;
  30. return n;
  31. },
  32. find: function (n) {
  33. var e = this, o;
  34. while (true) {
  35. o = e.def[n];
  36. if (o && o.nud) {
  37. return o;
  38. }
  39. e = e.parent;
  40. if (!e) {
  41. if (!symbol_table.hasOwnProperty(n)) {
  42. var s = symbol(n);
  43. s.nud = function() {
  44. return this;
  45. }
  46. }
  47. return symbol_table[n];
  48. }
  49. }
  50. },
  51. pop: function () {
  52. scope = this.parent;
  53. },
  54. reserve: function (n) {
  55. if (n.arity !== "name" || n.reserved) {
  56. return;
  57. }
  58. var t = this.def[n.value];
  59. if (t) {
  60. if (t.reserved) {
  61. return;
  62. }
  63. if (t.arity === "name") {
  64. //n.error("Already defined.");
  65. }
  66. }
  67. this.def[n.value] = n;
  68. n.reserved = true;
  69. }
  70. };
  71. var new_scope = function () {
  72. var s = scope;
  73. scope = Object.create(original_scope);
  74. scope.def = {};
  75. scope.parent = s;
  76. return scope;
  77. };
  78. var advance = function (id) {
  79. var a, o, t, v, cl, cli;
  80. if (id && token.id !== id) {
  81. token.error("Expected '" + id + "'.");
  82. }
  83. if (token_nr >= tokens.length) {
  84. token = symbol_table["(end)"];
  85. return;
  86. }
  87. t = tokens[token_nr];
  88. token_nr += 1;
  89. v = t.value;
  90. a = t.type;
  91. if (a === "name") {
  92. o = scope.find(v);
  93. } else if (a === "operator") {
  94. o = symbol_table[v];
  95. if (!o) {
  96. t.error("Unknown operator.");
  97. }
  98. } else if (a === "string" || a === "number" || a === "regexp" || a === "regexpops") {
  99. o = symbol_table["(literal)"];
  100. a = "literal";
  101. } else if (a === "comment") {
  102. o = symbol_table["(comment)"];
  103. } else {
  104. t.error("Unexpected token.");
  105. }
  106. token = Object.create(o);
  107. token.from = t.from;
  108. token.to = t.to;
  109. token.line = t.line;
  110. token.value = v;
  111. token.arity = a;
  112. //window.status = JSON.stringify(token);
  113. if (token.arity === "comment") {
  114. cl = v.split(/\n/g);
  115. for (cli=0; cli<cl.length; cli++) {
  116. nextComments.push(cl[cli]);
  117. }
  118. advance();
  119. }
  120. return token;
  121. };
  122. var expression = function (rbp) {
  123. var left;
  124. var t = token;
  125. advance();
  126. left = t.nud();
  127. while (rbp < token.lbp) {
  128. t = token;
  129. advance();
  130. left = t.led(left);
  131. }
  132. if (left) {
  133. left.comments = nextComments;
  134. nextComments = [ ];
  135. }
  136. return left;
  137. };
  138. var statement = function () {
  139. var n = token, v;
  140. if (n.std) {
  141. advance();
  142. scope.reserve(n);
  143. return n.std();
  144. }
  145. v = expression(0);
  146. /*if (!v.assignment &&
  147. v.id !== "(" &&
  148. v.id!== "++" &&
  149. v.id!== "--" &&
  150. v.value!=="use strict" &&
  151. v.id!=="typeof") {
  152. v.error("Bad expression statement.");
  153. }*/
  154. /*if (v.assignment && v.arity==="function") {
  155. advance();
  156. } else {
  157. advance(";");
  158. }*/
  159. if (token.id===";") {
  160. advance(";");
  161. }
  162. if (v) {
  163. v.comments = nextComments;
  164. nextComments = [ ];
  165. }
  166. return v;
  167. };
  168. var statements = function () {
  169. var a = [], s;
  170. while (true) {
  171. if (token.id === "}" || token.id === "(end)") {
  172. break;
  173. }
  174. s = statement();
  175. if (s) {
  176. a.push(s);
  177. }
  178. }
  179. return a.length === 0 ? null : a.length === 1 ? a[0] : a;
  180. };
  181. var block = function () {
  182. var t = token;
  183. advance("{");
  184. return t.std();
  185. };
  186. var original_symbol = {
  187. nud: function () {
  188. //this.error("Undefined.");
  189. },
  190. led: function (left) {
  191. this.error("Missing operator.");
  192. }
  193. };
  194. var symbol = function (id, bp) {
  195. var s = symbol_table[id];
  196. bp = bp || 0;
  197. if (s) {
  198. if (bp >= s.lbp) {
  199. s.lbp = bp;
  200. }
  201. } else {
  202. s = Object.create(original_symbol);
  203. s.id = s.value = id;
  204. s.lbp = bp;
  205. symbol_table[id] = s;
  206. }
  207. return s;
  208. };
  209. var constant = function (s, v) {
  210. var x = symbol(s);
  211. x.nud = function () {
  212. scope.reserve(this);
  213. this.value = symbol_table[this.id].value;
  214. this.arity = "literal";
  215. return this;
  216. };
  217. x.value = v;
  218. return x;
  219. };
  220. var infix = function (id, bp, led) {
  221. var s = symbol(id, bp);
  222. s.led = led || function (left) {
  223. this.first = left;
  224. this.second = expression(bp);
  225. this.arity = "binary";
  226. return this;
  227. };
  228. return s;
  229. };
  230. var infixr = function (id, bp, led) {
  231. var s = symbol(id, bp);
  232. s.led = led || function (left) {
  233. this.first = left;
  234. this.second = expression(bp - 1);
  235. this.arity = "binary";
  236. return this;
  237. };
  238. return s;
  239. };
  240. var assignment = function (id) {
  241. return infixr(id, 10, function (left) {
  242. if (left.id !== "." && left.id !== "[" && left.arity !== "name") {
  243. left.error("Bad lvalue.");
  244. }
  245. this.first = left;
  246. this.second = expression(9);
  247. this.assignment = true;
  248. this.arity = "binary";
  249. if (token.id===",") {
  250. advance(",");
  251. }
  252. return this;
  253. });
  254. };
  255. var prefix = function (id, nud) {
  256. var s = symbol(id);
  257. s.nud = nud || function () {
  258. scope.reserve(this);
  259. this.first = expression(70);
  260. this.arity = "unary";
  261. return this;
  262. };
  263. return s;
  264. };
  265. var stmt = function (s, f) {
  266. var x = symbol(s);
  267. x.std = f;
  268. return x;
  269. };
  270. symbol("(end)");
  271. symbol("(name)");
  272. symbol(":");
  273. symbol(";");
  274. symbol(")");
  275. symbol("]");
  276. symbol("}");
  277. symbol(",");
  278. symbol("else");
  279. constant("true", true);
  280. constant("false", false);
  281. constant("null", null);
  282. constant("pi", 3.141592653589793);
  283. constant("Object", {});
  284. constant("Array", []);
  285. constant("Date", "Date");
  286. constant("Math", "Math");
  287. symbol("(literal)").nud = itself;
  288. symbol("(comment)");
  289. symbol("this").nud = function () {
  290. scope.reserve(this);
  291. this.arity = "this";
  292. return this;
  293. };
  294. assignment("=");
  295. assignment("+=");
  296. assignment("-=");
  297. assignment("*=");
  298. assignment("/=");
  299. assignment("%=");
  300. assignment("&=");
  301. assignment("|=");
  302. assignment("^=");
  303. assignment(">>=");
  304. assignment(">>>=");
  305. assignment("<<=");
  306. infix("?", 20, function (left) {
  307. this.first = left;
  308. this.second = expression(0);
  309. advance(":");
  310. this.third = expression(0);
  311. this.arity = "ternary";
  312. return this;
  313. });
  314. infixr("&", 20);
  315. infixr("|", 20);
  316. infixr("&&", 30);
  317. infixr("||", 30);
  318. infixr("in", 40);
  319. infixr("==", 40);
  320. infixr("!=", 40);
  321. infixr("===", 40);
  322. infixr("!==", 40);
  323. infixr("<", 40);
  324. infixr("<=", 40);
  325. infixr(">", 40);
  326. infixr(">=", 40);
  327. infixr(">>", 40);
  328. infixr(">>>", 40);
  329. infixr("<<", 40);
  330. infixr("instanceof", 45);
  331. infix("+", 50);
  332. infix("-", 50);
  333. infix("^", 60);
  334. infix("*", 60);
  335. infix("/", 60);
  336. infix("%", 60);
  337. infix("++", 65, function (left) {
  338. this.first = left;
  339. this.arity = "unary";
  340. return this;
  341. });
  342. infix("--", 65, function (left) {
  343. this.first = left;
  344. this.arity = "unary";
  345. return this;
  346. });
  347. infix(".", 80, function (left) {
  348. this.first = left;
  349. //if (token.arity !== "name") {
  350. // token.error("Expected a property name.");
  351. //}
  352. token.arity = "literal";
  353. this.second = token;
  354. this.arity = "binary";
  355. advance();
  356. return this;
  357. });
  358. infix("[", 80, function (left) {
  359. this.first = left;
  360. this.second = expression(0);
  361. this.arity = "binary";
  362. advance("]");
  363. return this;
  364. });
  365. infix("(", 80, function (left) {
  366. var a = [];
  367. if (left && (left.id === "." || left.id === "[")) {
  368. this.arity = "ternary";
  369. this.first = left.first;
  370. this.second = left.second;
  371. this.third = a;
  372. } else {
  373. this.arity = "binary";
  374. this.first = left;
  375. this.second = a;
  376. /*if ((left.arity !== "unary" || left.id !== "function") &&
  377. left.arity !== "name" && left.id !== "(" &&
  378. left.id !== "&&" && left.id !== "||" && left.id !== "?" &&
  379. left.id !== "function") {
  380. left.error("Expected a variable name.");
  381. }*/
  382. }
  383. if (token.id !== ")") {
  384. while (true) {
  385. a.push(expression(0));
  386. if (token.id !== ",") {
  387. break;
  388. }
  389. advance(",");
  390. }
  391. }
  392. advance(")");
  393. return this;
  394. });
  395. prefix("new");
  396. prefix("!");
  397. prefix("~");
  398. prefix("-");
  399. prefix("+");
  400. prefix("--");
  401. prefix("++");
  402. prefix("typeof", function() {
  403. var e = expression(0);
  404. this.first = e;
  405. return this;
  406. });
  407. prefix("(", function () {
  408. var e = expression(0);
  409. advance(")");
  410. return e;
  411. });
  412. prefix("function", function () {
  413. var a = [];
  414. new_scope();
  415. if (token.arity === "name") {
  416. scope.define(token);
  417. this.name = token.value;
  418. advance();
  419. }
  420. if (token.id !== "(") {
  421. scope.define(token);
  422. this.name = token.value;
  423. advance();
  424. }
  425. advance("(");
  426. if (token.id !== ")") {
  427. while (true) {
  428. if (token.arity !== "name") {
  429. token.error("Expected a parameter name.");
  430. }
  431. scope.define(token);
  432. a.push(token);
  433. advance();
  434. if (token.id !== ",") {
  435. break;
  436. }
  437. advance(",");
  438. }
  439. }
  440. this.first = a;
  441. advance(")");
  442. this.second = block();
  443. /*advance("{");
  444. this.second = statements();
  445. advance("}");*/
  446. this.arity = "function";
  447. this.assignment = true;
  448. scope.pop();
  449. return this;
  450. });
  451. prefix("[", function () {
  452. var a = [];
  453. if (token.id !== "]") {
  454. while (true) {
  455. a.push(expression(0));
  456. if (token.id !== ",") {
  457. break;
  458. }
  459. advance(",");
  460. }
  461. }
  462. advance("]");
  463. this.first = a;
  464. this.arity = "unary";
  465. return this;
  466. });
  467. prefix("{", function () {
  468. var a = [], n, v;
  469. if (token.id !== "}") {
  470. while (true) {
  471. n = token;
  472. if (n.arity !== "name" && n.arity !== "literal") {
  473. token.error("Bad property name.");
  474. }
  475. advance();
  476. advance(":");
  477. v = expression(0);
  478. v.key = n.value;
  479. a.push(v);
  480. if (token.id !== ",") {
  481. break;
  482. }
  483. advance(",");
  484. }
  485. }
  486. advance("}");
  487. this.first = a;
  488. this.arity = "unary";
  489. return this;
  490. });
  491. stmt("<script", function() {
  492. while (token.value!==">") {
  493. advance();
  494. }
  495. advance(">");
  496. });
  497. stmt("</script", function() {
  498. while (token.value!==">") {
  499. advance();
  500. }
  501. advance(">");
  502. });
  503. stmt("{", function () {
  504. new_scope();
  505. var a = statements();
  506. advance("}");
  507. scope.pop();
  508. return a;
  509. });
  510. stmt("var", function () {
  511. var a = [], n, t;
  512. while (true) {
  513. n = token;
  514. if (n.arity !== "name") {
  515. n.error("Expected a new variable name.");
  516. }
  517. scope.define(n);
  518. advance();
  519. if (token.id === "=") {
  520. t = token;
  521. advance("=");
  522. t.first = n;
  523. t.second = expression(0);
  524. t.arity = "binary";
  525. a.push(t);
  526. }
  527. if (token.id === "in") {
  528. t = token;
  529. advance("in");
  530. t.first = n;
  531. t.second = expression(0);
  532. t.arity = "binary";
  533. a.push(t);
  534. }
  535. if (token.id !== ",") {
  536. break;
  537. }
  538. advance(",");
  539. }
  540. if (token.id === ";") {
  541. advance(";");
  542. }
  543. return a.length === 0 ? null : a.length === 1 ? a[0] : a;
  544. });
  545. stmt("try", function() {
  546. this.first = block();
  547. if (token.value === "catch") {
  548. this.second = statement();
  549. }
  550. if (token.value === "finally") {
  551. //this.third = statement();
  552. }
  553. this.arity = "statement";
  554. return this;
  555. });
  556. stmt("catch", function() {
  557. advance("(");
  558. if (token.id!==")") {
  559. this.first = expression(0);
  560. }
  561. advance(")");
  562. this.second = block();
  563. this.arity = "statement";
  564. return this;
  565. });
  566. stmt("finally", function() {
  567. this.first = block();
  568. this.arity = "statement";
  569. return this;
  570. });
  571. stmt("if", function () {
  572. advance("(");
  573. this.first = expression(0);
  574. advance(")");
  575. if (token.value==="{") {
  576. this.second = block();
  577. } else {
  578. this.second = statement();
  579. }
  580. if (token.id === "else") {
  581. scope.reserve(token);
  582. advance("else");
  583. if (token.id==="if") {
  584. this.third = statement();
  585. } else if (token.value==="{") {
  586. this.third = block();
  587. } else {
  588. this.third = statement();
  589. }
  590. //this.third = token.id === "if" ? statement() : block();
  591. } else {
  592. this.third = null;
  593. }
  594. this.arity = "statement";
  595. return this;
  596. });
  597. stmt("debugger", function() {
  598. if (token.id === ";") {
  599. advance(";");
  600. }
  601. this.arity = "statement";
  602. return this;
  603. });
  604. stmt("return", function () {
  605. this.first = null;
  606. this.second = null;
  607. if (token.id !== ";") {
  608. this.first = expression(0);
  609. }
  610. if (token.id === ";") {
  611. advance(";");
  612. }
  613. this.arity = "statement";
  614. return this;
  615. });
  616. stmt("throw", function() {
  617. this.first = expression(0);
  618. if (token.id === ";") {
  619. advance(";");
  620. }
  621. this.arity = "statement";
  622. return this;
  623. });
  624. stmt("delete", function() {
  625. this.first = expression(0);
  626. if (token.id === ";") {
  627. advance(";");
  628. }
  629. this.arity = "statement";
  630. return this;
  631. });
  632. stmt("break", function () {
  633. if (token.id === ";") {
  634. advance(";");
  635. }
  636. if (token.id !== "}" && token.id !== "case" && token.id !== "default" && token.id !== "return") {
  637. //token.error("Unreachable statement.");
  638. }
  639. this.arity = "statement";
  640. return this;
  641. });
  642. stmt("while", function () {
  643. advance("(");
  644. this.first = expression(0);
  645. advance(")");
  646. if (token.value==="{") {
  647. this.second = block();
  648. } else {
  649. this.second = statement();
  650. }
  651. this.arity = "statement";
  652. return this;
  653. });
  654. stmt("switch", function() {
  655. advance("(");
  656. this.first = expression(0);
  657. advance(")");
  658. this.second = block();
  659. this.arity = "statement";
  660. return this;
  661. });
  662. stmt("case", function() {
  663. this.first = expression(0);
  664. advance(":");
  665. this.arity = "statement";
  666. return this;
  667. });
  668. stmt("default", function() {
  669. advance(":");
  670. this.arity = "statement";
  671. return this;
  672. });
  673. stmt("for", function() {
  674. this.first = [ ];
  675. advance("(");
  676. if (token.value==="var") {
  677. this.first.push(statement());
  678. } else if (token.value!==";") {
  679. while (token.id!==";" && token.id!==")") {
  680. this.first.push(expression(0));
  681. }
  682. if (token.id===";") {
  683. advance(";");
  684. }
  685. } else {
  686. advance(";");
  687. }
  688. while (token.id!==")") {
  689. if (token.value!==";") {
  690. this.first.push(expression(0));
  691. if (token.id===";") {
  692. advance(";");
  693. }
  694. } else {
  695. advance(";");
  696. }
  697. }
  698. advance(")");
  699. if (token.value==="{") {
  700. this.second = block();
  701. } else {
  702. this.second = statement();
  703. }
  704. this.arity = "statement";
  705. return this;
  706. });
  707. return function (source) {
  708. tokens = source.tokens('=<>!+-*&|/%^', '=<>&|+-/');
  709. token_nr = 0;
  710. new_scope();
  711. advance();
  712. var s = statements();
  713. advance("(end)");
  714. scope.pop();
  715. if (s.length) {
  716. s[s.length-1].comments = nextComments;
  717. } else {
  718. s.comments = nextComments;
  719. }
  720. return s;
  721. };
  722. };