/** * @class TokenStream * * BC notes: * * nextT => nextTok * lookT => lookTok * */ namespace JSDOC { public errordomain TokenStreamError { ArgumentError } public class TokenStream : Object { protected Gee.ArrayList tokens; public int cursor; // where are we in the stream. public TokenStream(Gee.ArrayList tokens) { this.tokens = tokens; this.rewind(); } public Gee.ArrayList toArray() { return this.tokens; } public void rewind() { this.cursor = -1; } /** @type JSDOC.Token */ public Token? look (int n, bool considerWhitespace) // depricated... causes all sorts of problems... { if (considerWhitespace == true) { if (this.cursor+n < 0 || this.cursor+n > (this.tokens.size -1)) { return new Token("", "VOID", "START_OF_STREAM"); } return this.tokens.get(this.cursor+n); } var count = 0; var i = this.cursor; while (true) { if (i < 0) { return new Token("", "VOID", "START_OF_STREAM"); } if (i >= this.tokens.size) { return new Token("", "VOID", "END_OF_STREAM"); } if (i != this.cursor && this.tokens.get(i).is("WHIT")) { i += (n < 0) ? -1 : 1; continue; } if (count == n) { return this.tokens.get(i); } count++; i += (n < 0) ? -1 : 1; } // return new Token("", "VOID", "STREAM_ERROR"); // because null isn't an object and caller always expects an object } // look through token stream, including white space... public Token lookAny (int n) { if (this.cursor+n < 0 || this.cursor+n > (this.tokens.size -1)) { return new Token("", "VOID", "START_OF_STREAM"); } return this.tokens.get(this.cursor+n); } public int lookFor (string data) { // non tree version.. var i = this.cursor < 0 ? 0 : this.cursor ; while (true) { if (i >= this.tokens.size) { return -1; } if (this.tokens.get(i).data == data) { return i; } i++; } // should not get here! // return -1; } /** * look ahead (or back) x number of tokens (which are not comment or whitespace) * ?? used any more? */ public Token lookTok (int n) { var count = 0; var i = this.cursor; while (true) { // print(i); if (i < 0) { if (n > -1) { i = 0; count++; continue; } return new Token("", "VOID", "END_OF_STREAM"); } if (i >= this.tokens.size) { return new Token("", "VOID", "END_OF_STREAM"); } if (i != this.cursor && ( this.tokens.get(i).is("WHIT") || this.tokens.get(i).is("COMM"))) { i += (n < 0) ? -1 : 1; continue; } if (count == n) { return this.tokens.get(i); } count++; i += (n < 0) ? -1 : 1; } // should never get here.. // return new Token("", "VOID", "END_OF_STREAM");; // because null isn't an object and caller always expects an object; } /** * @return {Token|null} * next token (with white space) */ public Token? next() { //if (typeof howMany == "undefined") howMany = 1; // if (howMany < 1) { return null; } if (this.cursor+1 >= this.tokens.size) { return null; } this.cursor++; return this.tokens.get(this.cursor); } public Gee.ArrayList? nextM(int howMany) throws TokenStreamError { //if (typeof howMany == "undefined") howMany = 1; if (howMany < 2) { throw new TokenStreamError.ArgumentError("nextM called with wrong number : %d", howMany); } var got = new Gee.ArrayList(); for (var i = 1; i <= howMany; i++) { if (this.cursor+i >= this.tokens.size) { return null; } got.add(this.tokens.get(this.cursor+i)); } this.cursor += howMany; return got; } // what about comments after 'function'... // is this used ??? public Token? nextTok() { return this.nextNonSpace(); } public Token? nextNonSpace () { while (true) { var tok = this.next(); if (tok == null) { return null; } if (tok.is("WHIT") || tok.is("COMM")) { continue; } return tok; } } /** * balance * -- returns all the tokens betweeen and including stop token eg.. from {... to } * @param start {String} token name or data (eg. '{' * @param stop {String} (Optional) token name or data (eg. '}' */ public Gee.ArrayList balance (string start, string in_stop = "") throws TokenStreamError { // accepts names or "{" etc.. var stop = in_stop; start = Lang.punc(start) == null ? start : Lang.punc(start); if (stop=="") { var newstop = Lang.matching(start); stop = newstop; } if (stop == null) { throw new TokenStreamError.ArgumentError("balance called with invalid start/stop : %s",start); } debug("START=%s, STOP=%s \n", start,stop); var depth = 0; var got = new Gee.ArrayList(); var started = false; //Seed.print("START:" + start); //Seed.print("STOP:" + stop); Token token; while (null != (token = this.lookAny(1))) { debug("BALANCE: %d %s " , this.cursor, token.asString()); if (token.is(start)) { // Seed.print("balance: START : " + depth + " " + token.data); depth++; started = true; } if (started) { got.add(token); } if (token.is(stop)) { depth--; //print("balance (%d): STOP: %s\n" , depth , token.data); if (depth < 1) { this.next(); // shift cursor to eat closer... //print("returning got %d\n", got.size); return got; } } if (null == this.next()) { break; } } return new Gee.ArrayList(); } public Token? getMatchingToken(string start, string stop) { var depth = 0; var cursor = this.cursor; if (start.length < 1) { var ns = Lang.matching(stop); start = ns; depth = 1; } if (stop.length < 1) { var ns = Lang.matching(start); stop = ns; } Token token; while (null != (token = this.tokens[cursor])) { if (token.is(start)) { depth++; } if (token.is(stop) && cursor != 0) { depth--; if (depth == 0) { return this.tokens[cursor]; } } cursor++; } return null; } /* public Gee.ArrayList insertAhead(Token token) { this.tokens.splice(this.cursor+1, 0, token); // fixme... } */ public Gee.ArrayList remaining() { var ret = new Gee.ArrayList(); while (true) { var tok = this.look(1,true); if (tok.is("VOID")) { return ret; } var nt = this.next(); if (nt != null) { ret.add(nt); } } } public void printRange(int start, int end) { for(var i = start; i < end +1; i++) { print(this.tokens.get(i).asString()); } } /* arrayToString : function(ar) { console.log(typeof(ar)); var ret = []; ar.forEach(function(e) { ret.push(e.data); }) return ret.join(''); }, */ public void dump(int start, int end) { start = int.max(start , 0); end = int.min(end, this.tokens.size); var outs = "";; for (var i =start;i < end; i++) { outs += (this.tokens[i].outData == "") ? this.tokens[i].data : this.tokens[i].outData; } print(outs); } public void dumpAll(string indent) { for (var i = 0;i < this.tokens.size; i++) { this.tokens[i].dump(""); } } public void dumpAllFlat() { for (var i = 0;i < this.tokens.size; i++) { print("%d: %s\n", i, this.tokens[i].asString()); } } } }