├── .gitignore ├── LICENSE ├── README.md ├── gcode.js ├── index.js ├── package.json ├── parser.js ├── parser.pegjs └── test ├── extensions.nc ├── spaces.nc └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | *~ 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directory 24 | # Commenting this out is preferred by some people, see 25 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 26 | node_modules 27 | 28 | # Users Environment Variables 29 | .lock-wscript 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ryan Sturmer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-gcode 2 | GCode interpreter and simulator for node.js 3 | 4 | Most of the function of this interpreter is derived from the [NIST G-code standard.](http://www.nist.gov/customcf/get_pdf.cfm?pub_id=823374) 5 | 6 | 7 | 8 | Installation 9 | ------------ 10 | Install from npm 11 | ``` 12 | npm install gcode 13 | ``` 14 | 15 | Parsing a G-code File 16 | --------------------- 17 | ```js 18 | gcode = require('gcode') 19 | gcode.parseFile('example.nc', function(err, result) { 20 | console.log(JSON.stringify(result)); 21 | }) 22 | ``` 23 | 24 | The data returned by the `parseFile` callback is a list of G-code blocks, where each block is an object with a `num` property (the G-code line number) and a `words` property (the list of G-code words in that block) Each G-code word is a list of two items, the word letter (G, M, X,Y,Z, etc.) and the word argument. Word arguments are typically numbers, but the parser supports full expressions, including parameter values, so in the event that an expression or parameter value is provided, an expression-tree-like object is returned that must be evaluated. Currently, this is left as an exercise for the reader. 25 | ```gcode 26 | G17 27 | G1 X1 Y2 Z3 F120 28 | G1 Z0 29 | G1 X-0.125 F240 30 | G0 X[3+5] 31 | ``` 32 | 33 | The output of the above example might look like this _(note the last line that includes 3+5 in the X-axis word)_: 34 | 35 | ```js 36 | [{"N":null,"words":[["G",17]]}, 37 | {"N":null,"words":[["G",1],["X",1],["Y",2],["Z",3],["F",120]]}, 38 | {"N":null,"words":[["G",1],["Z",0]]}, 39 | {"N":null,"words":[["G",1],["Z",-0.125],["F",240]]}, 40 | {"N":null,"words":[["G",0],["X",{"left":3,"right":5,"op":"+"}]]}] 41 | ``` 42 | 43 | Parsing a G-code String 44 | ----------------------- 45 | ```js 46 | gcode = require('gcode'); 47 | gcode.parseString('G0 X1 Y2 Z3', function(err, result) { 48 | console.log(JSON.stringify(result)); 49 | }); 50 | ``` 51 | 52 | See `parseFile` above for the output format. Strings work exactly the same way. Strings can contain any number of codes, separated by newlines per the standard. The output of the above example would be: 53 | 54 | ```js 55 | [{"N":null,"words":[["G",0], ['X',1],['Y',2],['Z',3]]}] 56 | ``` 57 | 58 | G-code Interpreters 59 | ------------------- 60 | Writing a custom interpreter for G-code is easy with the `Interpreter` object provided by the gcode library. To create your own interpreter, use the following example: 61 | 62 | ```js 63 | Interpreter = require('gcode').Interpreter; 64 | 65 | var MyGCodeRunner = function() { 66 | this.units = 'imperial'; 67 | Interpreter.call(this); 68 | } 69 | util.inherits(MyGCodeRunner, Interpreter) 70 | 71 | MyGCodeRunner.prototype.G0 = function(args) { 72 | console.log("Got a G0 code!"); 73 | console.log(args); 74 | } 75 | 76 | MyGCodeRunner.prototype.G20 = function(args) { 77 | console.log("Switching to inches."); 78 | this.units = 'imperial'; 79 | } 80 | 81 | MyGCodeRunner.prototype.G21 = function(args) { 82 | console.log("Switching to millimeters."); 83 | this.units = 'metric'; 84 | } 85 | 86 | runner = new MyGCodeRunner(); 87 | 88 | runner.interpretFile('example.nc'); 89 | runner.interpretString('G0 X1 Y2 Z3\nG4 P3'); 90 | ``` 91 | 92 | Any handlers attached to the interpreter whose names correspond to G or M codes are called in turn as those codes are parsed from the incoming file stream. 93 | 94 | G-codes that contain decimal points (G38.2, G59.1, etc.) are represented as handler functions by replacing the decimal point in the code with an underscore. Thus, the handler for G38.2 would be: 95 | 96 | ```js 97 | MyGCodeRunner.prototype.G38_2 = function(args) { 98 | console.log("Initiating a straight probe: " + args); 99 | } 100 | ``` 101 | 102 | There is a special handler for any unhandled codes, which is just an underscore. If you define this method on your interpeter, it will be called for every unknown G or M code that is encountered. Because it is for handling unknown G/M codes, it takes two arguments, the command, followed by the remaining words in the code: 103 | 104 | ```js 105 | MyGCodeRunner.prototype._ = function(cmd, args) { 106 | console.log('Got an unknown G/M code: ',cmd); 107 | console.log('With arguments: ', args); 108 | } 109 | ``` 110 | -------------------------------------------------------------------------------- /gcode.js: -------------------------------------------------------------------------------- 1 | var stream = require('stream'); 2 | var util = require('util'); 3 | var parser = require('./parser'); 4 | var fs = require('fs'); 5 | var byline = require('byline'); 6 | 7 | // Strips spaces out of all incoming g-code except for comments 8 | function GCodeScrubber() { 9 | this.in_comment = false; 10 | stream.Transform.call(this); 11 | } 12 | util.inherits(GCodeScrubber, stream.Transform); 13 | 14 | GCodeScrubber.prototype._transform = function(s, enc, done) { 15 | try { 16 | for(var result = [], i=0, j=0; i 1 ? arguments[1] : {}, 29 | 30 | peg$FAILED = {}, 31 | 32 | peg$startRuleFunctions = { start: peg$parsestart }, 33 | peg$startRuleFunction = peg$parsestart, 34 | 35 | peg$c0 = peg$FAILED, 36 | peg$c1 = null, 37 | peg$c2 = [], 38 | peg$c3 = function(num, words) { 39 | return {'N':num, 'words':words} 40 | }, 41 | peg$c4 = function(word, value) { return [word, value]; }, 42 | peg$c5 = "N", 43 | peg$c6 = { type: "literal", value: "N", description: "\"N\"" }, 44 | peg$c7 = /^[0-9]/, 45 | peg$c8 = { type: "class", value: "[0-9]", description: "[0-9]" }, 46 | peg$c9 = function() { return parseInt(text()); }, 47 | peg$c10 = /^[+\-]/, 48 | peg$c11 = { type: "class", value: "[+\\-]", description: "[+\\-]" }, 49 | peg$c12 = /^[.]/, 50 | peg$c13 = { type: "class", value: "[.]", description: "[.]" }, 51 | peg$c14 = function() { return parseFloat(text()); }, 52 | peg$c15 = "[", 53 | peg$c16 = { type: "literal", value: "[", description: "\"[\"" }, 54 | peg$c17 = "]", 55 | peg$c18 = { type: "literal", value: "]", description: "\"]\"" }, 56 | peg$c19 = function(expr) {return expr; }, 57 | peg$c20 = "ATAN", 58 | peg$c21 = { type: "literal", value: "ATAN", description: "\"ATAN\"" }, 59 | peg$c22 = "/", 60 | peg$c23 = { type: "literal", value: "/", description: "\"/\"" }, 61 | peg$c24 = function(left, right) { 62 | return {'op':"ATAN", 'left':left, 'right':right}; 63 | }, 64 | peg$c25 = function(op, expr) {return {'op':op, 'right':expr}}, 65 | peg$c26 = "#", 66 | peg$c27 = { type: "literal", value: "#", description: "\"#\"" }, 67 | peg$c28 = function(expr) { return {'op':'#', 'right':expr }}, 68 | peg$c29 = function(first, rest) { 69 | return buildTree(first, rest); 70 | }, 71 | peg$c30 = function(first, rest) { 72 | return buildTree(first, rest); 73 | }, 74 | peg$c31 = "**", 75 | peg$c32 = { type: "literal", value: "**", description: "\"**\"" }, 76 | peg$c33 = "*", 77 | peg$c34 = { type: "literal", value: "*", description: "\"*\"" }, 78 | peg$c35 = "MOD", 79 | peg$c36 = { type: "literal", value: "MOD", description: "\"MOD\"" }, 80 | peg$c37 = "+", 81 | peg$c38 = { type: "literal", value: "+", description: "\"+\"" }, 82 | peg$c39 = "-", 83 | peg$c40 = { type: "literal", value: "-", description: "\"-\"" }, 84 | peg$c41 = "OR", 85 | peg$c42 = { type: "literal", value: "OR", description: "\"OR\"" }, 86 | peg$c43 = "XOR", 87 | peg$c44 = { type: "literal", value: "XOR", description: "\"XOR\"" }, 88 | peg$c45 = "AND", 89 | peg$c46 = { type: "literal", value: "AND", description: "\"AND\"" }, 90 | peg$c47 = "ABS", 91 | peg$c48 = { type: "literal", value: "ABS", description: "\"ABS\"" }, 92 | peg$c49 = "ACOS", 93 | peg$c50 = { type: "literal", value: "ACOS", description: "\"ACOS\"" }, 94 | peg$c51 = "ASIN", 95 | peg$c52 = { type: "literal", value: "ASIN", description: "\"ASIN\"" }, 96 | peg$c53 = "COS", 97 | peg$c54 = { type: "literal", value: "COS", description: "\"COS\"" }, 98 | peg$c55 = "EXP", 99 | peg$c56 = { type: "literal", value: "EXP", description: "\"EXP\"" }, 100 | peg$c57 = "FIX", 101 | peg$c58 = { type: "literal", value: "FIX", description: "\"FIX\"" }, 102 | peg$c59 = "FUP", 103 | peg$c60 = { type: "literal", value: "FUP", description: "\"FUP\"" }, 104 | peg$c61 = "ROUND", 105 | peg$c62 = { type: "literal", value: "ROUND", description: "\"ROUND\"" }, 106 | peg$c63 = "LN", 107 | peg$c64 = { type: "literal", value: "LN", description: "\"LN\"" }, 108 | peg$c65 = "SIN", 109 | peg$c66 = { type: "literal", value: "SIN", description: "\"SIN\"" }, 110 | peg$c67 = "SQRT", 111 | peg$c68 = { type: "literal", value: "SQRT", description: "\"SQRT\"" }, 112 | peg$c69 = "TAN", 113 | peg$c70 = { type: "literal", value: "TAN", description: "\"TAN\"" }, 114 | peg$c71 = "EXISTS", 115 | peg$c72 = { type: "literal", value: "EXISTS", description: "\"EXISTS\"" }, 116 | peg$c73 = "A", 117 | peg$c74 = { type: "literal", value: "A", description: "\"A\"" }, 118 | peg$c75 = "B", 119 | peg$c76 = { type: "literal", value: "B", description: "\"B\"" }, 120 | peg$c77 = "C", 121 | peg$c78 = { type: "literal", value: "C", description: "\"C\"" }, 122 | peg$c79 = "D", 123 | peg$c80 = { type: "literal", value: "D", description: "\"D\"" }, 124 | peg$c81 = "F", 125 | peg$c82 = { type: "literal", value: "F", description: "\"F\"" }, 126 | peg$c83 = "G", 127 | peg$c84 = { type: "literal", value: "G", description: "\"G\"" }, 128 | peg$c85 = "H", 129 | peg$c86 = { type: "literal", value: "H", description: "\"H\"" }, 130 | peg$c87 = "I", 131 | peg$c88 = { type: "literal", value: "I", description: "\"I\"" }, 132 | peg$c89 = "J", 133 | peg$c90 = { type: "literal", value: "J", description: "\"J\"" }, 134 | peg$c91 = "K", 135 | peg$c92 = { type: "literal", value: "K", description: "\"K\"" }, 136 | peg$c93 = "L", 137 | peg$c94 = { type: "literal", value: "L", description: "\"L\"" }, 138 | peg$c95 = "M", 139 | peg$c96 = { type: "literal", value: "M", description: "\"M\"" }, 140 | peg$c97 = "P", 141 | peg$c98 = { type: "literal", value: "P", description: "\"P\"" }, 142 | peg$c99 = "Q", 143 | peg$c100 = { type: "literal", value: "Q", description: "\"Q\"" }, 144 | peg$c101 = "R", 145 | peg$c102 = { type: "literal", value: "R", description: "\"R\"" }, 146 | peg$c103 = "S", 147 | peg$c104 = { type: "literal", value: "S", description: "\"S\"" }, 148 | peg$c105 = "T", 149 | peg$c106 = { type: "literal", value: "T", description: "\"T\"" }, 150 | peg$c107 = "X", 151 | peg$c108 = { type: "literal", value: "X", description: "\"X\"" }, 152 | peg$c109 = "Y", 153 | peg$c110 = { type: "literal", value: "Y", description: "\"Y\"" }, 154 | peg$c111 = "Z", 155 | peg$c112 = { type: "literal", value: "Z", description: "\"Z\"" }, 156 | 157 | peg$currPos = 0, 158 | peg$reportedPos = 0, 159 | peg$cachedPos = 0, 160 | peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }, 161 | peg$maxFailPos = 0, 162 | peg$maxFailExpected = [], 163 | peg$silentFails = 0, 164 | 165 | peg$result; 166 | 167 | if ("startRule" in options) { 168 | if (!(options.startRule in peg$startRuleFunctions)) { 169 | throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); 170 | } 171 | 172 | peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; 173 | } 174 | 175 | function text() { 176 | return input.substring(peg$reportedPos, peg$currPos); 177 | } 178 | 179 | function offset() { 180 | return peg$reportedPos; 181 | } 182 | 183 | function line() { 184 | return peg$computePosDetails(peg$reportedPos).line; 185 | } 186 | 187 | function column() { 188 | return peg$computePosDetails(peg$reportedPos).column; 189 | } 190 | 191 | function expected(description) { 192 | throw peg$buildException( 193 | null, 194 | [{ type: "other", description: description }], 195 | peg$reportedPos 196 | ); 197 | } 198 | 199 | function error(message) { 200 | throw peg$buildException(message, null, peg$reportedPos); 201 | } 202 | 203 | function peg$computePosDetails(pos) { 204 | function advance(details, startPos, endPos) { 205 | var p, ch; 206 | 207 | for (p = startPos; p < endPos; p++) { 208 | ch = input.charAt(p); 209 | if (ch === "\n") { 210 | if (!details.seenCR) { details.line++; } 211 | details.column = 1; 212 | details.seenCR = false; 213 | } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { 214 | details.line++; 215 | details.column = 1; 216 | details.seenCR = true; 217 | } else { 218 | details.column++; 219 | details.seenCR = false; 220 | } 221 | } 222 | } 223 | 224 | if (peg$cachedPos !== pos) { 225 | if (peg$cachedPos > pos) { 226 | peg$cachedPos = 0; 227 | peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }; 228 | } 229 | advance(peg$cachedPosDetails, peg$cachedPos, pos); 230 | peg$cachedPos = pos; 231 | } 232 | 233 | return peg$cachedPosDetails; 234 | } 235 | 236 | function peg$fail(expected) { 237 | if (peg$currPos < peg$maxFailPos) { return; } 238 | 239 | if (peg$currPos > peg$maxFailPos) { 240 | peg$maxFailPos = peg$currPos; 241 | peg$maxFailExpected = []; 242 | } 243 | 244 | peg$maxFailExpected.push(expected); 245 | } 246 | 247 | function peg$buildException(message, expected, pos) { 248 | function cleanupExpected(expected) { 249 | var i = 1; 250 | 251 | expected.sort(function(a, b) { 252 | if (a.description < b.description) { 253 | return -1; 254 | } else if (a.description > b.description) { 255 | return 1; 256 | } else { 257 | return 0; 258 | } 259 | }); 260 | 261 | while (i < expected.length) { 262 | if (expected[i - 1] === expected[i]) { 263 | expected.splice(i, 1); 264 | } else { 265 | i++; 266 | } 267 | } 268 | } 269 | 270 | function buildMessage(expected, found) { 271 | function stringEscape(s) { 272 | function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } 273 | 274 | return s 275 | .replace(/\\/g, '\\\\') 276 | .replace(/"/g, '\\"') 277 | .replace(/\x08/g, '\\b') 278 | .replace(/\t/g, '\\t') 279 | .replace(/\n/g, '\\n') 280 | .replace(/\f/g, '\\f') 281 | .replace(/\r/g, '\\r') 282 | .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) 283 | .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) 284 | .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) 285 | .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); 286 | } 287 | 288 | var expectedDescs = new Array(expected.length), 289 | expectedDesc, foundDesc, i; 290 | 291 | for (i = 0; i < expected.length; i++) { 292 | expectedDescs[i] = expected[i].description; 293 | } 294 | 295 | expectedDesc = expected.length > 1 296 | ? expectedDescs.slice(0, -1).join(", ") 297 | + " or " 298 | + expectedDescs[expected.length - 1] 299 | : expectedDescs[0]; 300 | 301 | foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; 302 | 303 | return "Expected " + expectedDesc + " but " + foundDesc + " found."; 304 | } 305 | 306 | var posDetails = peg$computePosDetails(pos), 307 | found = pos < input.length ? input.charAt(pos) : null; 308 | 309 | if (expected !== null) { 310 | cleanupExpected(expected); 311 | } 312 | 313 | return new SyntaxError( 314 | message !== null ? message : buildMessage(expected, found), 315 | expected, 316 | found, 317 | pos, 318 | posDetails.line, 319 | posDetails.column 320 | ); 321 | } 322 | 323 | function peg$parsestart() { 324 | var s0; 325 | 326 | s0 = peg$parseline(); 327 | 328 | return s0; 329 | } 330 | 331 | function peg$parseline() { 332 | var s0, s1, s2, s3; 333 | 334 | s0 = peg$currPos; 335 | s1 = peg$parseline_number(); 336 | if (s1 === peg$FAILED) { 337 | s1 = peg$c1; 338 | } 339 | if (s1 !== peg$FAILED) { 340 | s2 = []; 341 | s3 = peg$parseword(); 342 | while (s3 !== peg$FAILED) { 343 | s2.push(s3); 344 | s3 = peg$parseword(); 345 | } 346 | if (s2 !== peg$FAILED) { 347 | peg$reportedPos = s0; 348 | s1 = peg$c3(s1, s2); 349 | s0 = s1; 350 | } else { 351 | peg$currPos = s0; 352 | s0 = peg$c0; 353 | } 354 | } else { 355 | peg$currPos = s0; 356 | s0 = peg$c0; 357 | } 358 | 359 | return s0; 360 | } 361 | 362 | function peg$parseword() { 363 | var s0, s1, s2; 364 | 365 | s0 = peg$currPos; 366 | s1 = peg$parseletter(); 367 | if (s1 !== peg$FAILED) { 368 | s2 = peg$parsefactor1(); 369 | if (s2 !== peg$FAILED) { 370 | peg$reportedPos = s0; 371 | s1 = peg$c4(s1, s2); 372 | s0 = s1; 373 | } else { 374 | peg$currPos = s0; 375 | s0 = peg$c0; 376 | } 377 | } else { 378 | peg$currPos = s0; 379 | s0 = peg$c0; 380 | } 381 | 382 | return s0; 383 | } 384 | 385 | function peg$parseline_number() { 386 | var s0, s1, s2; 387 | 388 | s0 = peg$currPos; 389 | if (input.charCodeAt(peg$currPos) === 78) { 390 | s1 = peg$c5; 391 | peg$currPos++; 392 | } else { 393 | s1 = peg$FAILED; 394 | if (peg$silentFails === 0) { peg$fail(peg$c6); } 395 | } 396 | if (s1 !== peg$FAILED) { 397 | s2 = peg$parseinteger(); 398 | if (s2 !== peg$FAILED) { 399 | s1 = [s1, s2]; 400 | s0 = s1; 401 | } else { 402 | peg$currPos = s0; 403 | s0 = peg$c0; 404 | } 405 | } else { 406 | peg$currPos = s0; 407 | s0 = peg$c0; 408 | } 409 | 410 | return s0; 411 | } 412 | 413 | function peg$parseinteger() { 414 | var s0, s1, s2; 415 | 416 | s0 = peg$currPos; 417 | s1 = []; 418 | if (peg$c7.test(input.charAt(peg$currPos))) { 419 | s2 = input.charAt(peg$currPos); 420 | peg$currPos++; 421 | } else { 422 | s2 = peg$FAILED; 423 | if (peg$silentFails === 0) { peg$fail(peg$c8); } 424 | } 425 | if (s2 !== peg$FAILED) { 426 | while (s2 !== peg$FAILED) { 427 | s1.push(s2); 428 | if (peg$c7.test(input.charAt(peg$currPos))) { 429 | s2 = input.charAt(peg$currPos); 430 | peg$currPos++; 431 | } else { 432 | s2 = peg$FAILED; 433 | if (peg$silentFails === 0) { peg$fail(peg$c8); } 434 | } 435 | } 436 | } else { 437 | s1 = peg$c0; 438 | } 439 | if (s1 !== peg$FAILED) { 440 | peg$reportedPos = s0; 441 | s1 = peg$c9(); 442 | } 443 | s0 = s1; 444 | 445 | return s0; 446 | } 447 | 448 | function peg$parsenumber() { 449 | var s0, s1, s2, s3, s4, s5, s6; 450 | 451 | s0 = peg$currPos; 452 | if (peg$c10.test(input.charAt(peg$currPos))) { 453 | s1 = input.charAt(peg$currPos); 454 | peg$currPos++; 455 | } else { 456 | s1 = peg$FAILED; 457 | if (peg$silentFails === 0) { peg$fail(peg$c11); } 458 | } 459 | if (s1 === peg$FAILED) { 460 | s1 = peg$c1; 461 | } 462 | if (s1 !== peg$FAILED) { 463 | s2 = []; 464 | if (peg$c7.test(input.charAt(peg$currPos))) { 465 | s3 = input.charAt(peg$currPos); 466 | peg$currPos++; 467 | } else { 468 | s3 = peg$FAILED; 469 | if (peg$silentFails === 0) { peg$fail(peg$c8); } 470 | } 471 | if (s3 !== peg$FAILED) { 472 | while (s3 !== peg$FAILED) { 473 | s2.push(s3); 474 | if (peg$c7.test(input.charAt(peg$currPos))) { 475 | s3 = input.charAt(peg$currPos); 476 | peg$currPos++; 477 | } else { 478 | s3 = peg$FAILED; 479 | if (peg$silentFails === 0) { peg$fail(peg$c8); } 480 | } 481 | } 482 | } else { 483 | s2 = peg$c0; 484 | } 485 | if (s2 !== peg$FAILED) { 486 | s3 = peg$currPos; 487 | if (peg$c12.test(input.charAt(peg$currPos))) { 488 | s4 = input.charAt(peg$currPos); 489 | peg$currPos++; 490 | } else { 491 | s4 = peg$FAILED; 492 | if (peg$silentFails === 0) { peg$fail(peg$c13); } 493 | } 494 | if (s4 !== peg$FAILED) { 495 | s5 = []; 496 | if (peg$c7.test(input.charAt(peg$currPos))) { 497 | s6 = input.charAt(peg$currPos); 498 | peg$currPos++; 499 | } else { 500 | s6 = peg$FAILED; 501 | if (peg$silentFails === 0) { peg$fail(peg$c8); } 502 | } 503 | if (s6 !== peg$FAILED) { 504 | while (s6 !== peg$FAILED) { 505 | s5.push(s6); 506 | if (peg$c7.test(input.charAt(peg$currPos))) { 507 | s6 = input.charAt(peg$currPos); 508 | peg$currPos++; 509 | } else { 510 | s6 = peg$FAILED; 511 | if (peg$silentFails === 0) { peg$fail(peg$c8); } 512 | } 513 | } 514 | } else { 515 | s5 = peg$c0; 516 | } 517 | if (s5 !== peg$FAILED) { 518 | s4 = [s4, s5]; 519 | s3 = s4; 520 | } else { 521 | peg$currPos = s3; 522 | s3 = peg$c0; 523 | } 524 | } else { 525 | peg$currPos = s3; 526 | s3 = peg$c0; 527 | } 528 | if (s3 === peg$FAILED) { 529 | s3 = peg$c1; 530 | } 531 | if (s3 !== peg$FAILED) { 532 | peg$reportedPos = s0; 533 | s1 = peg$c14(); 534 | s0 = s1; 535 | } else { 536 | peg$currPos = s0; 537 | s0 = peg$c0; 538 | } 539 | } else { 540 | peg$currPos = s0; 541 | s0 = peg$c0; 542 | } 543 | } else { 544 | peg$currPos = s0; 545 | s0 = peg$c0; 546 | } 547 | 548 | return s0; 549 | } 550 | 551 | function peg$parseexpression() { 552 | var s0, s1, s2, s3; 553 | 554 | s0 = peg$currPos; 555 | if (input.charCodeAt(peg$currPos) === 91) { 556 | s1 = peg$c15; 557 | peg$currPos++; 558 | } else { 559 | s1 = peg$FAILED; 560 | if (peg$silentFails === 0) { peg$fail(peg$c16); } 561 | } 562 | if (s1 !== peg$FAILED) { 563 | s2 = peg$parsefactor4(); 564 | if (s2 !== peg$FAILED) { 565 | if (input.charCodeAt(peg$currPos) === 93) { 566 | s3 = peg$c17; 567 | peg$currPos++; 568 | } else { 569 | s3 = peg$FAILED; 570 | if (peg$silentFails === 0) { peg$fail(peg$c18); } 571 | } 572 | if (s3 !== peg$FAILED) { 573 | peg$reportedPos = s0; 574 | s1 = peg$c19(s2); 575 | s0 = s1; 576 | } else { 577 | peg$currPos = s0; 578 | s0 = peg$c0; 579 | } 580 | } else { 581 | peg$currPos = s0; 582 | s0 = peg$c0; 583 | } 584 | } else { 585 | peg$currPos = s0; 586 | s0 = peg$c0; 587 | } 588 | 589 | return s0; 590 | } 591 | 592 | function peg$parseatan_factor() { 593 | var s0, s1, s2, s3, s4; 594 | 595 | s0 = peg$currPos; 596 | if (input.substr(peg$currPos, 4) === peg$c20) { 597 | s1 = peg$c20; 598 | peg$currPos += 4; 599 | } else { 600 | s1 = peg$FAILED; 601 | if (peg$silentFails === 0) { peg$fail(peg$c21); } 602 | } 603 | if (s1 !== peg$FAILED) { 604 | s2 = peg$parseexpression(); 605 | if (s2 !== peg$FAILED) { 606 | if (input.charCodeAt(peg$currPos) === 47) { 607 | s3 = peg$c22; 608 | peg$currPos++; 609 | } else { 610 | s3 = peg$FAILED; 611 | if (peg$silentFails === 0) { peg$fail(peg$c23); } 612 | } 613 | if (s3 !== peg$FAILED) { 614 | s4 = peg$parseexpression(); 615 | if (s4 !== peg$FAILED) { 616 | peg$reportedPos = s0; 617 | s1 = peg$c24(s2, s4); 618 | s0 = s1; 619 | } else { 620 | peg$currPos = s0; 621 | s0 = peg$c0; 622 | } 623 | } else { 624 | peg$currPos = s0; 625 | s0 = peg$c0; 626 | } 627 | } else { 628 | peg$currPos = s0; 629 | s0 = peg$c0; 630 | } 631 | } else { 632 | peg$currPos = s0; 633 | s0 = peg$c0; 634 | } 635 | 636 | return s0; 637 | } 638 | 639 | function peg$parseunary_factor() { 640 | var s0, s1, s2; 641 | 642 | s0 = peg$currPos; 643 | s1 = peg$parseunary_op(); 644 | if (s1 !== peg$FAILED) { 645 | s2 = peg$parseexpression(); 646 | if (s2 !== peg$FAILED) { 647 | peg$reportedPos = s0; 648 | s1 = peg$c25(s1, s2); 649 | s0 = s1; 650 | } else { 651 | peg$currPos = s0; 652 | s0 = peg$c0; 653 | } 654 | } else { 655 | peg$currPos = s0; 656 | s0 = peg$c0; 657 | } 658 | 659 | return s0; 660 | } 661 | 662 | function peg$parseparam_value() { 663 | var s0, s1, s2; 664 | 665 | s0 = peg$currPos; 666 | if (input.charCodeAt(peg$currPos) === 35) { 667 | s1 = peg$c26; 668 | peg$currPos++; 669 | } else { 670 | s1 = peg$FAILED; 671 | if (peg$silentFails === 0) { peg$fail(peg$c27); } 672 | } 673 | if (s1 !== peg$FAILED) { 674 | s2 = peg$parseexpression(); 675 | if (s2 === peg$FAILED) { 676 | s2 = peg$parsenumber(); 677 | if (s2 === peg$FAILED) { 678 | s2 = peg$parseparam_value(); 679 | } 680 | } 681 | if (s2 !== peg$FAILED) { 682 | peg$reportedPos = s0; 683 | s1 = peg$c28(s2); 684 | s0 = s1; 685 | } else { 686 | peg$currPos = s0; 687 | s0 = peg$c0; 688 | } 689 | } else { 690 | peg$currPos = s0; 691 | s0 = peg$c0; 692 | } 693 | 694 | return s0; 695 | } 696 | 697 | function peg$parsefactor1() { 698 | var s0; 699 | 700 | s0 = peg$parseexpression(); 701 | if (s0 === peg$FAILED) { 702 | s0 = peg$parsenumber(); 703 | if (s0 === peg$FAILED) { 704 | s0 = peg$parseatan_factor(); 705 | if (s0 === peg$FAILED) { 706 | s0 = peg$parseunary_factor(); 707 | if (s0 === peg$FAILED) { 708 | s0 = peg$parseparam_value(); 709 | } 710 | } 711 | } 712 | } 713 | 714 | return s0; 715 | } 716 | 717 | function peg$parsefactor2() { 718 | var s0, s1, s2, s3, s4, s5; 719 | 720 | s0 = peg$currPos; 721 | s1 = peg$parsefactor1(); 722 | if (s1 !== peg$FAILED) { 723 | s2 = []; 724 | s3 = peg$currPos; 725 | s4 = peg$parsegroup1_op(); 726 | if (s4 !== peg$FAILED) { 727 | s5 = peg$parsefactor1(); 728 | if (s5 !== peg$FAILED) { 729 | s4 = [s4, s5]; 730 | s3 = s4; 731 | } else { 732 | peg$currPos = s3; 733 | s3 = peg$c0; 734 | } 735 | } else { 736 | peg$currPos = s3; 737 | s3 = peg$c0; 738 | } 739 | while (s3 !== peg$FAILED) { 740 | s2.push(s3); 741 | s3 = peg$currPos; 742 | s4 = peg$parsegroup1_op(); 743 | if (s4 !== peg$FAILED) { 744 | s5 = peg$parsefactor1(); 745 | if (s5 !== peg$FAILED) { 746 | s4 = [s4, s5]; 747 | s3 = s4; 748 | } else { 749 | peg$currPos = s3; 750 | s3 = peg$c0; 751 | } 752 | } else { 753 | peg$currPos = s3; 754 | s3 = peg$c0; 755 | } 756 | } 757 | if (s2 !== peg$FAILED) { 758 | peg$reportedPos = s0; 759 | s1 = peg$c29(s1, s2); 760 | s0 = s1; 761 | } else { 762 | peg$currPos = s0; 763 | s0 = peg$c0; 764 | } 765 | } else { 766 | peg$currPos = s0; 767 | s0 = peg$c0; 768 | } 769 | 770 | return s0; 771 | } 772 | 773 | function peg$parsefactor3() { 774 | var s0, s1, s2, s3, s4, s5; 775 | 776 | s0 = peg$currPos; 777 | s1 = peg$parsefactor2(); 778 | if (s1 !== peg$FAILED) { 779 | s2 = []; 780 | s3 = peg$currPos; 781 | s4 = peg$parsegroup2_op(); 782 | if (s4 !== peg$FAILED) { 783 | s5 = peg$parsefactor2(); 784 | if (s5 !== peg$FAILED) { 785 | s4 = [s4, s5]; 786 | s3 = s4; 787 | } else { 788 | peg$currPos = s3; 789 | s3 = peg$c0; 790 | } 791 | } else { 792 | peg$currPos = s3; 793 | s3 = peg$c0; 794 | } 795 | while (s3 !== peg$FAILED) { 796 | s2.push(s3); 797 | s3 = peg$currPos; 798 | s4 = peg$parsegroup2_op(); 799 | if (s4 !== peg$FAILED) { 800 | s5 = peg$parsefactor2(); 801 | if (s5 !== peg$FAILED) { 802 | s4 = [s4, s5]; 803 | s3 = s4; 804 | } else { 805 | peg$currPos = s3; 806 | s3 = peg$c0; 807 | } 808 | } else { 809 | peg$currPos = s3; 810 | s3 = peg$c0; 811 | } 812 | } 813 | if (s2 !== peg$FAILED) { 814 | peg$reportedPos = s0; 815 | s1 = peg$c30(s1, s2); 816 | s0 = s1; 817 | } else { 818 | peg$currPos = s0; 819 | s0 = peg$c0; 820 | } 821 | } else { 822 | peg$currPos = s0; 823 | s0 = peg$c0; 824 | } 825 | 826 | return s0; 827 | } 828 | 829 | function peg$parsefactor4() { 830 | var s0, s1, s2, s3, s4, s5; 831 | 832 | s0 = peg$currPos; 833 | s1 = peg$parsefactor3(); 834 | if (s1 !== peg$FAILED) { 835 | s2 = []; 836 | s3 = peg$currPos; 837 | s4 = peg$parsegroup3_op(); 838 | if (s4 !== peg$FAILED) { 839 | s5 = peg$parsefactor3(); 840 | if (s5 !== peg$FAILED) { 841 | s4 = [s4, s5]; 842 | s3 = s4; 843 | } else { 844 | peg$currPos = s3; 845 | s3 = peg$c0; 846 | } 847 | } else { 848 | peg$currPos = s3; 849 | s3 = peg$c0; 850 | } 851 | while (s3 !== peg$FAILED) { 852 | s2.push(s3); 853 | s3 = peg$currPos; 854 | s4 = peg$parsegroup3_op(); 855 | if (s4 !== peg$FAILED) { 856 | s5 = peg$parsefactor3(); 857 | if (s5 !== peg$FAILED) { 858 | s4 = [s4, s5]; 859 | s3 = s4; 860 | } else { 861 | peg$currPos = s3; 862 | s3 = peg$c0; 863 | } 864 | } else { 865 | peg$currPos = s3; 866 | s3 = peg$c0; 867 | } 868 | } 869 | if (s2 !== peg$FAILED) { 870 | peg$reportedPos = s0; 871 | s1 = peg$c30(s1, s2); 872 | s0 = s1; 873 | } else { 874 | peg$currPos = s0; 875 | s0 = peg$c0; 876 | } 877 | } else { 878 | peg$currPos = s0; 879 | s0 = peg$c0; 880 | } 881 | 882 | return s0; 883 | } 884 | 885 | function peg$parsegroup1_op() { 886 | var s0; 887 | 888 | if (input.substr(peg$currPos, 2) === peg$c31) { 889 | s0 = peg$c31; 890 | peg$currPos += 2; 891 | } else { 892 | s0 = peg$FAILED; 893 | if (peg$silentFails === 0) { peg$fail(peg$c32); } 894 | } 895 | 896 | return s0; 897 | } 898 | 899 | function peg$parsegroup2_op() { 900 | var s0; 901 | 902 | if (input.charCodeAt(peg$currPos) === 42) { 903 | s0 = peg$c33; 904 | peg$currPos++; 905 | } else { 906 | s0 = peg$FAILED; 907 | if (peg$silentFails === 0) { peg$fail(peg$c34); } 908 | } 909 | if (s0 === peg$FAILED) { 910 | if (input.charCodeAt(peg$currPos) === 47) { 911 | s0 = peg$c22; 912 | peg$currPos++; 913 | } else { 914 | s0 = peg$FAILED; 915 | if (peg$silentFails === 0) { peg$fail(peg$c23); } 916 | } 917 | if (s0 === peg$FAILED) { 918 | if (input.substr(peg$currPos, 3) === peg$c35) { 919 | s0 = peg$c35; 920 | peg$currPos += 3; 921 | } else { 922 | s0 = peg$FAILED; 923 | if (peg$silentFails === 0) { peg$fail(peg$c36); } 924 | } 925 | } 926 | } 927 | 928 | return s0; 929 | } 930 | 931 | function peg$parsegroup3_op() { 932 | var s0; 933 | 934 | if (input.charCodeAt(peg$currPos) === 43) { 935 | s0 = peg$c37; 936 | peg$currPos++; 937 | } else { 938 | s0 = peg$FAILED; 939 | if (peg$silentFails === 0) { peg$fail(peg$c38); } 940 | } 941 | if (s0 === peg$FAILED) { 942 | if (input.charCodeAt(peg$currPos) === 45) { 943 | s0 = peg$c39; 944 | peg$currPos++; 945 | } else { 946 | s0 = peg$FAILED; 947 | if (peg$silentFails === 0) { peg$fail(peg$c40); } 948 | } 949 | if (s0 === peg$FAILED) { 950 | if (input.substr(peg$currPos, 2) === peg$c41) { 951 | s0 = peg$c41; 952 | peg$currPos += 2; 953 | } else { 954 | s0 = peg$FAILED; 955 | if (peg$silentFails === 0) { peg$fail(peg$c42); } 956 | } 957 | if (s0 === peg$FAILED) { 958 | if (input.substr(peg$currPos, 3) === peg$c43) { 959 | s0 = peg$c43; 960 | peg$currPos += 3; 961 | } else { 962 | s0 = peg$FAILED; 963 | if (peg$silentFails === 0) { peg$fail(peg$c44); } 964 | } 965 | if (s0 === peg$FAILED) { 966 | if (input.substr(peg$currPos, 3) === peg$c45) { 967 | s0 = peg$c45; 968 | peg$currPos += 3; 969 | } else { 970 | s0 = peg$FAILED; 971 | if (peg$silentFails === 0) { peg$fail(peg$c46); } 972 | } 973 | } 974 | } 975 | } 976 | } 977 | 978 | return s0; 979 | } 980 | 981 | function peg$parseunary_op() { 982 | var s0; 983 | 984 | if (input.substr(peg$currPos, 3) === peg$c47) { 985 | s0 = peg$c47; 986 | peg$currPos += 3; 987 | } else { 988 | s0 = peg$FAILED; 989 | if (peg$silentFails === 0) { peg$fail(peg$c48); } 990 | } 991 | if (s0 === peg$FAILED) { 992 | if (input.substr(peg$currPos, 4) === peg$c49) { 993 | s0 = peg$c49; 994 | peg$currPos += 4; 995 | } else { 996 | s0 = peg$FAILED; 997 | if (peg$silentFails === 0) { peg$fail(peg$c50); } 998 | } 999 | if (s0 === peg$FAILED) { 1000 | if (input.substr(peg$currPos, 4) === peg$c51) { 1001 | s0 = peg$c51; 1002 | peg$currPos += 4; 1003 | } else { 1004 | s0 = peg$FAILED; 1005 | if (peg$silentFails === 0) { peg$fail(peg$c52); } 1006 | } 1007 | if (s0 === peg$FAILED) { 1008 | if (input.substr(peg$currPos, 3) === peg$c53) { 1009 | s0 = peg$c53; 1010 | peg$currPos += 3; 1011 | } else { 1012 | s0 = peg$FAILED; 1013 | if (peg$silentFails === 0) { peg$fail(peg$c54); } 1014 | } 1015 | if (s0 === peg$FAILED) { 1016 | if (input.substr(peg$currPos, 3) === peg$c55) { 1017 | s0 = peg$c55; 1018 | peg$currPos += 3; 1019 | } else { 1020 | s0 = peg$FAILED; 1021 | if (peg$silentFails === 0) { peg$fail(peg$c56); } 1022 | } 1023 | if (s0 === peg$FAILED) { 1024 | if (input.substr(peg$currPos, 3) === peg$c57) { 1025 | s0 = peg$c57; 1026 | peg$currPos += 3; 1027 | } else { 1028 | s0 = peg$FAILED; 1029 | if (peg$silentFails === 0) { peg$fail(peg$c58); } 1030 | } 1031 | if (s0 === peg$FAILED) { 1032 | if (input.substr(peg$currPos, 3) === peg$c59) { 1033 | s0 = peg$c59; 1034 | peg$currPos += 3; 1035 | } else { 1036 | s0 = peg$FAILED; 1037 | if (peg$silentFails === 0) { peg$fail(peg$c60); } 1038 | } 1039 | if (s0 === peg$FAILED) { 1040 | if (input.substr(peg$currPos, 5) === peg$c61) { 1041 | s0 = peg$c61; 1042 | peg$currPos += 5; 1043 | } else { 1044 | s0 = peg$FAILED; 1045 | if (peg$silentFails === 0) { peg$fail(peg$c62); } 1046 | } 1047 | if (s0 === peg$FAILED) { 1048 | if (input.substr(peg$currPos, 2) === peg$c63) { 1049 | s0 = peg$c63; 1050 | peg$currPos += 2; 1051 | } else { 1052 | s0 = peg$FAILED; 1053 | if (peg$silentFails === 0) { peg$fail(peg$c64); } 1054 | } 1055 | if (s0 === peg$FAILED) { 1056 | if (input.substr(peg$currPos, 3) === peg$c65) { 1057 | s0 = peg$c65; 1058 | peg$currPos += 3; 1059 | } else { 1060 | s0 = peg$FAILED; 1061 | if (peg$silentFails === 0) { peg$fail(peg$c66); } 1062 | } 1063 | if (s0 === peg$FAILED) { 1064 | if (input.substr(peg$currPos, 4) === peg$c67) { 1065 | s0 = peg$c67; 1066 | peg$currPos += 4; 1067 | } else { 1068 | s0 = peg$FAILED; 1069 | if (peg$silentFails === 0) { peg$fail(peg$c68); } 1070 | } 1071 | if (s0 === peg$FAILED) { 1072 | if (input.substr(peg$currPos, 3) === peg$c69) { 1073 | s0 = peg$c69; 1074 | peg$currPos += 3; 1075 | } else { 1076 | s0 = peg$FAILED; 1077 | if (peg$silentFails === 0) { peg$fail(peg$c70); } 1078 | } 1079 | if (s0 === peg$FAILED) { 1080 | if (input.substr(peg$currPos, 6) === peg$c71) { 1081 | s0 = peg$c71; 1082 | peg$currPos += 6; 1083 | } else { 1084 | s0 = peg$FAILED; 1085 | if (peg$silentFails === 0) { peg$fail(peg$c72); } 1086 | } 1087 | } 1088 | } 1089 | } 1090 | } 1091 | } 1092 | } 1093 | } 1094 | } 1095 | } 1096 | } 1097 | } 1098 | } 1099 | 1100 | return s0; 1101 | } 1102 | 1103 | function peg$parseletter() { 1104 | var s0; 1105 | 1106 | if (input.charCodeAt(peg$currPos) === 65) { 1107 | s0 = peg$c73; 1108 | peg$currPos++; 1109 | } else { 1110 | s0 = peg$FAILED; 1111 | if (peg$silentFails === 0) { peg$fail(peg$c74); } 1112 | } 1113 | if (s0 === peg$FAILED) { 1114 | if (input.charCodeAt(peg$currPos) === 66) { 1115 | s0 = peg$c75; 1116 | peg$currPos++; 1117 | } else { 1118 | s0 = peg$FAILED; 1119 | if (peg$silentFails === 0) { peg$fail(peg$c76); } 1120 | } 1121 | if (s0 === peg$FAILED) { 1122 | if (input.charCodeAt(peg$currPos) === 67) { 1123 | s0 = peg$c77; 1124 | peg$currPos++; 1125 | } else { 1126 | s0 = peg$FAILED; 1127 | if (peg$silentFails === 0) { peg$fail(peg$c78); } 1128 | } 1129 | if (s0 === peg$FAILED) { 1130 | if (input.charCodeAt(peg$currPos) === 68) { 1131 | s0 = peg$c79; 1132 | peg$currPos++; 1133 | } else { 1134 | s0 = peg$FAILED; 1135 | if (peg$silentFails === 0) { peg$fail(peg$c80); } 1136 | } 1137 | if (s0 === peg$FAILED) { 1138 | if (input.charCodeAt(peg$currPos) === 70) { 1139 | s0 = peg$c81; 1140 | peg$currPos++; 1141 | } else { 1142 | s0 = peg$FAILED; 1143 | if (peg$silentFails === 0) { peg$fail(peg$c82); } 1144 | } 1145 | if (s0 === peg$FAILED) { 1146 | if (input.charCodeAt(peg$currPos) === 71) { 1147 | s0 = peg$c83; 1148 | peg$currPos++; 1149 | } else { 1150 | s0 = peg$FAILED; 1151 | if (peg$silentFails === 0) { peg$fail(peg$c84); } 1152 | } 1153 | if (s0 === peg$FAILED) { 1154 | if (input.charCodeAt(peg$currPos) === 72) { 1155 | s0 = peg$c85; 1156 | peg$currPos++; 1157 | } else { 1158 | s0 = peg$FAILED; 1159 | if (peg$silentFails === 0) { peg$fail(peg$c86); } 1160 | } 1161 | if (s0 === peg$FAILED) { 1162 | if (input.charCodeAt(peg$currPos) === 73) { 1163 | s0 = peg$c87; 1164 | peg$currPos++; 1165 | } else { 1166 | s0 = peg$FAILED; 1167 | if (peg$silentFails === 0) { peg$fail(peg$c88); } 1168 | } 1169 | if (s0 === peg$FAILED) { 1170 | if (input.charCodeAt(peg$currPos) === 74) { 1171 | s0 = peg$c89; 1172 | peg$currPos++; 1173 | } else { 1174 | s0 = peg$FAILED; 1175 | if (peg$silentFails === 0) { peg$fail(peg$c90); } 1176 | } 1177 | if (s0 === peg$FAILED) { 1178 | if (input.charCodeAt(peg$currPos) === 75) { 1179 | s0 = peg$c91; 1180 | peg$currPos++; 1181 | } else { 1182 | s0 = peg$FAILED; 1183 | if (peg$silentFails === 0) { peg$fail(peg$c92); } 1184 | } 1185 | if (s0 === peg$FAILED) { 1186 | if (input.charCodeAt(peg$currPos) === 76) { 1187 | s0 = peg$c93; 1188 | peg$currPos++; 1189 | } else { 1190 | s0 = peg$FAILED; 1191 | if (peg$silentFails === 0) { peg$fail(peg$c94); } 1192 | } 1193 | if (s0 === peg$FAILED) { 1194 | if (input.charCodeAt(peg$currPos) === 77) { 1195 | s0 = peg$c95; 1196 | peg$currPos++; 1197 | } else { 1198 | s0 = peg$FAILED; 1199 | if (peg$silentFails === 0) { peg$fail(peg$c96); } 1200 | } 1201 | if (s0 === peg$FAILED) { 1202 | if (input.charCodeAt(peg$currPos) === 80) { 1203 | s0 = peg$c97; 1204 | peg$currPos++; 1205 | } else { 1206 | s0 = peg$FAILED; 1207 | if (peg$silentFails === 0) { peg$fail(peg$c98); } 1208 | } 1209 | if (s0 === peg$FAILED) { 1210 | if (input.charCodeAt(peg$currPos) === 81) { 1211 | s0 = peg$c99; 1212 | peg$currPos++; 1213 | } else { 1214 | s0 = peg$FAILED; 1215 | if (peg$silentFails === 0) { peg$fail(peg$c100); } 1216 | } 1217 | if (s0 === peg$FAILED) { 1218 | if (input.charCodeAt(peg$currPos) === 82) { 1219 | s0 = peg$c101; 1220 | peg$currPos++; 1221 | } else { 1222 | s0 = peg$FAILED; 1223 | if (peg$silentFails === 0) { peg$fail(peg$c102); } 1224 | } 1225 | if (s0 === peg$FAILED) { 1226 | if (input.charCodeAt(peg$currPos) === 83) { 1227 | s0 = peg$c103; 1228 | peg$currPos++; 1229 | } else { 1230 | s0 = peg$FAILED; 1231 | if (peg$silentFails === 0) { peg$fail(peg$c104); } 1232 | } 1233 | if (s0 === peg$FAILED) { 1234 | if (input.charCodeAt(peg$currPos) === 84) { 1235 | s0 = peg$c105; 1236 | peg$currPos++; 1237 | } else { 1238 | s0 = peg$FAILED; 1239 | if (peg$silentFails === 0) { peg$fail(peg$c106); } 1240 | } 1241 | if (s0 === peg$FAILED) { 1242 | if (input.charCodeAt(peg$currPos) === 88) { 1243 | s0 = peg$c107; 1244 | peg$currPos++; 1245 | } else { 1246 | s0 = peg$FAILED; 1247 | if (peg$silentFails === 0) { peg$fail(peg$c108); } 1248 | } 1249 | if (s0 === peg$FAILED) { 1250 | if (input.charCodeAt(peg$currPos) === 89) { 1251 | s0 = peg$c109; 1252 | peg$currPos++; 1253 | } else { 1254 | s0 = peg$FAILED; 1255 | if (peg$silentFails === 0) { peg$fail(peg$c110); } 1256 | } 1257 | if (s0 === peg$FAILED) { 1258 | if (input.charCodeAt(peg$currPos) === 90) { 1259 | s0 = peg$c111; 1260 | peg$currPos++; 1261 | } else { 1262 | s0 = peg$FAILED; 1263 | if (peg$silentFails === 0) { peg$fail(peg$c112); } 1264 | } 1265 | } 1266 | } 1267 | } 1268 | } 1269 | } 1270 | } 1271 | } 1272 | } 1273 | } 1274 | } 1275 | } 1276 | } 1277 | } 1278 | } 1279 | } 1280 | } 1281 | } 1282 | } 1283 | } 1284 | 1285 | return s0; 1286 | } 1287 | 1288 | 1289 | buildTree = function(first, rest) { 1290 | if(rest.length == 0) { 1291 | return first; 1292 | } else { 1293 | var next = rest.shift(); 1294 | var operator = next[0] 1295 | var term = next[1] 1296 | return {left: first, right: buildTree(term, rest), op: operator}; 1297 | } 1298 | } 1299 | 1300 | 1301 | peg$result = peg$startRuleFunction(); 1302 | 1303 | if (peg$result !== peg$FAILED && peg$currPos === input.length) { 1304 | return peg$result; 1305 | } else { 1306 | if (peg$result !== peg$FAILED && peg$currPos < input.length) { 1307 | peg$fail({ type: "end", description: "end of input" }); 1308 | } 1309 | 1310 | throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos); 1311 | } 1312 | } 1313 | 1314 | return { 1315 | SyntaxError: SyntaxError, 1316 | parse: parse 1317 | }; 1318 | })(); 1319 | -------------------------------------------------------------------------------- /parser.pegjs: -------------------------------------------------------------------------------- 1 | { 2 | buildTree = function(first, rest) { 3 | if(rest.length == 0) { 4 | return first; 5 | } else { 6 | var next = rest.shift(); 7 | var operator = next[0] 8 | var term = next[1] 9 | return {left: first, right: buildTree(term, rest), op: operator}; 10 | } 11 | } 12 | } 13 | 14 | start 15 | = line 16 | 17 | line 18 | = num:line_number? words:word* { 19 | return {'N':num, 'words':words} 20 | } 21 | 22 | word = word:letter value:real_value { return [word, value]; } 23 | 24 | line_number 25 | = "N" integer 26 | 27 | real_value 28 | = factor1 29 | 30 | integer 31 | = [0-9]+ { return parseInt(text()); } 32 | 33 | number 34 | = [\+\-]?[0-9]+([\.][0-9]+)? { return parseFloat(text()); } 35 | 36 | expression 37 | = "[" expr:factor4 "]" {return expr; } 38 | 39 | atan_factor = "ATAN" left:expression "/" right:expression { 40 | return {'op':"ATAN", 'left':left, 'right':right}; 41 | } 42 | 43 | unary_factor = op:unary_op expr:expression {return {'op':op, 'right':expr}} 44 | 45 | param_value = "#" expr:(expression / number / param_value) { return {'op':'#', 'right':expr }} 46 | 47 | factor1 48 | = expression 49 | / number 50 | / atan_factor 51 | / unary_factor 52 | / param_value 53 | 54 | factor2 55 | = first:factor1 rest:(group1_op factor1)* { 56 | return buildTree(first, rest); 57 | } 58 | factor3 59 | = first:factor2 rest:(group2_op factor2)* { 60 | return buildTree(first, rest); 61 | } 62 | factor4 63 | = first:factor3 rest:(group3_op factor3)* { 64 | return buildTree(first, rest); 65 | } 66 | 67 | group1_op = "**" 68 | group2_op = "*" / "/" / "MOD" 69 | group3_op = "+" / "-" / "OR" / "XOR" / "AND" 70 | unary_op = "ABS" / "ACOS" / "ASIN" / "COS" / "EXP" / "FIX" / "FUP" / "ROUND" / "LN" / "SIN" / "SQRT" / "TAN" / "EXISTS" 71 | letter = "A" / "B" / "C" / "D" / "F" / "G" / "H" / "I" / "J" / "K" / "L" / "M" / "P" / "Q" / "R" / "S" / "T" / "X" / "Y" / "Z" -------------------------------------------------------------------------------- /test/extensions.nc: -------------------------------------------------------------------------------- 1 | G38.2 X1 Y2 Z3 -------------------------------------------------------------------------------- /test/spaces.nc: -------------------------------------------------------------------------------- 1 | G0X1Y2Z3 2 | G0X1.0Y2.0Z3.0 3 | G0 X1.0 Y2.0 Z3.0 4 | G0 X 1.0 Y 2.0 Z 3.0 5 | G0 X 1 . 0 Y 2 . 0 Z 3 . 0 -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | var expect = require('chai').expect; 3 | var gcode = require("../index"); 4 | var util = require('util'); 5 | 6 | describe('G-Code String', function(done) { 7 | 8 | describe('Parse String', function(done) { 9 | it('Should return correct output for parsing a simple string.', function(done) { 10 | gcode.parseString('N03 G1 X1.234 Y5.678 Z-9.101112 F250', function(err, data) { 11 | should.not.exist(err); 12 | data.forEach(function(item) { 13 | words = item.words 14 | words.forEach(function(word) { 15 | if(word[0] === 'N') { word[1].should.equal(3)} 16 | if(word[0] === 'G') { word[1].should.equal(1)} 17 | if(word[0] === 'X') { word[1].should.equal(1.234)} 18 | if(word[0] === 'Y') { word[1].should.equal(5.678)} 19 | if(word[0] === 'Z') { word[1].should.equal(-9.101112)} 20 | if(word[0] === 'F') { word[1].should.equal(250)} 21 | }); 22 | }) 23 | done(); 24 | }); 25 | }); 26 | }); 27 | 28 | describe('Parse Multiline String', function(done) { 29 | it('Should return correct output for parsing a simple string.', function(done) { 30 | gcode.parseString('N03 G1 X1.234 Y5.678 Z-9.101112 F250\nG0 A1 B2', function(err, data) { 31 | should.not.exist(err); 32 | data[0].words.forEach(function(word) { 33 | if(word[0] === 'N') { word[1].should.equal(3)} 34 | if(word[0] === 'G') { word[1].should.equal(1)} 35 | if(word[0] === 'X') { word[1].should.equal(1.234)} 36 | if(word[0] === 'Y') { word[1].should.equal(5.678)} 37 | if(word[0] === 'Z') { word[1].should.equal(-9.101112)} 38 | if(word[0] === 'F') { word[1].should.equal(250)} 39 | }); 40 | data[1].words.forEach(function(word) { 41 | if(word[0] === 'G') { word[1].should.equal(0)} 42 | if(word[0] === 'A') { word[1].should.equal(1)} 43 | if(word[0] === 'B') { word[1].should.equal(2)} 44 | }); 45 | done(); 46 | }); 47 | }); 48 | }); 49 | 50 | describe('Space Invariance', function(done) { 51 | it('Should parse differently spaced lines as identical.', function(done) { 52 | gcode.parseFile('test/spaces.nc', function(err, data) { 53 | should.not.exist(err); 54 | data.forEach(function(item) { 55 | words = item.words 56 | words.forEach(function(word) { 57 | if(word[0] === 'G') { word[1].should.equal(0.0)} 58 | if(word[0] === 'X') { word[1].should.equal(1.0)} 59 | if(word[0] === 'Y') { word[1].should.equal(2.0)} 60 | if(word[0] === 'Z') { word[1].should.equal(3.0)} 61 | }); 62 | }) 63 | done(); 64 | }); 65 | }); 66 | }); 67 | 68 | describe('G-Code Extensions', function(done) { 69 | it('Should call parser functions for GX.Y functions.', function(done) { 70 | 71 | var MyGCodeRunner = function() { 72 | gcode.Interpreter.call(this); 73 | } 74 | util.inherits(MyGCodeRunner, gcode.Interpreter) 75 | 76 | MyGCodeRunner.prototype.G38_2 = function(args) { 77 | args.X.should.equal(1); 78 | args.Y.should.equal(2); 79 | args.Z.should.equal(3); 80 | done(); 81 | } 82 | 83 | runner = new MyGCodeRunner(); 84 | 85 | runner.interpretFile('test/extensions.nc'); 86 | }); 87 | }); 88 | 89 | 90 | describe('Interpret String', function(done) { 91 | it('Should correctly interpret a provided string.', function(done) { 92 | var MyGCodeRunner = function() { 93 | gcode.Interpreter.call(this); 94 | } 95 | util.inherits(MyGCodeRunner, gcode.Interpreter) 96 | 97 | MyGCodeRunner.prototype.G1 = function(args) { 98 | args.X.should.equal(1.234); 99 | args.Y.should.equal(5.678) 100 | } 101 | 102 | MyGCodeRunner.prototype._ = function(cmd, args) { 103 | cmd.should.equal('G0'); 104 | args.A.should.equal(1); 105 | args.B.should.equal(2); 106 | } 107 | 108 | var runner = new MyGCodeRunner(); 109 | 110 | runner.interpretString('N03 G1 X1.234 Y5.678 Z-9.101112 F250\nG0 A1 B2', function(err, result) { 111 | done(); 112 | }); 113 | }); 114 | }); 115 | 116 | describe('Complete Callback', function(done) { 117 | it('Should call the interpretFile callback at the end of interpreting the file.', function(done) { 118 | var MyGCodeRunner = function() { 119 | gcode.Interpreter.call(this); 120 | this.attribute = "Test Attribute"; 121 | } 122 | util.inherits(MyGCodeRunner, gcode.Interpreter) 123 | runner = new MyGCodeRunner(); 124 | runner.interpretFile('test/spaces.nc', function(err, result) { 125 | expect(this.attribute).to.equal("Test Attribute"); 126 | done(); 127 | }); 128 | }); 129 | }); 130 | 131 | 132 | }); 133 | --------------------------------------------------------------------------------