List<Expression> GetParameters() // e.g. myfun(a, b, c+2) { // check whether next token is a (, // restore state and bail if it's not var pos = _ptr; var tk = _token; GetToken(); if (_token.ID != TKID.OPEN) { _ptr = pos; _token = tk; return null; } // check for empty Parameter list pos = _ptr; GetToken(); if (_token.ID == TKID.CLOSE) { return null; } _ptr = pos; // get Parameters until we reach the end of the list var parms = new List<Expression>(); var expr = ParseExpression(); parms.Add(expr); while (_token.ID == TKID.COMMA) { expr = ParseExpression(); parms.Add(expr); } // make sure the list was closed correctly if (_token.ID != TKID.CLOSE) { Throw(); } // done return parms; }
Token GetMember() { // check whether next token is a MEMBER token ('.'), // restore state and bail if it's not var pos = _ptr; var tk = _token; GetToken(); if (_token.ID != TKID.PERIOD) { _ptr = pos; _token = tk; return null; } // skip member token GetToken(); if (_token.Type != TKTYPE.IDENTIFIER) { Throw("Identifier expected"); } return _token; }
void AddToken(object symbol, TKID id, TKTYPE type) { var token = new Token(symbol, id, type); _tkTbl.Add(symbol, token); }
//--------------------------------------------------------------------------- #region ** parser void GetToken() { // eat white space while (_ptr < _len && _expr[_ptr] <= ' ') { _ptr++; } // are we done? if (_ptr >= _len) { _token = new Token(null, TKID.END, TKTYPE.GROUP); return; } // prepare to parse int i; var c = _expr[_ptr]; // operators // this gets called a lot, so it's pretty optimized. // note that operators must start with non-letter/digit characters. var isLetter = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); var isDigit = c >= '0' && c <= '9'; if (!isLetter && !isDigit) { // if this is a number starting with a decimal, don't parse as operator var nxt = _ptr + 1 < _len ? _expr[_ptr + 1] : 0; bool isNumber = c == _decimal && nxt >= '0' && nxt <= '9'; if (!isNumber) { // look up localized list separator if (c == _listSep) { _token = new Token(c, TKID.COMMA, TKTYPE.GROUP); _ptr++; return; } // look up single-char tokens on table Token tk; if (_tkTbl.TryGetValue(c, out tk)) { // save token we found _token = tk; _ptr++; // look for double-char tokens (special case) if (_ptr < _len && (c == '>' || c == '<')) { if (_tkTbl.TryGetValue(_expr.Substring(_ptr - 1, 2), out tk)) { _token = tk; _ptr++; } } // found token on the table return; } } } // parse numbers if (isDigit || c == _decimal) { var sci = false; var pct = false; var div = -1.0; // use double, not int (this may get really big) var val = 0.0; for (i = 0; i + _ptr < _len; i++) { c = _expr[_ptr + i]; // digits always OK if (c >= '0' && c <= '9') { val = val * 10 + (c - '0'); if (div > -1) { div *= 10; } continue; } // one decimal is OK if (c == _decimal && div < 0) { div = 1; continue; } // scientific notation? if ((c == 'E' || c == 'e') && !sci) { sci = true; c = _expr[_ptr + i + 1]; if (c == '+' || c == '-') i++; continue; } // percentage? if (c == _percent) { pct = true; i++; break; } // end of literal break; } // end of number, get value if (!sci) { // much faster than ParseDouble if (div > 1) { val /= div; } if (pct) { val /= 100.0; } } else { var lit = _expr.Substring(_ptr, i); val = ParseDouble(lit, _ci); } // build token _token = new Token(val, TKID.ATOM, TKTYPE.LITERAL); // advance pointer and return _ptr += i; return; } // parse strings if (c == '\"') { // look for end quote, skip double quotes for (i = 1; i + _ptr < _len; i++) { c = _expr[_ptr + i]; if (c != '\"') continue; char cNext = i + _ptr < _len - 1 ? _expr[_ptr + i + 1]: ' '; if (cNext != '\"') break; i++; } // check that we got the end of the string if (c != '\"') { Throw("Can't find final quote."); } // end of string var lit = _expr.Substring(_ptr + 1, i - 1); _ptr += i + 1; _token = new Token(lit.Replace("\"\"", "\""), TKID.ATOM, TKTYPE.LITERAL); return; } // parse dates (review) if (c == '#') { // look for end # for (i = 1; i + _ptr < _len; i++) { c = _expr[_ptr + i]; if (c == '#') break; } // check that we got the end of the date if (c != '#') { Throw("Can't find final date delimiter ('#')."); } // end of date var lit = _expr.Substring(_ptr + 1, i - 1); _ptr += i + 1; _token = new Token(DateTime.Parse(lit, _ci), TKID.ATOM, TKTYPE.LITERAL); return; } // identifiers (functions, objects) must start with alpha or underscore if (!isLetter && c != '_' && (_idChars == null || _idChars.IndexOf(c) < 0)) { Throw("Identifier expected."); } // and must contain only letters/digits/_idChars for (i = 1; i + _ptr < _len; i++) { c = _expr[_ptr + i]; isLetter = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); isDigit = c >= '0' && c <= '9'; if (!isLetter && !isDigit && c != '_' && (_idChars == null || _idChars.IndexOf(c) < 0)) { break; } } // got identifier var id = _expr.Substring(_ptr, i); _ptr += i; _token = new Token(id, TKID.ATOM, TKTYPE.IDENTIFIER); }
internal Expression(Token tk) { _token = tk; }
internal Expression(object value) { _token = new Token(value, TKID.ATOM, TKTYPE.LITERAL); }
//--------------------------------------------------------------------------- #region ** ctors internal Expression() { _token = new Token(null, TKID.ATOM, TKTYPE.IDENTIFIER); }
// ** ctor public BinaryExpression(Token tk, Expression exprLeft, Expression exprRight) : base(tk) { _lft = exprLeft; _rgt = exprRight; }
// ** ctor public UnaryExpression(Token tk, Expression expr) : base(tk) { _expr = expr; }