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

90.62% Statements 174/192
85.71% Branches 72/84
100% Functions 2/2
90.62% Lines 174/192

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 1921x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1853x 1853x 1853x 1853x 1853x 1853x 1853x 1853x 1853x 1853x 1853x 110x 110x 6x 6x 6x 6x 6x 1x 1x 1x 1x 1x 1x 6x 109x 109x 1743x 1853x 761x 761x     761x 761x 1x 761x 233x   233x 233x 233x 12x 12x 12x 16x 16x 12x 12x 12x 12x 2x 2x 12x     12x     12x 760x 56x 56x 56x     527x 6x 471x 233x 10x 10x 233x 233x 233x 3x 3x 3x 3x 3x 3x 3x 1x 1x 1x 1x 3x 2x 2x 3x     3x 233x 465x 232x 45x 1x 1x 44x 45x 31x 31x 232x 100x 187x 19x 19x 7x 2x 7x 2x 2x 7x 87x 20x 20x 14x 4x 14x 2x 2x 14x 20x 761x 761x 1734x 1734x 1734x 1x 1x 1x 1x 1853x 1853x 1853x 1853x 1853x 1853x     1853x 1853x 1853x 79x 1853x 1774x 1726x 1774x 48x 48x 48x     48x 48x 27x 27x 48x 17x 17x 17x 21x 4x 4x 48x       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, CharacterType, HexType} 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";
import {SourceField} from "./source_field";
 
export class FieldChain {
 
  public runSyntax(
    node: ExpressionNode,
    scope: CurrentScope,
    filename: string,
    refType?: ReferenceType | ReferenceType[] | undefined): AbstractType | undefined {
 
    const children = node.getChildren();
 
    let context: AbstractType | undefined = undefined;
    try {
      context = this.findTop(children[0], scope, filename, refType);
    } catch (error) {
      const concat = node.concatTokens();
      if (concat.includes("-") && node.getFirstChild()?.get() instanceof Expressions.SourceField) {
        // workaround for names with dashes, eg. "sy-repid"
        const offset = node.findDirectExpression(Expressions.FieldOffset)?.concatTokens() || "";
        const length = node.findDirectExpression(Expressions.FieldLength)?.concatTokens() || "";
        const found = scope.findVariable(concat.replace(offset, "").replace(length, ""));
        if (found) {
          if (refType) {
            scope.addReference(node.getFirstToken(), found, refType, filename);
          }
          // this is not completely correct, but will work, dashes in names is a mess anyhow
          return found.getType();
        }
      }
      throw error;
    }
 
    for (let i = 1; i < children.length; i++) {
      const current = children[i];
      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) {
            let contextName = "";
            for (let j = 0; j < i; j++) {
              contextName += children[j].concatTokens();
            }
            if (scope.isAllowHeaderUse(contextName)) {
              // 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 an 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();
        }
        try {
          context = new ComponentName().runSyntax(context, current);
        } catch (error) {
          const concat = node.concatTokens();
          if (concat.includes("-")) {
            // workaround for names with dashes, eg. "sy-repid"
            const offset = node.findDirectExpression(Expressions.FieldOffset)?.concatTokens() || "";
            const length = node.findDirectExpression(Expressions.FieldLength)?.concatTokens() || "";
            const found = scope.findVariable(concat.replace(offset, "").replace(length, ""));
            if (found) {
              if (refType) {
                scope.addReference(node.getFirstToken(), found, refType, filename);
              }
              context = found.getType();
            } else {
              throw error;
            }
          } else {
            throw error;
          }
        }
 
      } 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) {
        const offset = new FieldOffset().runSyntax(current, scope, filename);
        if (offset) {
          if (context instanceof CharacterType) {
            context = new CharacterType(context.getLength() - offset);
          } else if (context instanceof HexType) {
            context = new HexType(context.getLength() - offset);
          }
        }
      } else if (current.get() instanceof Expressions.FieldLength && current instanceof ExpressionNode) {
        const length = new FieldLength().runSyntax(current, scope, filename);
        if (length) {
          if (context instanceof CharacterType) {
            context = new CharacterType(length);
          } else if (context instanceof HexType) {
            context = new HexType(length);
          }
        }
      }
 
    }
 
    return context;
  }
 
  ////////////////
 
  private findTop(
    node: INode | undefined,
    scope: CurrentScope,
    filename: string,
    type: ReferenceType | 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 instanceof ExpressionNode
        && node.get() instanceof Expressions.SourceField) {
      return new SourceField().runSyntax(node, scope, filename, type);
    } 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?.id) {
        scope.addReference(classTok, found.id, ReferenceType.ObjectOrientedReference, 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;
  }
 
}