src/resolver/client.js (1,036 lines of code) (raw):
'use strict';
const debug = require('../lib/debug');
const BaseResolver = require('./base');
const {
AnnotationItem,
ConstructItem,
ObjectItem,
FuncItem,
PropItem,
GrammerVar,
GrammerCall,
GrammerExpr,
GrammerLoop,
GrammerBreak,
GrammerCatch,
GrammerValue,
GrammerReturn,
GrammerThrows,
GrammerFinally,
GrammerContinue,
GrammerTryCatch,
GrammerNewObject,
GrammerCondition,
GrammerException,
BehaviorRetry,
BehaviorToMap,
BehaviorToModel,
BehaviorTimeNow,
BehaviorDoAction,
BehaviorSetMapItem,
BehaviorTamplateString,
TypeMap,
TypeBool,
TypeVoid,
TypeItem,
TypeNull,
TypeObject,
TypeString,
TypeInteger,
TypeGeneric,
} = require('../langs/common/items');
const {
Symbol,
Modify,
} = require('../langs/common/enum');
const {
_isBasicType, is, _string
} = require('../lib/helper');
const assert = require('assert');
const systemPackage = ['Util'];
const int32 = new TypeInteger(32);
const genericType = new TypeGeneric();
const runtimeType = new TypeMap(new TypeString(), genericType);
const errorType = new TypeObject('$Error');
const exceptionType = new TypeObject('$Exception');
const requestType = new TypeObject('$Request');
const nullType = new TypeNull();
const unretryableType = new TypeObject('$ExceptionUnretryable');
const retryableType = new TypeObject('$ExceptionRetryable');
class ClientResolver extends BaseResolver {
constructor(astNode, combinator, globalAst) {
super(astNode, combinator, globalAst);
this.object = new ObjectItem('client');
this.currThrows = {};
}
resolve() {
const object = this.object;
const combinator = this.combinator;
const config = this.config;
const ast = this.ast;
combinator.config.emitType = 'client';
object.name = config.clientName || 'Client';
// resolve props
this.resolveProps(ast);
// resolve global annotation
if (ast.annotation) {
this.initAnnotation(ast.annotation);
}
object.topAnnotation.push(new AnnotationItem(
object.index,
'single',
config.generateFileInfo
));
// resolve extends
if (ast.extends) {
object.extends.push(`^${ast.extends.lexeme}`);
} else if (config.baseClient) {
let extendsClass = [];
if (Array.isArray(config.baseClient)) {
config.baseClient.forEach(item => extendsClass.push(`^${item}`));
} else {
extendsClass = [`^${config.baseClient}`];
}
object.extends = extendsClass;
}
// resolve construct body
const [init] = ast.moduleBody.nodes.filter((item) => {
return item.type === 'init';
});
if (init) {
this.resolveInitBody(init);
}
// resolve api
ast.moduleBody.nodes.filter((item) => {
return item.type === 'api';
}).forEach(item => {
const func = new FuncItem();
func.name = item.apiName.lexeme;
this.resolveFunc(func, item, item.apiBody);
});
// resolve function
ast.moduleBody.nodes.filter((item) => {
return item.type === 'function';
}).forEach(item => {
const func = new FuncItem();
func.name = item.functionName.lexeme;
this.resolveFunc(func, item, item.functionBody);
});
return object;
}
resolveProps(ast) {
this.comments = ast.comments;
ast.moduleBody.nodes.filter((item) => {
return item.type === 'type';
}).forEach(item => {
const prop = new PropItem();
prop.name = item.vid.lexeme.replace('@', '_');
prop.type = this.resolveTypeItem(item.value, item);
prop.addModify(Modify.public());
if (item.tokenRange) {
let comments = this.getFrontComments(item.tokenRange[0]);
if (comments.length > 0) {
comments.forEach(c => {
this.object.addBodyNode(this.resolveAnnotation(c, this.object.index));
});
}
}
this.object.addBodyNode(prop);
});
}
resolveInitBody(init) {
const object = this.object;
let constructNode = new ConstructItem();
this.currThrows = {};
if (init.params && init.params.params) {
init.params.params.forEach(param => {
let type = this.resolveTypeItem(param.paramType, param);
constructNode.addParamNode(new GrammerValue(type, param.defaultValue, param.paramName.lexeme));
});
}
if (init.annotation) {
constructNode.addAnnotation(this.resolveAnnotation(init.annotation, constructNode.index));
}
if (init.initBody && init.initBody.stmts) {
init.initBody.stmts.forEach(stmt => {
this.visitStmt(constructNode, stmt, constructNode.index);
});
}
if (Object.keys(this.currThrows).length > 0) {
constructNode.throws = Object.values(this.currThrows);
}
object.addBodyNode(constructNode);
}
resolveFunc(func, ast, body) {
this.currThrows = {};
if (ast.annotation) {
func.addAnnotation(this.resolveAnnotation(ast.annotation, func.index));
}
this.addAnnotations(func, ast);
if (body === null) {
func.addBodyNode(new GrammerThrows(exceptionType, [], 'Un-implemented'));
}
func.hasThrow = ast.hasThrow;
if (ast.isAsync || ast.type === 'api') {
func.modify.push(Modify.async());
}
if (ast.isStatic) {
func.modify.push(Modify.static());
}
func.modify.push(Modify.public());
ast.params.params.forEach(p => {
var param = new GrammerValue();
param.type = this.resolveTypeItem(p.paramType, p);
param.key = p.paramName.lexeme;
if (ast.isStatic) {
param.isOptional = true;
}
if (p.needValidate) {
func.addBodyNode(new GrammerCall('method', [
{ type: 'object', name: param.key },
{ type: 'call', name: 'validate' }
], [], new TypeVoid(), true)); // validator
}
func.params.push(param);
});
func.return.push(this.resolveTypeItem(ast.returnType));
if (ast.runtimeBody) {
this.runtimeMode(func, ast, body);
} else {
if (ast.functionBody || ast.wrapBody) {
body.stmts.stmts.forEach(stmtItem => {
this.visitStmt(func, stmtItem, func.index);
});
} else {
this.requestBody(ast, body, func);
this.currThrows['$Exception'] = exceptionType;
}
}
if (func.body.length === 0) {
this.findComments(func, body, 'between');
}
if (Object.keys(this.currThrows).length > 0) {
func.throws = Object.values(this.currThrows);
}
this.object.addBodyNode(func);
}
runtimeMode(func, ast, body) {
var val = new GrammerValue('array', []);
this.renderGrammerValue(val, ast.runtimeBody);
if (ast.runtimeBody.tokenRange) {
this.resolveAnnotations(this.getFrontComments(ast.runtimeBody.tokenRange[1])).forEach(c => {
val.value.push(c);
});
}
// _runtime = {}
func.addBodyNode(new GrammerExpr(
new GrammerVar(this.config.runtime, runtimeType), Symbol.assign(), val
));
// _lastRequest = null;
func.addBodyNode(new GrammerExpr(
new GrammerVar('_lastRequest', requestType, 'var', false, true),
Symbol.assign(),
new GrammerValue('null', null, nullType)
));
// _lastException = null;
func.addBodyNode(new GrammerExpr(
new GrammerVar('_lastException', exceptionType, 'var', false, true),
Symbol.assign(),
new GrammerValue('null', null, nullType)
));
// _now = Date.now();
func.addBodyNode(new GrammerExpr(
new GrammerVar('_now', int32),
Symbol.assign(),
new GrammerValue('behavior', new BehaviorTimeNow(), int32)
));
// let _retryTimes = 0;
func.addBodyNode(new GrammerExpr(
new GrammerVar('_retryTimes', int32, 'var'),
Symbol.assign(),
new GrammerValue('number', 0, int32)
));
let whileOperation = new GrammerCondition('while');
whileOperation.addCondition(
new GrammerCall('method', [
{ type: 'object_static', name: '$Core' },
{ type: 'call_static', name: this.config.tea.core.allowRetry }
], [
new GrammerValue('call', new GrammerCall('key', [
{ type: 'object', name: this.config.runtime },
{ type: 'map', name: 'retry' }
], [], genericType)),
new GrammerValue('param', '_retryTimes', int32),
new GrammerValue('param', '_now', int32),
], new TypeBool())
);
let retryTimesIf = new GrammerCondition('if');
retryTimesIf.addCondition(
new GrammerExpr(
new GrammerVar('_retryTimes', int32),
Symbol.greater(),
new GrammerValue('number', 0, int32)
)
);
retryTimesIf.addBodyNode(
new GrammerExpr(
new GrammerVar('_backoffTime', int32),
Symbol.assign(),
new GrammerCall('method', [
{ type: 'object_static', name: '$Core' },
{ type: 'call_static', name: this.config.tea.core.getBackoffTime }
], [
new GrammerValue('call', new GrammerCall('key', [
{ type: 'object', name: this.config.runtime },
{ type: 'map', name: 'backoff' }
]), genericType),
new GrammerValue('param', '_retryTimes', int32),
], int32)
)
);
let backoffTimeIf = new GrammerCondition('if');
let backoffTimeValue = new GrammerValue('number', 0, int32);
backoffTimeValue.dataType = int32;
backoffTimeIf.addCondition(
new GrammerExpr(
new GrammerVar('_backoffTime', int32),
Symbol.greater(),
backoffTimeValue
)
);
backoffTimeIf.addBodyNode(
new GrammerCall('method', [
{ type: 'object_static', name: '$Core' },
{ type: 'call_static', name: this.config.tea.core.sleep }
], [
new GrammerValue('param', '_backoffTime', int32),
])
);
retryTimesIf.addBodyNode(backoffTimeIf);
whileOperation.addBodyNode(retryTimesIf);
let retryTimesValue = new GrammerValue('number', 1, int32);
retryTimesValue.dataType = int32;
whileOperation.addBodyNode(new GrammerExpr(
new GrammerVar('_retryTimes', int32),
Symbol.assign(),
new GrammerExpr(
new GrammerVar('_retryTimes', int32),
Symbol.plus(),
retryTimesValue
)
));
let requestTryCatch = new GrammerTryCatch();
let exceptionVar = new GrammerVar('error', exceptionType);
let exceptionParam = new GrammerValue('var', exceptionVar, exceptionType);
let catchException = new GrammerException(null, exceptionVar);
let tryCatch = new GrammerCatch([
new GrammerCondition('if', [
new GrammerCall('method', [
{ type: 'object_static', name: '$Core' },
{ type: 'call_static', name: this.config.tea.core.isRetryable }
], [exceptionVar])
], [
new GrammerExpr(
new GrammerVar('_lastException', exceptionType, 'var'),
Symbol.assign(),
exceptionVar,
new GrammerVar('error', retryableType)
),
new GrammerContinue(whileOperation.index)
]),
new GrammerThrows(null, [exceptionParam])
], catchException);
this.currThrows['$Error'] = errorType;
this.currThrows['$Exception'] = exceptionType;
this.requestBody(ast, body, requestTryCatch);
requestTryCatch.addCatch(tryCatch);
whileOperation.addBodyNode(requestTryCatch);
func.addBodyNode(whileOperation);
func.addBodyNode(
new GrammerThrows(
unretryableType, [
new GrammerValue('var', new GrammerVar('_lastRequest', requestType), requestType),
new GrammerValue('var', new GrammerVar('_lastException', exceptionType), exceptionType)
]
)
);
this.currThrows['$ExceptionUnretryable'] = unretryableType;
}
requestBody(ast, body, func) {
if (body) {
if (body.type === 'apiBody') {
// TeaRequest _request = new TeaRequest()
func.addBodyNode(
new GrammerExpr(
new GrammerVar(this.config.request, requestType),
Symbol.assign(),
new GrammerNewObject('$Request')
)
);
}
const stmt = ['declares', 'protocol', 'port', 'method', 'pathname', 'quert', 'headers', 'body', 'appendStmts'];
stmt.forEach(key => {
if (body[key] && body[key] !== 'undefined') {
if (Array.isArray(body[key])) {
body[key].forEach(stmtItem => {
this.visitStmt(func, stmtItem, func.index);
});
} else {
this.visitStmt(func, body[key], func.index);
}
}
});
if (body.type === 'apiBody') {
var doActionParams = [];
doActionParams.push(new GrammerValue('param', this.config.request, requestType));
if (ast.runtimeBody) {
doActionParams.push(new GrammerValue('param', this.config.runtime, runtimeType));
}
// response = Tea.doAction
const doActionBehavior = new BehaviorDoAction(
new GrammerVar(this.config.response, new TypeObject('$Response')), doActionParams
);
if (body.stmts) {
body.stmts.stmts.forEach(stmt => {
this.visitStmt(func, stmt, func.index);
});
}
// _lastRequest = request_;
func.addBodyNode(
new GrammerExpr(
new GrammerVar('_lastRequest', requestType, 'var'),
Symbol.assign(),
new GrammerVar(this.config.request, requestType)
)
);
// returns
if (ast.returns) {
if (ast.returns.stmts.stmts.length > 0) {
ast.returns.stmts.stmts.forEach(stmt => {
this.visitStmt(doActionBehavior, stmt, doActionBehavior.index);
});
} else {
this.findComments(doActionBehavior, ast.returns, 'between');
}
}
func.addBodyNode(doActionBehavior);
}
if (body.tokenRange) {
this.resolveAnnotations(this.getFrontComments(body.tokenRange[1], func.index)).forEach(c => {
func.addBodyNode(c);
});
}
}
}
renderGrammerValue(valGrammer, object, expectedType = null, canCast = false) {
if (!valGrammer) {
valGrammer = new GrammerValue();
}
if (!valGrammer.value && object.type === 'object') {
valGrammer.value = [];
}
if (object.type === 'variable') {
if (object.needCast) {
let grammerVar = new GrammerVar(object.id.lexeme, this.resolveTypeItem(object.inferred));
valGrammer.type = 'behavior';
grammerVar.belong = valGrammer.index;
const behaviorToMap = new BehaviorToMap(grammerVar, object.inferred);
behaviorToMap.belong = valGrammer.index;
valGrammer.value = behaviorToMap;
} else {
valGrammer.type = 'var';
let grammerVar;
if (object.id.type === 'model') {
const name = `#${object.id.lexeme}`;
const type = this.resolveTypeItem(object.inferred);
type.objectName = name;
grammerVar = new GrammerVar(object.id.lexeme, type);
grammerVar.varType = 'static_class';
grammerVar.name = name;
} else if (object.type === 'variable') {
grammerVar = new GrammerVar(object.id.lexeme, this.resolveTypeItem(object.inferred));
grammerVar.varType = 'var';
grammerVar.needCast = canCast;
grammerVar.expected = expectedType ? this.resolveTypeItem(expectedType) : null;
}
valGrammer.value = grammerVar;
}
} else if (object.type === 'object') {
object.fields.forEach(field => {
var val = null;
var type = field.expr.type;
if (field.expr.type === 'object') {
val = [];
type = 'map';
}
var exprChild = new GrammerValue(type, val);
if (field.type === 'expandField') {
exprChild.isExpand = true;
}
if (field.fieldName && (field.fieldName.lexeme || field.fieldName.string)) {
exprChild.key = field.fieldName.lexeme || _string(field.fieldName);
}
this.renderGrammerValue(exprChild, field.expr, expectedType, true);
this.findComments(valGrammer, field);
valGrammer.value.push(exprChild);
this.findComments(valGrammer, field.expr, 'back');
});
valGrammer.type = 'map';
valGrammer.dataType = this.resolveTypeItem(object.inferred);
valGrammer.expected = expectedType ? this.resolveTypeItem(expectedType) : null;
} else if (object.type === 'string') {
valGrammer.type = 'string';
valGrammer.value = object.value.string;
} else if (object.type === 'property_access') {
let last_path_type = this.resolveTypeItem(object.id.inferred || object.inferred);
let call = new GrammerCall('prop', [], [], last_path_type);
var model = false;
if (object.id.lexeme !== '__request' && object.id.lexeme !== '__response') {
model = canCast;
}
call.addPath({ type: 'object', name: object.id.lexeme, dataType: last_path_type, needCast: model });
object.propertyPath.forEach((item, i) => {
var path_name = item.lexeme;
let path_type = this.resolveTypeItem(object.propertyPathTypes[i]);
let call_type = 'prop';
if (is.array(last_path_type)) {
call_type = 'list';
} else if (is.map(last_path_type)) {
call_type = 'map';
} else if (is.object(last_path_type)) {
call_type = 'prop';
} else {
debug.stack(last_path_type);
}
var resolve = false;
if (i !== (object.propertyPath.length - 1) && object.id.lexeme !== '__request' && object.id.lexeme !== '__response') {
resolve = true;
}
call.addPath({ type: call_type, name: path_name, dataType: path_type, needCast: resolve });
last_path_type = path_type;
});
valGrammer.type = 'call';
valGrammer.value = call;
if (object.needCast) {
valGrammer.type = 'behavior';
valGrammer.value = new BehaviorToMap(valGrammer.value, object.inferred);
}
valGrammer.needCast = model;
} else if (object.type === 'number') {
valGrammer.type = 'number';
valGrammer.value = object.value.value;
valGrammer.dataType = this.resolveTypeItem(object.inferred);
} else if (object.type === 'virtualVariable') {
valGrammer.type = 'call';
let call = new GrammerCall('prop', [
{ type: 'parent', name: '' },
{ type: 'prop', name: object.vid.lexeme },
]);
// call.isOptional = true;
valGrammer.needCast = canCast;
valGrammer.value = call;
} else if (object.type === 'template_string') {
valGrammer.type = 'behavior';
let behaviorTamplateString = new BehaviorTamplateString();
object.elements.forEach(ele => {
if (ele.type !== 'element') {
behaviorTamplateString.addItem(this.renderGrammerValue(null, ele.expr, null, true));
} else {
behaviorTamplateString.addItem(new GrammerValue('string', ele.value.string, new TypeString()));
}
});
valGrammer.value = behaviorTamplateString;
} else if (object.type === 'null') {
valGrammer.type = 'null';
valGrammer.value = null;
} else if (object.type === 'construct_model') {
let objectName = object.aliasId.lexeme ? object.aliasId.lexeme : '';
if (object.propertyPath && object.propertyPath.length > 0) {
if (object.propertyPath.length > 0 && objectName !== '') {
objectName = objectName + '.';
}
object.propertyPath.forEach((p, i) => {
objectName += p.lexeme;
if (i !== object.propertyPath.length - 1) {
objectName += '.';
}
});
}
objectName = `#${objectName}`;
valGrammer.type = 'instance';
let params = [];
if (object.object) {
params = this.renderGrammerValue(null, object.object);
if (params.value.length === 0) {
this.findComments(params, object.object, 'between');
}
if (object.object.inferred.type === 'map') {
params.type = 'model_construct_params';
}
}
valGrammer.value = new GrammerNewObject(objectName, params);
} else if (object.type === 'call') {
let call_type = 'method';
valGrammer.type = 'call';
if (object.left && object.left.id && object.left.id.type === 'module') {
if (systemPackage.indexOf(object.left.id.lexeme) > -1) {
call_type = 'sys_func';
}
} else {
valGrammer.type = 'call';
call_type = 'method';
}
let call = new GrammerCall(call_type, undefined, undefined, undefined, object.hasThrow, object.isAsync, object.isStatic, object.isOptional);
if (object.left) {
let isStatic = object.isStatic ? true : false;
let callType = isStatic ? '_static' : '';
if (object.left.type === 'method_call') {
if (isStatic) {
call.addPath({ type: 'class', name: '' });
}
call.addPath({ type: 'call' + callType, name: object.left.id.lexeme });
} else if (object.left.type === 'instance_call') {
if (object.left && object.left.id && object.left.id.lexeme) {
if (object.left.id.type === 'variable') {
call.addPath({ type: 'object' + callType, name: object.left.id.lexeme });
} else if (typeof object.left.id.type === 'undefined' && object.left.id.lexeme.indexOf('@') > -1) {
call.addPath({ type: 'parent', name: '' });
call.addPath({ type: 'prop', name: object.left.id.lexeme, needCast: true });
} else {
debug.stack('Unsupported object.left.id.type : ' + object.left.id.type, object);
}
}
} else if (object.left.type === 'static_call') {
if (object.left.id.type === 'module') {
call.addPath({ type: 'object_static', name: `^${object.left.id.lexeme}` });
isStatic = true;
} else {
// call.addPath({ type: 'call_static', name: object.left.id.lexeme });
debug.stack(object);
}
} else {
debug.stack(object.left);
}
if (object.left.propertyPath) {
object.left.propertyPath.forEach(p => {
call.addPath({
type: 'call' + callType,
name: p.lexeme
});
});
}
}
if (object.args) {
object.args.forEach(arg => {
const grammerValue = this.renderGrammerValue(null, arg, null, true);
grammerValue.belong = call.index;
call.addParams(grammerValue);
});
}
call.returnType = this.resolveTypeItem(object.inferred);
valGrammer.value = call;
} else if (object.type === 'construct') {
valGrammer.type = 'instance';
const objectName = `^${object.aliasId.lexeme}`;
valGrammer.value = new GrammerNewObject(objectName);
object.args.forEach(item => {
valGrammer.value.addParam(this.renderGrammerValue(null, item));
});
} else if (object.type === 'boolean') {
valGrammer.type = 'bool';
valGrammer.value = object.value;
} else if (object.type === 'not') {
valGrammer.type = 'not';
valGrammer.value = this.renderGrammerValue(null, object.expr);
} else if (object.type === 'array') {
valGrammer.type = 'array';
valGrammer.value = [];
if (object.items.length > 0) {
object.items.forEach(field => {
this.findComments(valGrammer, field);
var exprChild = this.renderGrammerValue(null, field, expectedType);
valGrammer.value.push(exprChild);
this.findComments(valGrammer, field, 'back');
});
} else {
this.findComments(valGrammer, object, 'between');
}
valGrammer.type = 'array';
valGrammer.expected = expectedType ? this.resolveTypeItem(expectedType) : null;
} else if (object.type === 'property') {
object.type = 'property_access';
this.renderGrammerValue(valGrammer, object, null, canCast);
} else if (object.type === 'super') {
valGrammer.type = 'call';
let call = new GrammerCall('super', undefined, undefined, undefined, object.hasThrow, object.isAsync);
object.args.forEach(arg => {
call.addParams(this.renderGrammerValue(null, arg));
});
valGrammer.value = call;
} else if (object.type === 'map_access') {
valGrammer.type = 'call';
let accessKey;
if (object.accessKey.inferred) {
accessKey = this.renderGrammerValue(null, object.accessKey, null, true);
} else if (object.accessKey.type === 'variable') {
accessKey = object.accessKey.id.lexeme;
} else if (object.accessKey.type === 'string') {
accessKey = object.accessKey.value.string;
} else if (object.accessKey.value && object.accessKey.value.lexeme) {
accessKey = object.accessKey.value.lexeme;
} else {
debug.stack(object);
}
let current = object.id.inferred;
let call = new GrammerCall('key');
call.addPath({ type: 'object', name: object.id.lexeme });
if (object.propertyPathTypes) {
for (let i = 0; i < object.propertyPath.length; i++) {
if (current.type === 'model') {
call.type = 'prop';
call.addPath({ type: 'prop', name: object.propertyPath[i].lexeme, needCast: canCast });
} else if (current.type === 'array') {
call.addPath({ type: 'list', name: object.propertyPath[i].lexeme, needCast: canCast });
} else {
call.addPath({ type: 'map', name: object.propertyPath[i].lexeme, needCast: canCast });
}
current = object.propertyPathTypes[i];
}
}
call.addPath({ type: 'map', name: accessKey });
if (object.inferred) {
let inferred = object.inferred;
if (object.propertyPathTypes && object.propertyPathTypes.length) {
if (object.propertyPathTypes[object.propertyPathTypes.length - 1].type === 'map') {
inferred = object.propertyPathTypes[object.propertyPathTypes.length - 1].keyType;
}
}
call.returnType = this.resolveTypeItem(inferred);
}
valGrammer.value = call;
valGrammer.needCast = canCast;
} else if (object.type === 'array_access') {
valGrammer.type = 'call';
let accessKey;
if (object.accessKey.inferred) {
accessKey = this.renderGrammerValue(null, object.accessKey);
} else if (object.accessKey.type === 'number') {
accessKey = object.accessKey.value.value;
} else if (object.accessKey.type === 'variable') {
accessKey = object.accessKey.id.lexeme;
} else {
debug.stack(object);
}
let current = object.id.inferred;
let call = new GrammerCall('key');
call.addPath({ type: 'object', name: object.id.lexeme });
if (object.propertyPathTypes) {
for (let i = 0; i < object.propertyPath.length; i++) {
if (current.type === 'model') {
call.type = 'prop';
call.addPath({ type: 'prop', name: object.propertyPath[i].lexeme });
} else if (current.type === 'array') {
call.addPath({ type: 'list', name: object.propertyPath[i].lexeme });
} else {
call.addPath({ type: 'map', name: object.propertyPath[i].lexeme });
}
current = object.propertyPathTypes[i];
}
}
call.addPath({ type: 'list', name: accessKey, isVar: object.accessKey.type === 'variable' });
if (object.inferred) {
call.returnType = this.resolveTypeItem(object.inferred);
}
valGrammer.value = call;
} else if (['and', 'or', 'not'].indexOf(object.type) > -1) {
valGrammer.type = 'expr';
valGrammer.value = this.visitIfConfition(object);
} else {
debug.stack('unimpelemented : ' + object.type, object);
}
if (object.inferred) {
valGrammer.dataType = this.resolveTypeItem(object.inferred);
}
if (object.needToReadable) {
valGrammer.needToReadable = true;
}
if (!valGrammer.dataType) {
debug.stack('Invalid GrammerValue.dataType', valGrammer, object);
}
return valGrammer;
}
visitStmt(obj, stmt, belong) {
let node;
let renderByGrammerValueTypes = [
'construct_model',
'property_access',
'map_access',
'boolean',
'super',
'not',
];
if (renderByGrammerValueTypes.indexOf(stmt.type) > -1) {
node = this.renderGrammerValue(null, stmt);
} else if (stmt.type === 'declare') {
let type = null;
if (stmt.expr && stmt.expr.inferred) {
let inferred = stmt.expr.inferred;
if (stmt.expr.propertyPathTypes && stmt.expr.propertyPathTypes.length) {
if (stmt.expr.propertyPathTypes[stmt.expr.propertyPathTypes.length - 1].type === 'map') {
inferred = stmt.expr.propertyPathTypes[stmt.expr.propertyPathTypes.length - 1].keyType;
}
}
type = this.resolveTypeItem(inferred);
} else if (stmt.expectedType) {
type = this.resolveTypeItem(stmt.expectedType);
} else {
debug.stack(stmt);
}
let expectedType = stmt.expectedType ? stmt.expectedType : null;
assert.strictEqual(true, type instanceof TypeItem);
let variate = new GrammerVar(stmt.id.lexeme, type);
if (stmt.expr.type === 'null') {
variate.isOptional = true;
}
let value = this.renderGrammerValue(null, stmt.expr, expectedType, true);
node = new GrammerExpr(variate, Symbol.assign(), value);
} else if (stmt.type === 'requestAssign') {
let variate = new GrammerCall('prop', [
{ type: 'object', name: this.config.request },
{ type: 'prop', name: stmt.left.id.lexeme }
]);
let value = this.renderGrammerValue(null, stmt.expr);
if (stmt.left.type === 'request_property_assign') {
let key = '';
stmt.left.propertyPath.forEach((p, i) => {
if (i === stmt.left.propertyPath.length - 1) {
key = p.lexeme;
} else {
variate.addPath({ type: 'map', name: p.lexeme });
}
});
node = new BehaviorSetMapItem(variate, key, value);
} else {
node = new GrammerExpr(variate, Symbol.assign(), value);
}
} else if (stmt.type === 'ifRequestAssign' || stmt.type === 'if') {
node = this.visitIfElse(stmt, 'if');
} else if (stmt.type === 'elseIfRequestAssign') {
node = this.visitIfElse(stmt, 'elseif');
} else if (stmt.type === 'return') {
node = new GrammerReturn();
if (typeof stmt.expr === 'undefined') {
node.type = 'null';
} else if (stmt.expr.type === 'null') {
node.type = 'null';
} else if (stmt.expr.type === 'call') {
let val = this.renderGrammerValue(null, stmt.expr, null, true);
node.expr = val;
node.type = 'grammer';
} else if (_isBasicType(stmt.expr.type)) {
node.type = stmt.expr.type;
node.expr = this.renderGrammerValue(null, stmt.expr);
} else {
node.expr = this.renderGrammerValue(null, stmt.expr, null, true);
node.type = 'grammer';
}
if (stmt.expectedType && stmt.expectedType.type === 'model') {
let expected = '';
if (stmt.expectedType.moduleName) {
expected += `${stmt.expectedType.moduleName}.`;
}
expected += stmt.expectedType.name;
node.expr = new BehaviorToModel(node.expr, expected);
}
} else if (stmt.type === 'throw') {
this.currThrows['$Error'] = errorType;
node = new GrammerThrows(errorType);
if (Array.isArray(stmt.expr)) {
stmt.expr.forEach(e => {
node.addParam(this.renderGrammerValue(null, e));
});
} else {
node.addParam(this.renderGrammerValue(null, stmt.expr));
}
} else if (stmt.type === 'virtualCall') {
let val = this.renderGrammerValue(null, stmt);
node = val.value;
} else if (stmt.type === 'while') {
node = new GrammerCondition('while');
if (Array.isArray(stmt.condition)) {
stmt.condition.forEach(c => {
node.addCondition(this.renderGrammerValue(null, c));
});
} else {
node.addCondition(this.renderGrammerValue(null, stmt.condition));
}
if (stmt.stmts) {
stmt.stmts.stmts.forEach(s => {
this.visitStmt(node, s, node.index);
});
}
} else if (stmt.type === 'for') {
node = new GrammerLoop('foreach');
node.item = new GrammerVar(stmt.id.lexeme, this.resolveTypeItem(stmt.list.inferred.itemType));
node.source = this.renderGrammerValue(null, stmt.list);
if (stmt.stmts) {
stmt.stmts.stmts.forEach(s => {
this.visitStmt(node, s, node.index);
});
}
} else if (stmt.type === 'assign') {
let hasMapAccess = false;
let expectedType = stmt.expectedType ? stmt.expectedType : stmt.left.inferred ? stmt.left.inferred.name || stmt.left.inferred : null;
let needCast = true;
if (stmt.left.type === 'virtualVariable' || stmt.left.type === 'property') {
needCast = false;
if (stmt.left.id && (stmt.left.id.lexeme === '__request' || stmt.left.id.lexeme === '__response')) {
needCast = true;
}
}
const right = this.renderGrammerValue(null, stmt.expr, expectedType, needCast);
if (stmt.left.type === 'property') {
hasMapAccess = stmt.left.propertyPathTypes.some(item => item.type === 'map');
if (hasMapAccess) {
const grammerValue = this.renderGrammerValue(null, stmt.left);
const call = grammerValue.value;
let lastIndex = 0;
for (let i = 0; i < stmt.left.propertyPathTypes.length; i++) {
const propertyPathType = stmt.left.propertyPathTypes[i];
if (propertyPathType.type === 'map') {
lastIndex = i;
break;
}
}
if (lastIndex + 1 < stmt.left.propertyPathTypes.length) {
call.type = 'prop';
call.path = call.path.splice(0, lastIndex + 2);
const key = stmt.left.propertyPath[lastIndex + 1].lexeme;
node = new BehaviorSetMapItem(call, key, right);
} else {
hasMapAccess = false;
}
}
} else if (stmt.left.type === 'map_access') {
hasMapAccess = true;
const grammerValue = this.renderGrammerValue(null, stmt.left);
const call = grammerValue.value;
call.type = 'prop';
call.path = call.path.splice(0, call.path.length - 1);
node = new BehaviorSetMapItem(call, stmt.left.accessKey.value.string, right);
}
if (!hasMapAccess) {
const left = this.renderGrammerValue(null, stmt.left);
let isBehavior = false;
if (left.type === 'call' && left.value instanceof GrammerCall && left.value.type === 'key') {
const keyPath = left.value.path[left.value.path.length - 1];
if (keyPath.type === 'map') {
left.value.path = left.value.path.slice(0, left.value.path.length - 1);
node = new BehaviorSetMapItem(left.value, keyPath.name, right);
isBehavior = true;
}
}
if (!isBehavior) {
node = new GrammerExpr(
left,
Symbol.assign(),
right
);
}
}
} else if (stmt.type === 'call') {
node = this.renderGrammerValue(null, stmt).value;
} else if (stmt.type === 'break') {
node = new GrammerBreak();
} else if (stmt.type === 'retry') {
node = new BehaviorRetry();
} else if (stmt.type === 'try') {
const tryGram = new GrammerTryCatch();
stmt.tryBlock.stmts.forEach(item => {
this.visitStmt(tryGram, item, tryGram.index);
});
if (stmt.catchId) {
const exceptionGram = new GrammerException(
exceptionType,
new GrammerVar(stmt.catchId.lexeme, exceptionType)
);
const catchGram = new GrammerCatch([], exceptionGram);
if (stmt.catchBlock) {
stmt.catchBlock.stmts.forEach(item => {
this.visitStmt(catchGram, item, catchGram.index);
});
}
tryGram.addCatch(catchGram);
}
if (stmt.finallyBlock) {
const finallyGram = new GrammerFinally();
stmt.finallyBlock.stmts.forEach(item => {
this.visitStmt(finallyGram, item, finallyGram.index);
});
tryGram.setFinally(finallyGram);
}
node = tryGram;
} else {
debug.stack(stmt);
}
if (belong) {
node.belong = belong;
}
this.findComments(obj, stmt, belong);
obj.addBodyNode(node);
this.findComments(obj, stmt, belong, 'back');
}
visitIfConfition(stmtCondition) {
let condition;
if (stmtCondition.left) {
let opt;
let optList = ['and', 'or', 'not'];
if (optList.indexOf(stmtCondition.type) > -1) {
switch (stmtCondition.type) {
case 'and': opt = Symbol.and(); break;
case 'or': opt = Symbol.or(); break;
case 'not': opt = Symbol.not(); break;
}
condition = new GrammerExpr(
this.renderGrammerValue(null, stmtCondition.left),
opt,
this.renderGrammerValue(null, stmtCondition.right)
);
} else if (stmtCondition.type === 'call') {
condition = this.renderGrammerValue(null, stmtCondition);
} else {
debug.stack(stmtCondition);
}
} else {
condition = this.renderGrammerValue(null, stmtCondition);
}
return condition;
}
visitIfElse(stmt, type = 'if') {
let node = new GrammerCondition(type);
if (stmt.condition) {
node.addCondition(this.visitIfConfition(stmt.condition));
}
if (stmt.stmts) {
if (stmt.stmts.type && stmt.stmts.type === 'stmts') {
if (stmt.stmts.stmts.length > 0) {
stmt.stmts.stmts.forEach(item => {
this.visitStmt(node, item, node.index);
});
} else {
this.findComments(node, stmt.stmts, 'between');
}
} else if (stmt.type && stmt.type === 'stmts') {
if (stmt.stmts.length > 0) {
stmt.stmts.forEach(item => {
this.visitStmt(node, item, node.index);
});
} else {
this.findComments(node, stmt, 'between');
}
} else {
debug.stack(stmt);
}
} else {
debug.stack(stmt);
}
if (stmt.elseIfs) {
stmt.elseIfs.forEach(elseIf => {
node.addElse(this.visitIfElse(elseIf, 'elseif'));
});
}
if (stmt.elseStmts) {
node.addElse(this.visitIfElse(stmt.elseStmts, 'else'));
}
if (stmt.elseAssigns) {
stmt.elseAssigns.forEach(elseAssign => {
node.elseItem.push(this.visitIfElse(elseAssign, 'else'));
});
}
return node;
}
}
module.exports = ClientResolver;