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

89.19% Statements 132/148
85.45% Branches 47/55
100% Functions 2/2
89.19% Lines 132/148

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 1x 1x 1x 1x 1x 1x 591x 591x 591x 591x 591x 591x 591x 103x 103x 103x 1x 1x 1x 1x 1x 103x 590x 590x 590x 590x 590x 591x 280x 280x 280x     280x 280x 76x   76x 76x 76x 5x 5x 5x 5x 5x 1x 1x 5x     5x     5x 280x 27x 27x 27x     204x 76x 177x 101x 11x 1x 1x 10x 11x 6x 6x 101x 50x 90x 7x 40x 8x 8x 280x 280x 523x 523x 523x 1x 1x 1x 1x 590x 590x 590x 590x 590x 590x     590x 590x 590x 563x 563x 563x 563x 55x 55x 508x 508x 508x 563x 3x 3x 3x 3x 3x 508x 508x 27x 27x 27x 27x 27x     27x 27x 20x 20x 27x 3x 7x 4x 4x 27x       1x 1x
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) {
        if (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();
      if (current === undefined) {
        break;
      }
 
      if (current.get() instanceof Dash) {
        if (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)) {
          if (context instanceof TableType && context.isWithHeader() === false) {
            if (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) {
        if (!(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 {
 
    if (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");
      }
      if (type) {
        scope.addReference(token, found, type, filename);
      }
      if (name.includes("~")) {
        const idef = scope.findInterfaceDefinition(name.split("~")[0]);
        if (idef) {
          scope.addReference(token, idef, ReferenceType.ObjectOrientedReference, filename);
        }
      }
      return found.getType();
    }
 
    if (node.get() instanceof Expressions.ClassName) {
      const classTok = node.getFirstToken();
      const classNam = classTok.getStr();
      if (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;
  }
 
}