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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 382x 382x 382x 382x 382x 164x 164x 218x 218x 218x 382x 9x 9x 218x 218x 382x 37x 37x 36x 36x 37x 217x 217x 217x 382x 348x 4x 348x 82x 82x 1x 1x 1x 1x 81x 81x 81x 344x 187x 187x 184x 184x 1x 1x 1x 1x 183x 183x 183x 184x 186x 348x 215x 382x 79x 79x 215x 215x 382x 8x 8x 382x 22x 22x 215x 382x 185x 185x 185x 185x 185x 185x 185x 161x 161x 185x 185x 7x 8x 8x 8x 7x 185x 181x 181x 185x 84x 84x 3x 3x 84x 4x 4x 4x 4x 4x 4x 4x 2x 2x 2x 2x 4x 84x 185x 215x 382x 9x 9x 215x 382x 36x 36x 215x 382x 1x 1x 382x 382x 1x 1x 1x 1x 1x 81x 8x 8x 73x 81x 81x 1x 1x 8x 4x 4x 4x 4x 1x | import {ExpressionNode} from "../../nodes";
import * as Expressions from "../../2_statements/expressions";
import {For} from "./for";
import {Source} from "./source";
import {AbstractType} from "../../types/basic/_abstract_type";
import {Let} from "./let";
import {FieldAssignment} from "./field_assignment";
import {AnyType, CharacterType, HexType, StringType, TableAccessType, TableType, UnknownType, VoidType} from "../../types/basic";
import {CheckSyntaxKey, SyntaxInput, syntaxIssue} from "../_syntax_input";
export class ValueBody {
public static runSyntax(
node: ExpressionNode | undefined,
input: SyntaxInput,
targetType: AbstractType | undefined): AbstractType | undefined {
if (node === undefined) {
return targetType;
}
let letScoped = false;
const letNode = node.findDirectExpression(Expressions.Let);
if (letNode) {
letScoped = Let.runSyntax(letNode, input);
}
let forScopes = 0;
for (const forNode of node.findDirectExpressions(Expressions.For) || []) {
const scoped = For.runSyntax(forNode, input);
if (scoped === true) {
forScopes++;
}
}
const fields = new Set<string>();
const hasTableLines = node.findDirectExpression(Expressions.ValueBodyLine) !== undefined;
for (const child of node.getChildren()) {
if (!(child instanceof ExpressionNode)) {
continue;
} else if (child.get() instanceof Expressions.FieldAssignment) {
const fieldname = child.findDirectExpression(Expressions.FieldSub)?.concatTokens().toUpperCase();
if (fieldname && hasTableLines === false && fields.has(fieldname)) {
const message = "Duplicate field assignment";
input.issues.push(syntaxIssue(input, child.getFirstToken(), message));
return VoidType.get(CheckSyntaxKey);
}
if (fieldname) {
fields.add(fieldname);
}
} else if (child.get() instanceof Expressions.ValueBodyLine) {
const rowFields = new Set(fields);
for (const assignment of child.findDirectExpressions(Expressions.FieldAssignment)) {
const fieldname = assignment.findDirectExpression(Expressions.FieldSub)?.concatTokens().toUpperCase();
if (fieldname && rowFields.has(fieldname)) {
const message = "Duplicate field assignment";
input.issues.push(syntaxIssue(input, assignment.getFirstToken(), message));
return VoidType.get(CheckSyntaxKey);
}
if (fieldname) {
rowFields.add(fieldname);
}
}
}
}
for (const s of node.findDirectExpressions(Expressions.FieldAssignment)) {
FieldAssignment.runSyntax(s, input, targetType);
}
let type: AbstractType | undefined = undefined; // todo, this is only correct if there is a single source in the body
for (const s of node.findDirectExpressions(Expressions.Source)) {
type = Source.runSyntax(s, input, type);
}
for (const s of node.findDirectExpression(Expressions.ValueBase)?.findDirectExpressions(Expressions.Source) || []) {
type = Source.runSyntax(s, input, type);
}
for (const foo of node.findDirectExpressions(Expressions.ValueBodyLine)) {
if (!(targetType instanceof TableType)
&& !(targetType instanceof UnknownType)
&& !(targetType instanceof AnyType)
&& targetType !== undefined
&& !(targetType instanceof VoidType)) {
const message = "Value, not a table type";
input.issues.push(syntaxIssue(input, foo.getFirstToken(), message));
return VoidType.get(CheckSyntaxKey);
}
let rowType: AbstractType | undefined = targetType;
if (targetType instanceof TableType) {
rowType = targetType.getRowType();
}
for (const l of foo.findDirectExpressions(Expressions.ValueBodyLines)) {
for (const s of l.findDirectExpressions(Expressions.Source)) {
// LINES OF ?? todo, pass type,
Source.runSyntax(s, input);
}
}
for (const s of foo.findDirectExpressions(Expressions.FieldAssignment)) {
FieldAssignment.runSyntax(s, input, rowType);
}
for (const s of foo.findDirectExpressions(Expressions.Source)) {
const sourceType = Source.runSyntax(s, input, rowType);
if (rowType instanceof StringType && sourceType instanceof CharacterType) {
const message = "VALUE, source type CharacterType not compatible with StringType";
input.issues.push(syntaxIssue(input, s.getFirstToken(), message));
} else if (this.insertsUsingTableKey(targetType)) {
// only STANDARD tables append the rows, and thus allow padding of shorter c and x values
const row = this.characterOrHexLength(rowType);
const source = this.characterOrHexLength(sourceType);
if (row !== undefined
&& source !== undefined
&& row.abap === source.abap
&& row.length !== source.length) {
const message = `VALUE, source type ${source.abap} LENGTH ${source.length} not compatible ` +
`with row type ${row.abap} LENGTH ${row.length}`;
input.issues.push(syntaxIssue(input, s.getFirstToken(), message));
}
}
}
}
if (letScoped === true) {
input.scope.pop(node.getLastToken().getEnd());
}
for (let i = 0; i < forScopes; i++) {
input.scope.pop(node.getLastToken().getEnd());
}
if (targetType?.isGeneric() && type) {
return type;
}
return targetType ? targetType : type;
}
/** SORTED and HASHED tables insert the constructed rows using the table key, which
* requires the rows to be compatible with the row type. STANDARD tables only append
* the rows, so shorter c and x values are padded on the right instead. */
private static insertsUsingTableKey(targetType: AbstractType | undefined): boolean {
if (!(targetType instanceof TableType)) {
return false;
}
const accessType = targetType.getAccessType();
return accessType === TableAccessType.sorted || accessType === TableAccessType.hashed;
}
private static characterOrHexLength(type: AbstractType | undefined): {abap: string, length: number} | undefined {
if (type instanceof CharacterType) {
return {abap: "c", length: type.getLength()};
} else if (type instanceof HexType) {
return {abap: "x", length: type.getLength()};
}
return undefined;
}
}
|