All files / src/abap/5_syntax/expressions source.ts

94.92% Statements 187/197
83.08% Branches 54/65
100% Functions 2/2
94.92% Lines 187/197

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 1971x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 919x 919x 919x 919x 919x 919x 2x 2x 917x 917x 917x 917x 919x 53x 53x 53x 53x 53x 1x 53x 3x 3x 3x 3x 3x 3x 53x 2x 2x 2x 2x 2x 2x 53x 4x 4x 4x 4x 4x 53x 2x 2x 2x 2x 2x 53x 4x 4x 4x 4x 53x 4x 4x 4x 4x 4x 53x 2x 2x 2x 2x 2x 2x     2x 53x 2x 2x 2x 2x 53x 2x 2x 2x 2x 53x 1x 53x 26x 26x 26x 26x 53x   53x 919x     865x 865x 865x 919x 906x 82x 906x 510x 824x 30x 314x 16x 284x 240x 268x 2x 28x   26x 1x 26x 3x 3x 835x 906x 794x 794x 906x 794x 794x 794x 794x 1x 1x 1x 1x 47x 47x 47x 47x 47x 47x 47x 47x 47x 47x 47x 47x   47x 15x 15x 8x 8x 15x 15x 32x 47x 25x 25x   25x     25x 25x 7x 7x 7x 1x 1x
import {ExpressionNode, TokenNode} from "../../nodes";
import {CurrentScope} from "../_current_scope";
import {AbstractType} from "../../types/basic/_abstract_type";
import * as Expressions from "../../2_statements/expressions";
import {MethodCallChain} from "./method_call_chain";
import {UnknownType} from "../../types/basic/unknown_type";
import {FieldChain} from "./field_chain";
import {VoidType, StringType, CharacterType, DataReference} from "../../types/basic";
import {Constant} from "./constant";
import {BasicTypes} from "../basic_types";
import {ComponentChain} from "./component_chain";
import {StringTemplate} from "./string_template";
import {ValueBody} from "./value_body";
import {Cond} from "./cond";
import {ReduceBody} from "./reduce_body";
import {ReferenceType} from "../_reference";
import {SwitchBody} from "./switch_body";
import {CondBody} from "./cond_body";
import {ConvBody} from "./conv_body";
import {FilterBody} from "./filter_body";
import {CorrespondingBody} from "./corresponding_body";
import {BuiltIn} from "../_builtin";
import {AttributeChain} from "./attribute_chain";
import {Dereference} from "./dereference";
 
/*
* 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
*/
 
export class Source {
  public runSyntax(
    node: ExpressionNode | undefined,
    scope: CurrentScope,
    filename: string,
    targetType?: AbstractType): AbstractType | undefined {
 
    if (node === undefined) {
      return undefined;
    }
 
    const children = node.getChildren().slice();
    let first = children.shift();
 
    if (first instanceof TokenNode) {
      const token = first.getFirstToken();
      const tok = token.getStr().toUpperCase();
      switch (tok) {
        case "(":
        case "-":
          break;
        case "BOOLC":
        {
          const method = new BuiltIn().searchBuiltin(tok);
          scope.addReference(token, method, ReferenceType.BuiltinMethodReference, filename);
          new Cond().runSyntax(node.findDirectExpression(Expressions.Cond), scope, filename);
          return new StringType();
        }
        case "XSDBOOL":
        {
          const method = new BuiltIn().searchBuiltin(tok);
          scope.addReference(token, method, ReferenceType.BuiltinMethodReference, filename);
          new Cond().runSyntax(node.findDirectExpression(Expressions.Cond), scope, filename);
          return new CharacterType(1);
        }
        case "REDUCE":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          const bodyType = new ReduceBody().runSyntax(node.findDirectExpression(Expressions.ReduceBody), scope, filename);
          return foundType ? foundType : bodyType;
        }
        case "SWITCH":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          new SwitchBody().runSyntax(node.findDirectExpression(Expressions.SwitchBody), scope, filename);
          return foundType;
        }
        case "COND":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          return new CondBody().runSyntax(node.findDirectExpression(Expressions.CondBody), scope, filename, foundType);
        }
        case "CONV":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          new ConvBody().runSyntax(node.findDirectExpression(Expressions.ConvBody), scope, filename);
          return foundType;
        }
        case "REF":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          const s = new Source().runSyntax(node.findDirectExpression(Expressions.Source), scope, filename);
          if (foundType === undefined && s) {
            return new DataReference(s);
          } else {
            return foundType;
          }
        }
        case "FILTER":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          return new FilterBody().runSyntax(node.findDirectExpression(Expressions.FilterBody), scope, filename, foundType);
        }
        case "CORRESPONDING":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          return new CorrespondingBody().runSyntax(node.findDirectExpression(Expressions.CorrespondingBody), scope, filename, foundType);
        }
        case "EXACT":
          return this.determineType(node, scope, filename, targetType);
        case "VALUE":
        {
          const foundType = this.determineType(node, scope, filename, targetType);
          return new ValueBody().runSyntax(node.findDirectExpression(Expressions.ValueBody), scope, filename, foundType);
        }
        default:
          return new UnknownType("todo, Source type " + tok);
      }
    } else if (first === undefined || !(first instanceof ExpressionNode)) {
      return undefined;
    }
 
    let context: AbstractType | undefined = new UnknownType("todo, Source type");
 
    while (children.length >= 0) {
      if (first instanceof ExpressionNode && first.get() instanceof Expressions.MethodCallChain) {
        context = new MethodCallChain().runSyntax(first, scope, filename, targetType);
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.FieldChain) {
        context = new FieldChain().runSyntax(first, scope, filename, ReferenceType.DataReadReference);
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.StringTemplate) {
        context = new StringTemplate().runSyntax(first, scope, filename);
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.Source) {
        context = new Source().runSyntax(first, scope, filename);
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.Constant) {
        context = new Constant().runSyntax(first);
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.Dereference) {
        context = new Dereference().runSyntax(context);
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.ArrowOrDash) {
//        console.dir("dash");
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.ComponentChain) {
        context = new ComponentChain().runSyntax(context, first);
      } else if (first instanceof ExpressionNode && first.get() instanceof Expressions.AttributeChain) {
        context = new AttributeChain().runSyntax(context, first, scope, filename, ReferenceType.DataReadReference);
      }
      first = children.shift();
      if (first === undefined) {
        break;
      }
    }
 
 
    return context;
  }
 
////////////////////////////////
 
  private determineType(
    node: ExpressionNode,
    scope: CurrentScope,
    filename: string,
    targetType: AbstractType | undefined): AbstractType | undefined {
 
    const basic = new BasicTypes(filename, scope);
 
    const typeExpression = node.findFirstExpression(Expressions.TypeNameOrInfer);
    const typeToken = typeExpression?.getFirstToken();
    const typeName = typeToken?.getStr();
 
    if (typeExpression === undefined) {
      throw new Error("determineType, child TypeNameOrInfer not found");
    } else if (typeName === "#" && targetType) {
      const found = basic.lookupQualifiedName(targetType.getQualifiedName());
      if (found) {
        scope.addReference(typeToken, found, ReferenceType.InferredType, filename);
      }
      return targetType;
    }
 
    if (typeName !== "#") {
      const found = basic.parseType(typeExpression);
      if (found === undefined && scope.getDDIC().inErrorNamespace(typeName) === false) {
        return new VoidType(typeName);
      } else if (found === undefined) {
        throw new Error("Type \"" + typeName + "\" not found in scope, VALUE");
      }
      return found;
    }
 
    return targetType;
  }
 
}