trader-server
Version:
OData server for testing strategies, simulating and real trading.
886 lines (728 loc) • 30.1 kB
text/typescript
import Utils from "./utils";
import Lexer from "./lexer";
import PrimitiveLiteral from "./primitiveLiteral";
import NameOrIdentifier from "./nameOrIdentifier";
import Expressions from "./expressions";
export namespace Query {
export function queryOptions(value: Utils.SourceArray, index: number, metadataContext?: any): Lexer.Token {
let token = Query.queryOption(value, index, metadataContext);
if (!token) return;
let start = index;
index = token.next;
let options = [];
while (token) {
options.push(token);
// &
if (value[index] !== 0x26) break;
index++;
token = Query.queryOption(value, index, metadataContext);
if (!token) return;
index = token.next;
}
return Lexer.tokenize(value, start, index, { options }, Lexer.TokenType.QueryOptions);
}
export function queryOption(value: Utils.SourceArray, index: number, metadataContext?: any): Lexer.Token {
return Query.systemQueryOption(value, index, metadataContext) ||
Query.aliasAndValue(value, index) ||
Query.customQueryOption(value, index);
}
export function systemQueryOption(value: Utils.SourceArray, index: number, metadataContext?: any): Lexer.Token {
return Query.expand(value, index, metadataContext) ||
Query.filter(value, index) ||
Query.format(value, index) ||
Query.id(value, index) ||
Query.inlinecount(value, index) ||
Query.orderby(value, index) ||
Query.search(value, index) ||
Query.select(value, index) ||
Query.skip(value, index) ||
Query.skiptoken(value, index) ||
Query.top(value, index);
}
export function customQueryOption(value: Utils.SourceArray, index: number): Lexer.Token {
let key = NameOrIdentifier.odataIdentifier(value, index);
if (!key) return;
let start = index;
index = key.next;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
while (value[index] !== 0x26 && index < value.length) index++;
if (index === eq) return;
return Lexer.tokenize(value, start, index, { key: key.raw, value: Utils.stringify(value, eq, index) }, Lexer.TokenType.CustomQueryOption);
}
export function id(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24id")) {
index += 5;
}else
if (Utils.equals(value, index, "$id")) {
index += 3;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
while (value[index] !== 0x26 && index < value.length) index++;
if (index === eq) return;
return Lexer.tokenize(value, start, index, Utils.stringify(value, eq, index), Lexer.TokenType.Id);
}
export function expand(value: Utils.SourceArray, index: number, metadataContext?: any): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24expand")) {
index += 9;
}else
if (Utils.equals(value, index, "$expand")) {
index += 7;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let items = [];
let token = Query.expandItem(value, index, metadataContext);
if (!token) return;
index = token.next;
while (token) {
items.push(token);
let comma = Lexer.COMMA(value, index);
if (comma) {
index = comma;
token = Query.expandItem(value, index, metadataContext);
if (!token) return;
index = token.next;
}else break;
}
return Lexer.tokenize(value, start, index, { items }, Lexer.TokenType.Expand);
}
export function expandItem(value: Utils.SourceArray, index: number, metadataContext?: any): Lexer.Token {
let start = index;
let star = Lexer.STAR(value, index);
if (star) {
index = star;
let ref = Expressions.refExpr(value, index);
if (ref) {
index = ref.next;
return Lexer.tokenize(value, start, index, { path: "*", ref }, Lexer.TokenType.ExpandItem);
} else {
let open = Lexer.OPEN(value, index);
if (open) {
index = open;
let token = Query.levels(value, index);
if (!token) return;
index = token.next;
let close = Lexer.CLOSE(value, index);
if (!close) return;
index = close;
return Lexer.tokenize(value, start, index, { path: "*", levels: token }, Lexer.TokenType.ExpandItem);
}
}
}
let path = Query.expandPath(value, index, metadataContext);
if (!path) return;
index = path.next;
let tokenValue: any = { path };
let ref = Expressions.refExpr(value, index);
if (ref) {
index = ref.next;
tokenValue.ref = ref;
let open = Lexer.OPEN(value, index);
if (open) {
index = open;
let option = Query.expandRefOption(value, index);
if (!option) return;
let refOptions = [];
while (option) {
refOptions.push(option);
index = option.next;
let semi = Lexer.SEMI(value, index);
if (semi) {
index = semi;
option = Query.expandRefOption(value, index);
if (!option) return;
}else break;
}
let close = Lexer.CLOSE(value, index);
if (!close) return;
index = close;
tokenValue.options = refOptions;
}
} else {
let count = Expressions.countExpr(value, index);
if (count) {
index = count.next;
tokenValue.count = count;
let open = Lexer.OPEN(value, index);
if (open) {
index = open;
let option = Query.expandCountOption(value, index);
if (!option) return;
let countOptions = [];
while (option) {
countOptions.push(option);
index = option.next;
let semi = Lexer.SEMI(value, index);
if (semi) {
index = semi;
option = Query.expandCountOption(value, index);
if (!option) return;
}else break;
}
let close = Lexer.CLOSE(value, index);
if (!close) return;
index = close;
tokenValue.options = countOptions;
}
} else {
let open = Lexer.OPEN(value, index);
if (open) {
index = open;
let option = Query.expandOption(value, index);
if (!option) return;
let options = [];
while (option) {
options.push(option);
index = option.next;
let semi = Lexer.SEMI(value, index);
if (semi) {
index = semi;
option = Query.expandOption(value, index);
if (!option) return;
}else break;
}
let close = Lexer.CLOSE(value, index);
if (!close) return;
index = close;
tokenValue.options = options;
}
}
}
return Lexer.tokenize(value, start, index, tokenValue, Lexer.TokenType.ExpandItem);
}
export function expandCountOption(value: Utils.SourceArray, index: number): Lexer.Token {
return Query.filter(value, index) ||
Query.search(value, index);
}
export function expandRefOption(value: Utils.SourceArray, index: number): Lexer.Token {
return Query.expandCountOption(value, index) ||
Query.orderby(value, index) ||
Query.skip(value, index) ||
Query.top(value, index) ||
Query.inlinecount(value, index);
}
export function expandOption(value: Utils.SourceArray, index: number): Lexer.Token {
return Query.expandRefOption(value, index) ||
Query.select(value, index) ||
Query.expand(value, index) ||
Query.levels(value, index);
}
export function expandPath(value: Utils.SourceArray, index: number, metadataContext?: any): Lexer.Token {
let start = index;
let path = [];
let token = NameOrIdentifier.qualifiedEntityTypeName(value, index, metadataContext) ||
NameOrIdentifier.qualifiedComplexTypeName(value, index, metadataContext);
if (token) {
index = token.next;
path.push(token);
if (value[index] !== 0x2f) return;
index++;
metadataContext = token.value.metadata;
delete token.value.metadata;
}
let complex = NameOrIdentifier.complexProperty(value, index, metadataContext) ||
NameOrIdentifier.complexColProperty(value, index, metadataContext);
while (complex) {
if (value[complex.next] === 0x2f) {
index = complex.next + 1;
path.push(complex);
let complexTypeName = NameOrIdentifier.qualifiedComplexTypeName(value, index, metadataContext);
if (complexTypeName) {
if (value[complexTypeName.next] === 0x2f) {
index = complexTypeName.next + 1;
path.push(complexTypeName);
}
metadataContext = complexTypeName.value.metadata;
delete complexTypeName.value.metadata;
}
complex = NameOrIdentifier.complexProperty(value, index, metadataContext) ||
NameOrIdentifier.complexColProperty(value, index, metadataContext);
}else break;
}
let nav = NameOrIdentifier.navigationProperty(value, index, metadataContext);
if (!nav) return;
index = nav.next;
path.push(nav);
metadataContext = nav.metadata;
delete nav.metadata;
if (value[index] === 0x2f) {
let typeName = NameOrIdentifier.qualifiedEntityTypeName(value, index + 1, metadataContext);
if (typeName) {
index = typeName.next;
path.push(typeName);
metadataContext = typeName.value.metadata;
delete typeName.value.metadata;
}
}
return Lexer.tokenize(value, start, index, path, Lexer.TokenType.ExpandPath);
}
export function search(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24search")) {
index += 9;
}else
if (Utils.equals(value, index, "$search")) {
index += 7;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let expr = Query.searchExpr(value, index);
if (!expr) return;
index = expr.next;
return Lexer.tokenize(value, start, index, expr, Lexer.TokenType.Search);
}
export function searchExpr(value: Utils.SourceArray, index: number): Lexer.Token {
let token = Query.searchParenExpr(value, index) ||
Query.searchTerm(value, index);
if (!token) return;
let start = index;
index = token.next;
let expr = Query.searchAndExpr(value, index) ||
Query.searchOrExpr(value, index);
if (expr) {
const left = Lexer.clone(token);
token.next = expr.value.next;
token.value = {
left: left,
right: expr.value
};
token.type = expr.type;
token.raw = Utils.stringify(value, token.position, token.next);
if (token.type === Lexer.TokenType.SearchAndExpression && token.value.right.type === Lexer.TokenType.SearchOrExpression) {
token.value.left = Lexer.tokenize(value, token.value.left.position, token.value.right.value.left.next, {
left: token.value.left,
right: token.value.right.value.left
}, token.type);
token.type = token.value.right.type;
token.value.right = token.value.right.value.right;
}
}
return token;
}
export function searchTerm(value: Utils.SourceArray, index: number): Lexer.Token {
return Query.searchNotExpr(value, index) ||
Query.searchPhrase(value, index) ||
Query.searchWord(value, index);
}
export function searchNotExpr(value: Utils.SourceArray, index: number): Lexer.Token {
let rws = Lexer.RWS(value, index);
if (!Utils.equals(value, rws, "NOT")) return;
let start = index;
index = rws + 3;
rws = Lexer.RWS(value, index);
if (rws === index) return;
index = rws;
let expr = Query.searchPhrase(value, index) ||
Query.searchWord(value, index);
if (!expr) return;
index = expr.next;
return Lexer.tokenize(value, start, index, expr, Lexer.TokenType.SearchNotExpression);
}
export function searchOrExpr(value: Utils.SourceArray, index: number): Lexer.Token {
let rws = Lexer.RWS(value, index);
if (rws === index || !Utils.equals(value, rws, "OR")) return;
let start = index;
index = rws + 2;
rws = Lexer.RWS(value, index);
if (rws === index) return;
index = rws;
let token = Query.searchExpr(value, index);
if (!token) return;
index = token.next;
return Lexer.tokenize(value, start, index, token, Lexer.TokenType.SearchOrExpression);
}
export function searchAndExpr(value: Utils.SourceArray, index: number): Lexer.Token {
let rws = Lexer.RWS(value, index);
if (rws === index || !Utils.equals(value, rws, "AND")) return;
let start = index;
index = rws + 3;
rws = Lexer.RWS(value, index);
if (rws === index) return;
index = rws;
let token = Query.searchExpr(value, index);
if (!token) return;
index = token.next;
return Lexer.tokenize(value, start, index, token, Lexer.TokenType.SearchAndExpression);
}
export function searchPhrase(value: Utils.SourceArray, index: number): Lexer.Token {
let mark = Lexer.quotationMark(value, index);
if (mark === index) return;
let start = index;
index = mark;
let valueStart = index;
let ch = Lexer.qcharNoAMPDQUOTE(value, index);
while (ch > index && !Lexer.OPEN(value, index) && !Lexer.CLOSE(value, index)) {
index = ch;
ch = Lexer.qcharNoAMPDQUOTE(value, index);
}
let valueEnd = index;
mark = Lexer.quotationMark(value, index);
if (!mark) return;
index = mark;
return Lexer.tokenize(value, start, index, Utils.stringify(value, valueStart, valueEnd), Lexer.TokenType.SearchPhrase);
}
export function searchWord(value: Utils.SourceArray, index: number): Lexer.Token {
let next = Utils.required(value, index, Lexer.ALPHA, 1);
if (!next) return;
let start = index;
index = next;
let token = Lexer.tokenize(value, start, index, null, Lexer.TokenType.SearchWord);
token.value = token.raw;
return token;
}
export function searchParenExpr(value: Utils.SourceArray, index: number): Lexer.Token {
let open = Lexer.OPEN(value, index);
if (!open) return;
let start = index;
index = open;
index = Lexer.BWS(value, index);
let expr = Query.searchExpr(value, index);
if (!expr) return;
index = expr.next;
index = Lexer.BWS(value, index);
let close = Lexer.CLOSE(value, index);
if (!close) return;
index = close;
return Lexer.tokenize(value, start, index, expr, Lexer.TokenType.SearchParenExpression);
}
export function levels(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24levels")) {
index += 9;
}else
if (Utils.equals(value, index, "$levels")) {
index += 7;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let level;
if (Utils.equals(value, index, "max")) {
level = "max";
index += 3;
} else {
let token = PrimitiveLiteral.int32Value(value, index);
if (!token) return;
level = token.raw;
index = token.next;
}
return Lexer.tokenize(value, start, index, level, Lexer.TokenType.Levels);
}
export function filter(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24filter")) {
index += 9;
}else
if (Utils.equals(value, index, "$filter")) {
index += 7;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let expr = Expressions.boolCommonExpr(value, index);
if (!expr) return;
index = expr.next;
return Lexer.tokenize(value, start, index, expr, Lexer.TokenType.Filter);
}
export function orderby(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24orderby")) {
index += 10;
}else
if (Utils.equals(value, index, "$orderby")) {
index += 8;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let items = [];
let token = Query.orderbyItem(value, index);
if (!token) return;
index = token.next;
while (token) {
items.push(token);
let comma = Lexer.COMMA(value, index);
if (comma) {
index = comma;
token = Query.orderbyItem(value, index);
if (!token) return;
index = token.next;
}else break;
}
return Lexer.tokenize(value, start, index, { items }, Lexer.TokenType.OrderBy);
}
export function orderbyItem(value: Utils.SourceArray, index: number): Lexer.Token {
let expr = Expressions.commonExpr(value, index);
if (!expr) return;
let start = index;
index = expr.next;
let direction = 1;
let rws = Lexer.RWS(value, index);
if (rws > index) {
index = rws;
if (Utils.equals(value, index, "asc")) index += 3;
else if (Utils.equals(value, index, "desc")) {
index += 4;
direction = -1;
}else return;
}
return Lexer.tokenize(value, start, index, { expr, direction }, Lexer.TokenType.OrderByItem);
}
export function skip(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24skip")) {
index += 7;
}else
if (Utils.equals(value, index, "$skip")) {
index += 5;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let token = PrimitiveLiteral.int32Value(value, index);
if (!token) return;
index = token.next;
return Lexer.tokenize(value, start, index, token, Lexer.TokenType.Skip);
}
export function top(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24top")) {
index += 6;
}else
if (Utils.equals(value, index, "$top")) {
index += 4;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let token = PrimitiveLiteral.int32Value(value, index);
if (!token) return;
index = token.next;
return Lexer.tokenize(value, start, index, token, Lexer.TokenType.Top);
}
export function format(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24format")) {
index += 9;
}else
if (Utils.equals(value, index, "$format")) {
index += 7;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let format;
if (Utils.equals(value, index, "atom")) {
format = "atom";
index += 4;
}else if (Utils.equals(value, index, "json")) {
format = "json";
index += 4;
}else if (Utils.equals(value, index, "xml")) {
format = "xml";
index += 3;
}
if (format) return Lexer.tokenize(value, start, index, { format }, Lexer.TokenType.Format);
}
export function inlinecount(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24count")) {
index += 8;
}else
if (Utils.equals(value, index, "$count")) {
index += 6;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let token = PrimitiveLiteral.booleanValue(value, index);
if (!token) return;
index = token.next;
return Lexer.tokenize(value, start, index, token, Lexer.TokenType.InlineCount);
}
export function select(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24select")) {
index += 9;
}else
if (Utils.equals(value, index, "$select")) {
index += 7;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let items = [];
let token = Query.selectItem(value, index);
if (!token) return;
while (token) {
items.push(token);
index = token.next;
let comma = Lexer.COMMA(value, index);
if (comma) {
index = comma;
token = Query.selectItem(value, index);
if (!token) return;
}else break;
}
return Lexer.tokenize(value, start, index, { items }, Lexer.TokenType.Select);
}
export function selectItem(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
let item;
let op = Query.allOperationsInSchema(value, index);
let star = Lexer.STAR(value, index);
if (op > index) {
item = { namespace: Utils.stringify(value, index, op - 2), value: "*" };
index = op;
}else if (star) {
item = { value: "*" };
index = star;
} else {
item = {};
let name = NameOrIdentifier.qualifiedEntityTypeName(value, index) ||
NameOrIdentifier.qualifiedComplexTypeName(value, index);
if (name && value[name.next] !== 0x2f) return;
else if (name && value[name.next] === 0x2f) {
index++;
item.name = name;
}
let select = Query.selectProperty(value, index) ||
Query.qualifiedActionName(value, index) ||
Query.qualifiedFunctionName(value, index);
if (!select) return;
index = select.next;
item = name ? { name, select } : select;
}
if (index > start) return Lexer.tokenize(value, start, index, item, Lexer.TokenType.SelectItem);
}
export function allOperationsInSchema(value: Utils.SourceArray, index: number): number {
let namespaceNext = NameOrIdentifier.namespace(value, index);
let star = Lexer.STAR(value, namespaceNext + 1);
if (namespaceNext > index && value[namespaceNext] === 0x2e && star) return star;
return index;
}
export function selectProperty(value: Utils.SourceArray, index: number): Lexer.Token {
let token = Query.selectPath(value, index) ||
NameOrIdentifier.primitiveProperty(value, index) ||
NameOrIdentifier.primitiveColProperty(value, index) ||
NameOrIdentifier.navigationProperty(value, index);
if (!token) return;
let start = index;
index = token.next;
if (token.type === Lexer.TokenType.SelectPath) {
if (value[index] === 0x2f) {
index++;
let prop = Query.selectProperty(value, index);
if (!prop) return;
let path = Lexer.clone(token);
token.next = prop.next;
token.raw = Utils.stringify(value, start, token.next);
token.value = { path, next: prop };
}
}
return token;
}
export function selectPath(value: Utils.SourceArray, index: number): Lexer.Token {
let token = NameOrIdentifier.complexProperty(value, index) ||
NameOrIdentifier.complexColProperty(value, index);
if (!token) return;
let start = index;
index = token.next;
let tokenValue: any = token;
if (value[index] === 0x2f) {
let name = NameOrIdentifier.qualifiedComplexTypeName(value, index + 1);
if (name) {
index = name.next;
tokenValue = { prop: token, name };
}
}
return Lexer.tokenize(value, start, index, tokenValue, Lexer.TokenType.SelectPath);
}
export function qualifiedActionName(value: Utils.SourceArray, index: number): Lexer.Token {
let namespaceNext = NameOrIdentifier.namespace(value, index);
if (namespaceNext === index || value[namespaceNext] !== 0x2e) return;
let start = index;
index = namespaceNext + 1;
let action = NameOrIdentifier.action(value, index);
if (!action) return;
action.value.namespace = Utils.stringify(value, start, namespaceNext);
return Lexer.tokenize(value, start, action.next, action, Lexer.TokenType.Action);
}
export function qualifiedFunctionName(value: Utils.SourceArray, index: number): Lexer.Token {
let namespaceNext = NameOrIdentifier.namespace(value, index);
if (namespaceNext === index || value[namespaceNext] !== 0x2e) return;
let start = index;
index = namespaceNext + 1;
let fn = NameOrIdentifier.odataFunction(value, index);
if (!fn) return;
fn.value.namespace = Utils.stringify(value, start, namespaceNext);
index = fn.next;
let tokenValue: any = { name: fn };
let open = Lexer.OPEN(value, index);
if (open) {
index = open;
tokenValue.parameters = [];
let param = Expressions.parameterName(value, index);
if (!param) return;
while (param) {
index = param.next;
tokenValue.parameters.push(param);
let comma = Lexer.COMMA(value, index);
if (comma) {
index = comma;
let param = Expressions.parameterName(value, index);
if (!param) return;
}else break;
}
let close = Lexer.CLOSE(value, index);
if (!close) return;
index = close;
}
return Lexer.tokenize(value, start, index, tokenValue, Lexer.TokenType.Function);
}
export function skiptoken(value: Utils.SourceArray, index: number): Lexer.Token {
let start = index;
if (Utils.equals(value, index, "%24skiptoken")) {
index += 12;
}else
if (Utils.equals(value, index, "$skiptoken")) {
index += 10;
}else return;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let ch = Lexer.qcharNoAMP(value, index);
if (!ch) return;
let valueStart = index;
while (ch > index) {
index = ch;
ch = Lexer.qcharNoAMP(value, index);
}
return Lexer.tokenize(value, start, index, Utils.stringify(value, valueStart, index), Lexer.TokenType.SkipToken);
}
export function aliasAndValue(value: Utils.SourceArray, index: number): Lexer.Token {
let alias = Expressions.parameterAlias(value, index);
if (!alias) return;
let start = index;
index = alias.next;
let eq = Lexer.EQ(value, index);
if (!eq) return;
index = eq;
let paramValue = Expressions.parameterValue(value, index);
if (!paramValue) return;
index = paramValue.next;
return Lexer.tokenize(value, start, index, {
alias,
value: paramValue
}, Lexer.TokenType.AliasAndValue);
}
}
export default Query;