@abaplint/core
Version:
abaplint - Core API
364 lines • 19.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Source = void 0;
const nodes_1 = require("../../nodes");
const Expressions = require("../../2_statements/expressions");
const method_call_chain_1 = require("./method_call_chain");
const unknown_type_1 = require("../../types/basic/unknown_type");
const field_chain_1 = require("./field_chain");
const basic_1 = require("../../types/basic");
const constant_1 = require("./constant");
const basic_types_1 = require("../basic_types");
const component_chain_1 = require("./component_chain");
const string_template_1 = require("./string_template");
const value_body_1 = require("./value_body");
const cond_1 = require("./cond");
const reduce_body_1 = require("./reduce_body");
const _reference_1 = require("../_reference");
const switch_body_1 = require("./switch_body");
const cond_body_1 = require("./cond_body");
const conv_body_1 = require("./conv_body");
const filter_body_1 = require("./filter_body");
const corresponding_body_1 = require("./corresponding_body");
const _builtin_1 = require("../_builtin");
const attribute_chain_1 = require("./attribute_chain");
const dereference_1 = require("./dereference");
const _typed_identifier_1 = require("../../types/_typed_identifier");
const _type_utils_1 = require("../_type_utils");
const _syntax_input_1 = require("../_syntax_input");
const assert_error_1 = require("../assert_error");
/*
* Type interference, valid scenarios:
* typed = VALUE #( ... ). right hand side must follow left hand type
* DATA(bar) = VALUE type( ... ). left gets the type of rigthand
* typed = VALUE type( ... ). types must match and be compatible???
************* ERRORS *********
* VALUE #( ... ). syntax error
* DATA(bar) = VALUE #( ... ). give error, no type can be derived
*/
// TODO: refactor all these method parameters to objects, this is getting messy
class Source {
static runSyntax(node, input, targetType, writeReference = false, allowGenericDeference = false) {
if (node === undefined) {
return undefined;
}
const children = node.getChildren().slice();
let first = children.shift();
if (first instanceof nodes_1.TokenNode) {
const token = first.getFirstToken();
const tok = token.getStr().toUpperCase();
switch (tok) {
case "(":
case "-":
case "+":
case "BIT":
break;
case "BOOLC":
{
const method = _builtin_1.BuiltIn.searchBuiltin(tok);
input.scope.addReference(token, method, _reference_1.ReferenceType.BuiltinMethodReference, input.filename);
cond_1.Cond.runSyntax(node.findDirectExpression(Expressions.Cond), input);
return basic_1.StringType.get();
}
case "XSDBOOL":
{
const method = _builtin_1.BuiltIn.searchBuiltin(tok);
input.scope.addReference(token, method, _reference_1.ReferenceType.BuiltinMethodReference, input.filename);
cond_1.Cond.runSyntax(node.findDirectExpression(Expressions.Cond), input);
return new basic_1.CharacterType(1, { qualifiedName: "ABAP_BOOL", ddicName: "ABAP_BOOL" });
}
case "REDUCE":
{
const foundType = this.determineType(node, input, targetType);
const bodyType = reduce_body_1.ReduceBody.runSyntax(node.findDirectExpression(Expressions.ReduceBody), input, foundType);
if (foundType === undefined || foundType.isGeneric()) {
this.addIfInferred(node, input, bodyType);
}
else {
this.addIfInferred(node, input, foundType);
}
return foundType ? foundType : bodyType;
}
case "SWITCH":
{
const foundType = this.determineType(node, input, targetType);
const bodyType = switch_body_1.SwitchBody.runSyntax(node.findDirectExpression(Expressions.SwitchBody), input);
if (foundType === undefined || foundType.isGeneric()) {
this.addIfInferred(node, input, bodyType);
}
else {
this.addIfInferred(node, input, foundType);
}
return foundType ? foundType : bodyType;
}
case "COND":
{
const foundType = this.determineType(node, input, targetType);
const bodyType = cond_body_1.CondBody.runSyntax(node.findDirectExpression(Expressions.CondBody), input, foundType);
/*
console.log("COND BODY type;:");
console.dir(bodyType);
*/
if (foundType === undefined || foundType.isGeneric()) {
this.addIfInferred(node, input, bodyType);
}
else {
this.addIfInferred(node, input, foundType);
}
children.shift();
children.shift();
children.shift();
children.shift();
this.traverseRemainingChildren(children, input);
return foundType ? foundType : bodyType;
}
case "CONV":
{
const foundType = this.determineType(node, input, targetType);
const bodyType = conv_body_1.ConvBody.runSyntax(node.findDirectExpression(Expressions.ConvBody), input);
if (new _type_utils_1.TypeUtils(input.scope).isConvable(foundType, bodyType) === false) {
const message = `CONV: Types not compatible, ${foundType === null || foundType === void 0 ? void 0 : foundType.constructor.name}, ${bodyType === null || bodyType === void 0 ? void 0 : bodyType.constructor.name}`;
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
}
this.addIfInferred(node, input, foundType);
return foundType;
}
case "REF":
{
let foundType = this.determineType(node, input, targetType);
const s = Source.runSyntax(node.findDirectExpression(Expressions.Source), input);
/*
console.dir(node.concatTokens());
console.dir(targetType);
console.dir(foundType);
console.dir(s);
*/
if (foundType && foundType.isGeneric() && s) {
foundType = new basic_1.DataReference(s);
}
else if (foundType === undefined && s) {
foundType = new basic_1.DataReference(s);
}
else if (foundType && targetType === undefined) {
foundType = new basic_1.DataReference(foundType);
}
/*
if (targetType && !(targetType instanceof DataReference)) {
const message = `REF: Types not compatible, ` + targetType.constructor.name;
input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
}
*/
this.addIfInferred(node, input, foundType);
return foundType;
}
case "FILTER":
{
const foundType = this.determineType(node, input, targetType);
const bodyType = filter_body_1.FilterBody.runSyntax(node.findDirectExpression(Expressions.FilterBody), input, foundType);
if (foundType === undefined || foundType.isGeneric()) {
this.addIfInferred(node, input, bodyType);
}
else {
this.addIfInferred(node, input, foundType);
}
if (foundType && !(foundType instanceof unknown_type_1.UnknownType)) {
return foundType;
}
else {
return bodyType;
}
}
case "CORRESPONDING":
{
const foundType = this.determineType(node, input, targetType);
corresponding_body_1.CorrespondingBody.runSyntax(node.findDirectExpression(Expressions.CorrespondingBody), input, foundType);
this.addIfInferred(node, input, foundType);
return foundType;
}
case "EXACT":
{
const foundType = this.determineType(node, input, targetType);
Source.runSyntax(node.findDirectExpression(Expressions.Source), input, foundType);
this.addIfInferred(node, input, foundType);
return foundType;
}
case "VALUE":
{
const foundType = this.determineType(node, input, targetType);
const bodyType = value_body_1.ValueBody.runSyntax(node.findDirectExpression(Expressions.ValueBody), input, foundType);
if (foundType === undefined || foundType.isGeneric()) {
this.addIfInferred(node, input, bodyType);
}
else {
this.addIfInferred(node, input, foundType);
}
return foundType ? foundType : bodyType;
}
default:
return new unknown_type_1.UnknownType("todo, Source type " + tok);
}
}
else if (first === undefined || !(first instanceof nodes_1.ExpressionNode)) {
return undefined;
}
let context = new unknown_type_1.UnknownType("todo, Source type");
const type = [_reference_1.ReferenceType.DataReadReference];
if (writeReference) {
type.push(_reference_1.ReferenceType.DataWriteReference);
}
let hexExpected = false;
let hexNext = false;
while (children.length >= 0) {
if (first instanceof nodes_1.ExpressionNode) {
const get = first.get();
if (get instanceof Expressions.MethodCallChain) {
context = method_call_chain_1.MethodCallChain.runSyntax(first, input, targetType);
if (context === undefined) {
const message = "Method has no RETURNING value";
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
}
}
else if (get instanceof Expressions.FieldChain) {
context = field_chain_1.FieldChain.runSyntax(first, input, type, allowGenericDeference);
}
else if (get instanceof Expressions.StringTemplate) {
context = string_template_1.StringTemplate.runSyntax(first, input);
}
else if (get instanceof Expressions.Source) {
const found = Source.runSyntax(first, input);
context = this.infer(context, found);
}
else if (get instanceof Expressions.Constant) {
const found = constant_1.Constant.runSyntax(first);
context = this.infer(context, found);
}
else if (get instanceof Expressions.Dereference) {
context = dereference_1.Dereference.runSyntax(first, context, input);
}
else if (get instanceof Expressions.ComponentChain) {
context = component_chain_1.ComponentChain.runSyntax(context, first, input);
}
else if (get instanceof Expressions.ArithOperator) {
if (first.concatTokens() === "**") {
context = new basic_1.FloatType();
}
const operator = first.concatTokens().toUpperCase();
if (operator === "BIT-OR" || operator === "BIT-AND" || operator === "BIT-XOR") {
hexExpected = true;
hexNext = true;
}
}
else if (get instanceof Expressions.AttributeChain) {
context = attribute_chain_1.AttributeChain.runSyntax(context, first, input, type);
}
}
if (hexExpected === true) {
if (!(context instanceof basic_1.VoidType)
&& !(context instanceof basic_1.XStringType)
&& !(context instanceof basic_1.HexType)
&& !(context instanceof basic_1.XGenericType)
&& !(context instanceof basic_1.XSequenceType)
&& !(context instanceof unknown_type_1.UnknownType)) {
const message = "Operator only valid for XSTRING or HEX";
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
}
if (hexNext === false) {
hexExpected = false;
}
hexNext = false;
}
first = children.shift();
if (first === undefined) {
break;
}
}
if (node.findDirectTokenByText("&&")) {
return basic_1.StringType.get();
}
return context;
}
////////////////////////////////
static traverseRemainingChildren(children, input) {
const last = children[children.length - 1];
if (last && last.get() instanceof Expressions.Source) {
Source.runSyntax(last, input);
}
}
static infer(context, found) {
if (context instanceof basic_1.FloatType && found instanceof basic_1.IntegerType) {
return context;
}
else {
return found;
}
}
static addIfInferred(node, input, inferredType) {
const basic = new basic_types_1.BasicTypes(input);
const typeExpression = node.findDirectExpression(Expressions.TypeNameOrInfer);
const typeToken = typeExpression === null || typeExpression === void 0 ? void 0 : typeExpression.getFirstToken();
// const typeName = typeToken?.getStr();
/*
console.dir(inferredType);
console.dir(typeToken);
*/
if (inferredType && typeToken) {
const found = basic.lookupQualifiedName(inferredType.getQualifiedName());
if (found) {
const tid = new _typed_identifier_1.TypedIdentifier(typeToken, input.filename, inferredType);
input.scope.addReference(typeToken, tid, _reference_1.ReferenceType.InferredType, input.filename, { foundQualified: true });
}
else if (inferredType instanceof basic_1.ObjectReferenceType) {
const def = input.scope.findObjectDefinition(inferredType.getQualifiedName());
const tid = new _typed_identifier_1.TypedIdentifier(typeToken, input.filename, inferredType);
if (def) {
input.scope.addReference(typeToken, tid, _reference_1.ReferenceType.InferredType, input.filename, { foundQualified: true });
}
else {
input.scope.addReference(typeToken, tid, _reference_1.ReferenceType.InferredType, input.filename, { foundQualified: false });
}
}
else {
const tid = new _typed_identifier_1.TypedIdentifier(typeToken, input.filename, inferredType);
input.scope.addReference(typeToken, tid, _reference_1.ReferenceType.InferredType, input.filename, { foundQualified: false });
}
}
}
static determineType(node, input, targetType) {
const basic = new basic_types_1.BasicTypes(input);
const typeExpression = node.findDirectExpression(Expressions.TypeNameOrInfer);
const typeToken = typeExpression === null || typeExpression === void 0 ? void 0 : typeExpression.getFirstToken();
const typeName = typeToken === null || typeToken === void 0 ? void 0 : typeToken.getStr();
if (typeExpression === undefined) {
throw new assert_error_1.AssertError("determineType, child TypeNameOrInfer not found");
}
else if (typeName === "#" && targetType) {
return targetType;
}
if (typeName !== "#" && typeToken) {
const found = basic.parseType(typeExpression);
if (found && found instanceof unknown_type_1.UnknownType) {
if (input.scope.getDDIC().inErrorNamespace(typeName) === false) {
input.scope.addReference(typeToken, undefined, _reference_1.ReferenceType.VoidType, input.filename);
return basic_1.VoidType.get(typeName);
}
else {
const tid = new _typed_identifier_1.TypedIdentifier(typeToken, input.filename, found);
input.scope.addReference(typeToken, tid, _reference_1.ReferenceType.TypeReference, input.filename);
return found;
}
}
else if (found === undefined) {
const message = "Type \"" + typeName + "\" not found in scope, VALUE";
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
}
return found;
}
return targetType;
}
}
exports.Source = Source;
//# sourceMappingURL=source.js.map