UNPKG

ares-ide

Version:

A browser-based code editor and UI designer for Enyo 2 projects

257 lines (253 loc) 7.26 kB
enyo.kind({ name: "analyzer.Parser", kind: "analyzer.AnalyzerDebug", constructor: function(inTokens) { // Debug mode is off by default. Could be dynamically turn on by calling AnalyzerDebug._debugEnabled = true; this.debug = analyzer.AnalyzerDebug._debugEnabled; return this.parse(inTokens); }, parse: function(inTokens) { // remove ws for easier debugging var tokens = []; var it = new analyzer.Iterator(inTokens); while (it.next()) { if (it.value.kind !== "ws") { tokens.push(it.value); } } // parse the token stream it = new analyzer.Iterator(tokens); return this.walk(it); }, combine: function(inNodes) { var r = ''; for (var i=0, n; (n=inNodes[i]); i++) { r += n.token; } return r; }, walk: function(it, inState) { if (this.debug) { this.logMethodEntry(it, "inState " + inState + " >>" + JSON.stringify(it.value) + "<<"); } var nodes = [], node; try { while (it.next()) { node = it.value; if (this.debug) { this.logProcessing(it, node); } // if (node.kind == "ws") { continue; } else if (node.kind == "comment") { node.kind = "comment"; } // else if (inState == "array") { if (node.kind == "terminal") { continue; } // we haven't actually used it.value yet, but we are about to initiate another walk, which will advance the stream pointer // put it.value back so we don't lose it it.prev(); var saved = it.value; // we collect each element as an object node = { kind: "element", token: "expr", children: this.walk(it, "expression") }; // if the token that terminated the expression was a ']', close the array // Do the same if we couldn't parse the children, for whatever reason (usually, a syntax error) if ((it.value && it.value.token == "]") || (it.value && it.value === saved)) { if (node.children.length) { // only push the node if it's got children nodes.push(node); } if (this.debug) { this.logMethodExit(it); } return nodes; } } else if (node.token == "[") { node.kind = "array"; node.children = this.walk(it, node.kind); if (it.value) { node.end = it.value.end; } else { if (this.debug) { this.logIterMsg(it, "No end token for array?"); } } } else if (inState == "expression" && node.token == "]") { if (this.debug) { this.logMethodExit(it); } return nodes; } // else if (node.token == "var") { node.kind = "var"; node.children = this.walk(it, "expression"); } // // terminals (; or ,) else if (node.kind == "terminal" && (inState == "expression" || inState == "var")) { if (this.debug) { this.logMethodExit(it); } return nodes; } else if (node.kind == "terminal") { continue; } // // block else if (node.token == "{") { node.kind = "block"; if (this.debug) { this.logIterMsg(it, "PROCESS BLOCK - START"); } node.children = this.walk(it, node.kind); if (this.debug) { this.logIterMsg(it, "PROCESS BLOCK - END"); } if (it.value) { node.end = it.value.end; } else { if (this.debug) { this.logIterMsg(it, "No end token for block?"); } } // Check if the block is terminated by a comma NB: Does not change the iterator node.commaTerminated = this.isCommaTerminated(it); if (inState == "expression" || inState == "function") { // a block terminates an expression nodes.push(node); if (this.debug) { this.logMethodExit(it); } return nodes; } } // close block during expression processing else if (inState == "expression" && (node.token == "}" || node.token == ")")) { // put the token back so the calling context can use it it.prev(); if (this.debug) { this.logMethodExit(it); } return nodes; } // close block during block processing else if (inState == "block" && node.token == "}") { if (this.debug) { this.logMethodExit(it); } return nodes; } // // assignment else if (node.token == "=" || (node.token == ":" && inState != "expression")) { var prev = nodes.pop(); if (prev.kind == "identifier") { prev.op = node.token; prev.kind = "assignment"; prev.children = this.walk(it, "expression"); // if our expression hit a terminal, don't consume it if (it.value && it.value.kind == "terminal") { prev.commaTerminated = (it.value.token === ','); it.prev(); } node = prev; } else { nodes.push(prev); } } // association else if (node.token == "(") { node.kind = "association"; node.children = this.walk(it, node.kind); } else if (inState == "association" && node.token == ")") { if (this.debug) { this.logMethodExit(it); } return nodes; } // function keyword else if (node.token == "function") { node.kind = "function"; if (this.debug) { this.logIterMsg(it, "PROCESS FUNCTION - START"); } node.children = this.walk(it, node.kind); if (this.debug) { this.logIterMsg(it, "PROCESS FUNCTION - END"); } if (it.value && it.value.kind === "symbol" && it.value.token === "}") { // Nothing to to } else { if (this.debug) { this.logIterMsg(it, "No end token for function?"); } } // if we are not processing an expression, this is an anonymous function or it is using "C-style" naming syntax // "function <name>(){..}" if (inState !== "expression" && node.children && node.children.length && node.children[0].kind == "identifier") { if (this.debug) { this.logIterMsg(it, "C-Style function"); } // tag the function with a name property node.name = node.children[0].token; node.children.shift(); // optionally convert this function to be an assignment node in the AST var neo = { kind: "assignment", token: node.name, children: [node] }; node = neo; } if (inState == "expression" || inState == "function") { // Determine if the function is followed by a comma NB: Does not change the iterator node.commaTerminated = this.isCommaTerminated(it); // a function terminates an expression nodes.push(node); if (this.debug) { this.logMethodExit(it); } return nodes; } } if (this.debug) { this.logIterMsg(it, "PUSH NODE"); } nodes.push(node); } } catch(x) { window.console.error(x); } if (this.debug) { this.logMethodExit(it); } return nodes; }, isCommaTerminated: function(it) { /* * This function read the next value * Check if it's a comma * Put back the value in the iterator */ var commaPresent = false; var item = it.next(); // Get next token to check if it's a comma if (item) { commaPresent = (item.kind === 'terminal' && item.token === ','); } it.prev(); // put the token back so the calling context can use it return commaPresent; } });