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

90.36% Statements 75/83
86.59% Branches 71/82
100% Functions 2/2
90.36% Lines 75/83

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 1481x       1x 1x 1x 1x 1x 1x 1x 1x 1x   1x               498x 498x   90x 90x 1x 1x   1x       497x 497x 497x   448x 242x 242x 242x       242x 64x   64x     5x 5x   5x 5x 1x                 178x 26x         152x 64x 88x   11x 1x   10x 10x 6x   77x 46x 31x 5x 26x 4x         441x                     497x       497x   473x 473x 473x 473x 45x   428x 428x   428x 3x 3x 3x     428x     24x 24x 24x 24x     24x 24x 17x 17x 7x 3x   4x              
import {ExpressionNode} from "../../nodes";
import {CurrentScope} from "../_current_scope";
import {AbstractType} from "../../types/basic/_abstract_type";
import {INode} from "../../nodes/_inode";
import * as Expressions from "../../2_statements/expressions";
import {Dash, InstanceArrow} from "../../1_lexer/tokens";
import {StructureType, ObjectReferenceType, VoidType, DataReference, TableType, UnknownType, GenericObjectReferenceType} from "../../types/basic";
import {ComponentName} from "./component_name";
import {AttributeName} from "./attribute_name";
import {ReferenceType} from "../_reference";
import {FieldOffset} from "./field_offset";
import {FieldLength} from "./field_length";
import {TableExpression} from "./table_expression";
 
export class FieldChain {
 
  public runSyntax(
    node: ExpressionNode,
    scope: CurrentScope,
    filename: string,
    refType?: ReferenceType | undefined): AbstractType | undefined {
 
    const concat = node.concatTokens();
    if (concat.includes("-")) {
      // workaround for names with dashes
      const found = scope.findVariable(concat);
      if (found) {
        Eif (refType) {
          scope.addReference(node.getFirstToken(), found, refType, filename);
        }
        return found.getType();
      }
    }
 
    const children = node.getChildren().slice();
    let contextName = children[0].concatTokens();
    let context = this.findTop(children.shift(), scope, filename, refType);
 
    while (children.length > 0) {
      contextName += children[0].concatTokens();
      const current = children.shift();
      Iif (current === undefined) {
        break;
      }
 
      if (current.get() instanceof Dash) {
        Iif (context instanceof UnknownType) {
          throw new Error("Not a structure, type unknown, FieldChain");
        } else if (!(context instanceof StructureType)
            && !(context instanceof TableType && context.isWithHeader())
            && !(context instanceof VoidType)) {
          Eif (context instanceof TableType && context.isWithHeader() === false) {
            Eif (scope.isAllowHeaderUse(contextName.substring(0, contextName.length - 1))) {
              // FOR ALL ENTRIES workaround
              context = context.getRowType();
              if (!(context instanceof StructureType) && !(context instanceof VoidType)) {
                context = new StructureType([{name: "TABLE_LINE", type: context}]);
              }
            } else {
              throw new Error("Table without header, cannot access fields, " + contextName);
            }
          } else {
            throw new Error("Not a structure, FieldChain");
          }
        }
      } else if (current.get() instanceof InstanceArrow) {
        Iif (!(context instanceof ObjectReferenceType)
            && !(context instanceof DataReference)
            && !(context instanceof VoidType)) {
          throw new Error("Not a object reference, field chain");
        }
      } else if (current.get() instanceof Expressions.ComponentName) {
        context = new ComponentName().runSyntax(context, current);
      } else if (current instanceof ExpressionNode
          && current.get() instanceof Expressions.TableExpression) {
        if (!(context instanceof TableType) && !(context instanceof VoidType)) {
          throw new Error("Table expression, expected table");
        }
        new TableExpression().runSyntax(current, scope, filename);
        if (!(context instanceof VoidType)) {
          context = context.getRowType();
        }
      } else if (current.get() instanceof Expressions.AttributeName) {
        context = new AttributeName().runSyntax(context, current, scope, filename, refType);
      } else if (current.get() instanceof Expressions.FieldOffset && current instanceof ExpressionNode) {
        new FieldOffset().runSyntax(current, scope, filename);
      } else if (current.get() instanceof Expressions.FieldLength && current instanceof ExpressionNode) {
        new FieldLength().runSyntax(current, scope, filename);
      }
 
    }
 
    return context;
  }
 
  ////////////////
 
  private findTop(
    node: INode | undefined,
    scope: CurrentScope,
    filename: string,
    type: ReferenceType | undefined): AbstractType | undefined {
 
    Iif (node === undefined) {
      return undefined;
    }
 
    if (node.get() instanceof Expressions.SourceField
        || node.get() instanceof Expressions.SourceFieldSymbol) {
      const token = node.getFirstToken();
      const name = token.getStr();
      const found = scope.findVariable(name);
      if (found === undefined) {
        throw new Error(name + " not found, findTop");
      }
      Eif (type) {
        scope.addReference(token, found, type, filename);
      }
      if (name.includes("~")) {
        const idef = scope.findInterfaceDefinition(name.split("~")[0]);
        Eif (idef) {
          scope.addReference(token, idef, ReferenceType.ObjectOrientedReference, filename);
        }
      }
      return found.getType();
    }
 
    Eif (node.get() instanceof Expressions.ClassName) {
      const classTok = node.getFirstToken();
      const classNam = classTok.getStr();
      Iif (classNam.toUpperCase() === "OBJECT") {
        return new GenericObjectReferenceType();
      }
      const found = scope.existsObject(classNam);
      if (found.found === true && found.id) {
        scope.addReference(classTok, found.id, found.type, filename);
        return new ObjectReferenceType(found.id);
      } else if (scope.getDDIC().inErrorNamespace(classNam) === false) {
        return new VoidType(classNam);
      } else {
        throw new Error("Unknown class " + classNam);
      }
    }
 
    return undefined;
  }
 
}