├── .gitignore
├── README.md
├── bin
└── rpn.js
├── examples
├── simple-example.rpn
├── test.html
└── with-error.rpn
├── lib
├── rpn.js
└── rpn
│ ├── ast.js
│ ├── bnf.js
│ └── lex.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | examples/*.js
2 | examples/*.map
3 | node_modules/
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fitzgen/rpn-js/0d041df637ee8c2e53d1bb40850533978656c357/README.md
--------------------------------------------------------------------------------
/bin/rpn.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var fs = require("fs");
3 | var rpn = require("rpn");
4 |
5 | process.argv.slice(2).forEach(function (sourceFilename) {
6 |
7 | var codeFilename = sourceFilename.replace(/\.[\w]+$/, ".js");
8 | var mapFilename = codeFilename + ".map";
9 |
10 | var input = fs.readFileSync(sourceFilename);
11 |
12 | var rootSourceNode = rpn.compile(input, {originalFilename: sourceFilename});
13 |
14 | // output :: { code :: String, map :: SourceMapGenerator }
15 | var output = rootSourceNode.toStringWithSourceMap({ file: mapFilename});
16 |
17 | //We must add the //# sourceMappingURL comment directive
18 | //so that the browser’s debugger knows where to find the source map.
19 | output.code += "\n//# sourceMappingURL=" + mapFilename;
20 |
21 | fs.writeFileSync(codeFilename, output.code);
22 | fs.writeFileSync(mapFilename, output.map);
23 |
24 | });
25 |
--------------------------------------------------------------------------------
/examples/simple-example.rpn:
--------------------------------------------------------------------------------
1 | a 8 =;
2 | b 3 =;
3 | c a b 1 - / =;
4 | c 1 print;
5 |
--------------------------------------------------------------------------------
/examples/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RPN Test Page!
5 |
6 |
7 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/with-error.rpn:
--------------------------------------------------------------------------------
1 | a 9 =;
2 | b 3 =;
3 | c a b / =;
4 | c a b c - / =;
5 | c 1 print;
--------------------------------------------------------------------------------
/lib/rpn.js:
--------------------------------------------------------------------------------
1 | var jison = require("jison");
2 | var sourceMap = require("source-map");
3 | var lex = require("./rpn/lex").lex;
4 | var bnf = require("./rpn/bnf").bnf;
5 |
6 | var parser = new jison.Parser({
7 | lex: lex,
8 | bnf: bnf
9 | });
10 |
11 | parser.yy = require("./rpn/ast");
12 |
13 | function getPreamble () {
14 | return new sourceMap.SourceNode(null, null, null, "")
15 | .add("var __rpn = {};\n")
16 | .add("__rpn._stack = [];\n")
17 | .add("__rpn.temp = 0;\n")
18 |
19 | .add("__rpn.push = function (val) {\n")
20 | .add(" __rpn._stack.push(val);\n")
21 | .add("};\n")
22 |
23 | .add("__rpn.pop = function () {\n")
24 | .add(" if (__rpn._stack.length > 0) {\n")
25 | .add(" return __rpn._stack.pop();\n")
26 | .add(" }\n")
27 | .add(" else {\n")
28 | .add(" throw new Error('can\\\'t pop from empty stack');\n")
29 | .add(" }\n")
30 | .add("};\n")
31 |
32 | .add("__rpn.print = function (val, repeat) {\n")
33 | .add(" while (repeat-- > 0) {\n")
34 | .add(" var el = document.createElement('div');\n")
35 | .add(" var txt = document.createTextNode(val);\n")
36 | .add(" el.appendChild(txt);\n")
37 | .add(" document.body.appendChild(el);\n")
38 | .add(" }\n")
39 | .add("};\n");
40 | }
41 |
42 | exports.compile = function (input, data) {
43 | var expressions = parser.parse(input.toString());
44 | var preamble = getPreamble();
45 |
46 | var result = new sourceMap.SourceNode(null, null, null, preamble);
47 | result.add(expressions.map(function (exp) {
48 | return exp.compile(data);
49 | }));
50 |
51 | return result;
52 | };
53 |
--------------------------------------------------------------------------------
/lib/rpn/ast.js:
--------------------------------------------------------------------------------
1 | var sourceMap = require("source-map");
2 | var SourceNode = sourceMap.SourceNode;
3 |
4 |
5 | function push(val) {
6 | return ["__rpn.push(", val, ");\n"];
7 | }
8 |
9 |
10 | var AstNode = function (line, column) {
11 | this._line = line;
12 | this._column = column;
13 | };
14 | AstNode.prototype.compile = function (data) {
15 | throw new Error("Not Yet Implemented");
16 | };
17 | AstNode.prototype.compileReference = function (data) {
18 | return this.compile(data);
19 | };
20 | AstNode.prototype._sn = function (originalFilename, chunk) {
21 | return new SourceNode(this._line, this._column, originalFilename, chunk);
22 | };
23 |
24 |
25 | exports.Number = function (line, column, numberText) {
26 | AstNode.call(this, line, column);
27 | this._value = Number(numberText);
28 | };
29 | exports.Number.prototype = Object.create(AstNode.prototype);
30 | exports.Number.prototype.compile = function (data) {
31 | return this._sn(data.originalFilename,
32 | push(this._value.toString()));
33 | };
34 |
35 |
36 | exports.Variable = function (line, column, variableText) {
37 | AstNode.call(this, line, column);
38 | this._name = variableText;
39 | };
40 | exports.Variable.prototype = Object.create(AstNode.prototype);
41 | exports.Variable.prototype.compileReference = function (data) {
42 | return this._sn(data.originalFilename,
43 | push(["'", this._name, "'"]));
44 | };
45 | exports.Variable.prototype.compile = function (data) {
46 | return this._sn(data.originalFilename,
47 | push(["window.", this._name]));
48 | };
49 |
50 |
51 | exports.Expression = function (line, column, operand1, operand2, operator) {
52 | AstNode.call(this, line, column);
53 | this._left = operand1;
54 | this._right = operand2;
55 | this._operator = operator;
56 | };
57 | exports.Expression.prototype = Object.create(AstNode.prototype);
58 | exports.Expression.prototype.compile = function (data) {
59 | var temp = "__rpn.temp";
60 | var output = this._sn(data.originalFilename, "");
61 |
62 | switch (this._operator.symbol) {
63 | case 'print':
64 | return output
65 | .add(this._left.compile(data))
66 | .add(this._right.compile(data))
67 | .add([temp, " = __rpn.pop();\n"])
68 | .add(["if (", temp, " <= 0) throw new Error('argument must be greater than 0');\n"])
69 | .add(["if (Math.floor(", temp, ") != ", temp,
70 | ") throw new Error('argument must be an integer');\n"])
71 | .add([this._operator.compile(data), "(__rpn.pop(), ", temp, ");\n"]);
72 | case '=':
73 | return output
74 | .add(this._right.compile(data))
75 | .add(this._left.compileReference(data))
76 | .add(["window[__rpn.pop()] ", this._operator.compile(data), " __rpn.pop();\n"]);
77 | case '/':
78 | return output
79 | .add(this._left.compile(data))
80 | .add(this._right.compile(data))
81 | .add([temp, " = __rpn.pop();\n"])
82 | .add(["if (", temp, " === 0) throw new Error('divide by zero error');\n"])
83 | .add(push(["__rpn.pop() ", this._operator.compile(data), " ", temp]));
84 | default:
85 | return output
86 | .add(this._left.compile(data))
87 | .add(this._right.compile(data))
88 | .add([temp, " = __rpn.pop();\n"])
89 | .add(push(["__rpn.pop() ", this._operator.compile(data), " ", temp]));
90 | }
91 | };
92 |
93 |
94 | exports.Operator = function (line, column, operatorText) {
95 | AstNode.call(this, line, column);
96 | this.symbol = operatorText;
97 | };
98 | exports.Operator.prototype = Object.create(AstNode.prototype);
99 | exports.Operator.prototype.compile = function (data) {
100 | if (this.symbol === "print") {
101 | return this._sn(data.originalFilename,
102 | "__rpn.print");
103 | }
104 | else {
105 | return this._sn(data.originalFilename,
106 | this.symbol);
107 | }
108 | };
109 |
--------------------------------------------------------------------------------
/lib/rpn/bnf.js:
--------------------------------------------------------------------------------
1 | exports.bnf = {
2 | start: [
3 | ["input EOF", "return $$;"]
4 | ],
5 | input: [
6 | ["", "$$ = [];"],
7 | ["line input", "$$ = [$1].concat($2);"]
8 | ],
9 | line: [
10 | ["exp SEMICOLON", "$$ = $1;"]
11 | ],
12 | exp: [
13 | ["NUMBER", "$$ = new yy.Number(@1.first_line, @1.first_column, yytext);"],
14 | ["VARIABLE", "$$ = new yy.Variable(@1.first_line, @1.first_column, yytext);"],
15 | ["exp exp operator", "$$ = new yy.Expression(@3.first_line, @3.first_column, $1, $2, $3);"]
16 | ],
17 | operator: [
18 | ["PRINT", "$$ = new yy.Operator(@1.first_line, @1.first_column, yytext);"],
19 | ["=", "$$ = new yy.Operator(@1.first_line, @1.first_column, yytext);"],
20 | ["+", "$$ = new yy.Operator(@1.first_line, @1.first_column, yytext);"],
21 | ["-", "$$ = new yy.Operator(@1.first_line, @1.first_column, yytext);"],
22 | ["*", "$$ = new yy.Operator(@1.first_line, @1.first_column, yytext);"],
23 | ["/", "$$ = new yy.Operator(@1.first_line, @1.first_column, yytext);"]
24 | ]
25 | };
26 |
--------------------------------------------------------------------------------
/lib/rpn/lex.js:
--------------------------------------------------------------------------------
1 | exports.lex = {
2 | rules: [
3 | ["\\s+", "/* Skip whitespace! */"],
4 | ["#.*\\n", "/* Skip comments! */"],
5 | [";", "return 'SEMICOLON'"],
6 | ["\\-?[0-9]+(\\.[0-9]+)?", "return 'NUMBER';"],
7 | ["print", "return 'PRINT';"],
8 | ["[a-zA-Z][a-zA-Z0-9_]*", "return 'VARIABLE';"],
9 | ["=", "return '=';"],
10 | ["\\+", "return '+';"],
11 | ["\\-", "return '-';"],
12 | ["\\*", "return '*';"],
13 | ["\\/", "return '/';"],
14 | ["$", "return 'EOF';"]
15 | ]
16 | };
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Nick Fitzgerald (http://fitzgeraldnick.com)",
3 | "name": "rpn",
4 | "description": "A reverse polish notation -> JavaScript compiler using source maps",
5 | "version": "0.0.0",
6 | "main": "./lib/rpn.js",
7 | "directories": { "lib": "./lib" },
8 | "dependencies": {
9 | "jison": ">=0.4.4",
10 | "source-map": ">=0.1.22"
11 | },
12 | "devDependencies": {},
13 | "optionalDependencies": {},
14 | "engines": {
15 | "node": "*"
16 | },
17 | "bin": {
18 | "rpn.js": "./bin/rpn.js"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------