in lib/semantic.js [2080:2203]
visitExpr(ast, env) {
if (ast.type === 'string') {
// noop();
} else if (ast.type === 'number') {
// noop();
} else if (ast.type === 'boolean') {
// noop();
} else if (ast.type === 'null') {
// noop();
} else if (ast.type === 'group') {
this.visitExpr(ast.expr, env);
} else if (ast.type === 'property_access') {
this.checkProperty(ast, env);
} else if (ast.type === 'object') {
this.visitObject(ast, env);
} else if (ast.type === 'variable') {
this.checkId(ast.id, env);
} else if (ast.type === 'virtualVariable') {
this.checkVid(ast.vid, env);
} else if (ast.type === 'template_string') {
for (var i = 0; i < ast.elements.length; i++) {
var item = ast.elements[i];
if (item.type === 'expr') {
this.visitExpr(item.expr, env);
}
}
} else if (ast.type === 'call') {
this.visitCall(ast, env);
} else if (ast.type === 'construct') {
this.visitConstruct(ast, env);
} else if (ast.type === 'construct_model') {
this.visitConstructModel(ast, env);
} else if (ast.type === 'array') {
this.visitArray(ast, env);
} else if (ast.type === 'and' || ast.type === 'or') {
this.visitExpr(ast.left, env);
// the expr type should be boolean
if (!this.isBooleanType(ast.left, env)) {
this.error(`the left expr must be boolean type`, ast.left);
}
this.visitExpr(ast.right, env);
// the expr type should be boolean
if (!this.isBooleanType(ast.right, env)) {
this.error(`the right expr must be boolean type`, ast.right);
}
} else if (ast.type === 'not') {
this.visitExpr(ast.expr, env);
if (!this.isBooleanType(ast.expr, env)) {
this.error(`the expr after ! must be boolean type`, ast.expr);
}
} else if (ast.type === 'increment' || ast.type === 'decrement') {
this.visitExpr(ast.expr, env);
if(!this.isNumberType(ast.expr, env)) {
this.error(`the increment/decrement expr must be number type`, ast.expr);
}
} else if (isCompare(ast.type)) {
this.visitExpr(ast.left, env);
const leftType = this.getExprType(ast.left, env);
this.visitExpr(ast.right, env);
const rightType = this.getExprType(ast.right, env);
if(ast.type !== 'eq' && ast.type !== 'neq' &&
(!(leftType.type === 'basic' && isNumber(leftType.name)) ||
!(rightType.type === 'basic' && isNumber(rightType.name)))) {
this.error(`${ast.type} can only operate number type, ` +
`but left: ${display(leftType)} and right: ${display(rightType)}`, ast);
}
if (!eql([leftType], [rightType])) {
this.error(`${display(leftType)} can only compare with ${display(leftType)} type, ` +
`but ${display(rightType)}`, ast);
}
} else if (ast.type === 'add') {
this.visitExpr(ast.left, env);
const leftType = this.getExprType(ast.left, env);
this.visitExpr(ast.right, env);
const rightType = this.getExprType(ast.right, env);
if (!eql([leftType], [rightType])) {
this.error(`${display(leftType)} can only add with ${display(leftType)} type, ` +
`but ${display(rightType)}`, ast);
}
} else if (ast.type === 'subtract' || ast.type === 'div' || ast.type === 'multi') {
this.visitExpr(ast.left, env);
const leftType = this.getExprType(ast.left, env);
this.visitExpr(ast.right, env);
const rightType = this.getExprType(ast.right, env);
if(!(leftType.type === 'basic' && isNumber(leftType.name)) ||
!(rightType.type === 'basic' && isNumber(rightType.name))) {
this.error(`${ast.type} can only operate number type, ` +
`but left: ${display(leftType)} and right: ${display(rightType)}`, ast);
}
if (!eql([leftType], [rightType])) {
this.error(`${display(leftType)} can only ${ast.type} with ${display(leftType)} type, ` +
`but ${display(rightType)}`, ast);
}
} else if (ast.type === 'super') {
this.visitSuperCall(ast, env);
} else if (ast.type === 'map_access') {
let mainType;
if (ast.propertyPath) {
this.checkProperty(ast, env);
mainType = this.calculatePropertyType(ast, env);
} else {
this.checkId(ast.id, env);
mainType = this.getVariableType(ast.id, env);
}
this.visitExpr(ast.accessKey, env);
if (mainType.type === 'map') {
if (!this.isStringType(ast.accessKey, env)) {
this.error(`The key expr type must be string type`, ast.accessKey);
}
} else if (mainType.type === 'array') {
ast.type = 'array_access';
if (!this.isNumberType(ast.accessKey, env)) {
this.error(`The key expr type must be number type`, ast.accessKey);
}
} else {
this.error(`the [] form only support map or array type`, ast.accessKey);
}
} else {
throw new Error('unimplemented.');
}
ast.inferred = this.getExprType(ast, env);
}