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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 73x 73x 73x 73x 73x 73x 73x 7x 9x 9x 7x 73x 73x 15x 15x 15x 15x 73x 73x 38x 44x 44x 38x 73x 73x 73x 73x 73x 2x 2x 71x 71x 73x 2x 2x 2x 69x 69x 73x 69x 69x 69x 69x 73x 2x 2x 2x 2x 67x 67x 67x 67x 67x 67x 67x 67x 67x 67x 67x 67x 4x 4x 63x 63x 63x 67x 67x 1x 1x 1x 130x 130x 130x 5x 5x 5x 5x 125x 125x 130x 57x 57x 57x 57x 57x 57x 57x 57x 19x 19x 19x 19x 57x 106x 106x 106x 1x | import * as Expressions from "../../2_statements/expressions";
import * as Statements from "../../2_statements/statements";
import {ExpressionNode, StatementNode} from "../../nodes";
import {ReferenceType} from "../_reference";
import {Source} from "../expressions/source";
import {StatementSyntax} from "../_statement_syntax";
import {Target} from "../expressions/target";
import {SyntaxInput, syntaxIssue} from "../_syntax_input";
import {AssertError} from "../assert_error";
import {Dynamic} from "../expressions/dynamic";
import {AbstractType} from "../../types/basic/_abstract_type";
import {TypeUtils} from "../_type_utils";
import {IdentifierMeta, TypedIdentifier} from "../../types/_typed_identifier";
type Parameter = {node: ExpressionNode, type: AbstractType | undefined};
export class Perform implements StatementSyntax {
public runSyntax(node: StatementNode, input: SyntaxInput): void {
if (!(node.get() instanceof Statements.Perform)) {
throw new AssertError("checkPerform unexpected node type");
}
////////////////////////////
// check parameters are defined
const changing: Parameter[] = [];
for (const c of node.findDirectExpressions(Expressions.PerformChanging)) {
for (const s of c.findDirectExpressions(Expressions.Target)) {
changing.push({node: s, type: Target.runSyntax(s, input)});
}
}
const tables: Parameter[] = [];
for (const t of node.findDirectExpressions(Expressions.PerformTables)) {
for (const s of t.findDirectExpressions(Expressions.Source)) {
tables.push({node: s, type: Source.runSyntax(s, input)});
}
}
const using: Parameter[] = [];
for (const u of node.findDirectExpressions(Expressions.PerformUsing)) {
for (const s of u.findDirectExpressions(Expressions.SimpleSource3)) {
using.push({node: s, type: Source.runSyntax(s, input)});
}
}
////////////////////////////
// find FORM definition
if (node.findFirstExpression(Expressions.IncludeName)) {
return; // in external program, not checked, todo
}
const dynamic = node.findFirstExpression(Expressions.Dynamic);
if (dynamic) {
Dynamic.runSyntax(dynamic, input);
return; // todo, maybe some parts can be checked
}
const expr = node.findFirstExpression(Expressions.FormName);
if (expr === undefined) {
return; // it might be a dynamic call
}
const name = expr.concatTokens();
const found = input.scope.findFormDefinition(name);
if (found === undefined) {
const message = "FORM definition \"" + name + "\" not found";
input.issues.push(syntaxIssue(input, expr.getFirstToken(), message));
return;
}
input.scope.addReference(expr.getFirstToken(), found, ReferenceType.FormReference, input.filename);
////////////////////////////
// check parameters match
// deferred, the FORM might be defined after the PERFORM, its parameters are only typed
// once the FORM statement itself has been traversed
input.deferred.push(() => {
// look up again, the definition is replaced when the FORM statement is traversed
const def = input.scope.findFormDefinition(name) ?? found;
if (this.checkParameters(node, tables, def.getTablesParameters(), "TABLES", input) === false) {
return;
}
// USING and CHANGING are interchangeable, the actual parameters form a single positional list
const formal = def.getUsingParameters().concat(def.getChangingParameters());
this.checkParameters(node, using.concat(changing), formal, "USING/CHANGING", input);
});
}
// returns false if an issue was reported
private checkParameters(node: StatementNode, actual: Parameter[], formal: readonly TypedIdentifier[],
kind: string, input: SyntaxInput): boolean {
if (actual.length !== formal.length) {
const message = `PERFORM, expected ${formal.length} ${kind} parameters, found ${actual.length}`;
input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
return false;
}
const typeUtils = new TypeUtils(input.scope);
for (let i = 0; i < actual.length; i++) {
const {node: source, type} = actual[i];
if (type === undefined) {
continue;
}
const parameter = formal[i];
const structureTyping = parameter.getMeta().includes(IdentifierMeta.FormParameterStructure);
const assignable = structureTyping
? typeUtils.isAssignableStructureTyping(type, parameter.getType(), source)
: typeUtils.isAssignableStrict(type, parameter.getType(), source);
if (assignable === false) {
const message = `PERFORM parameter type not compatible, ${parameter.getName()}`;
input.issues.push(syntaxIssue(input, source.getFirstToken(), message));
return false;
}
}
return true;
}
} |