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

90% Statements 144/160
86.88% Branches 53/61
100% Functions 2/2
90% Lines 144/160

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 1601x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1220x 1220x 1220x 1220x 1220x 1220x 1220x 230x 230x 230x 1x 1x 1x 1x 1x 230x 1219x 1219x 1219x 1219x 1219x 1220x 555x 555x 555x     555x 555x 1x 555x 169x   169x 169x 169x 6x 6x 6x 6x 6x 1x 1x 6x     6x     6x 554x 48x 48x 48x     385x 9x 337x 169x 10x 10x 169x 328x 159x 37x 1x 1x 36x 37x 24x 24x 159x 77x 122x 6x 45x 6x 6x 555x 555x 1117x 1117x 1117x 1x 1x 1x 1x 1219x 1219x 1219x 1219x 1219x 1219x     1219x 1219x 1219x 64x 1219x 1122x 1122x 1122x 1122x 87x 87x 1035x 1035x 1035x 1122x 4x 4x 4x 4x 4x 1035x 1155x 33x 33x 33x     33x 33x 25x 25x 33x 4x 4x 4x 4x 4x 4x 33x       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, DashW, 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";
import {Dereference as DereferenceExpression} from "../../2_statements/expressions";
import {Dereference} from "./dereference";
import {SourceFieldSymbol} from "./source_field_symbol";
 
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 DashW) {
        throw new Error("Ending with dash");
      } else 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 DereferenceExpression) {
        context = new Dereference().runSyntax(context);
      } else if (current.get() instanceof Expressions.ComponentName) {
        if (context instanceof TableType && context.isWithHeader()) {
          context = context.getRowType();
        }
        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 instanceof ExpressionNode
        && node.get() instanceof Expressions.SourceFieldSymbol) {
      return new SourceFieldSymbol().runSyntax(node, scope, filename);
    } else if (node.get() instanceof Expressions.SourceField) {
      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();
    } else 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) {
        scope.addReference(classTok, undefined,
                           ReferenceType.ObjectOrientedVoidReference, filename, {ooName: classNam.toUpperCase()});
        return new VoidType(classNam);
      } else {
        throw new Error("Unknown class " + classNam);
      }
    }

    return undefined;
  }
 
}