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 74x 74x 74x 74x 74x 74x 74x 7x 9x 9x 7x 74x 74x 15x 15x 15x 15x 74x 74x 39x 45x 45x 39x 74x 74x 74x 74x 74x 2x 2x 72x 72x 74x 2x 2x 2x 70x 70x 74x 70x 70x 70x 70x 74x 2x 2x 2x 2x 68x 68x 68x 68x 68x 68x 68x 68x 68x 68x 68x 68x 4x 4x 64x 64x 64x 68x 68x 1x 1x 1x 132x 132x 132x 5x 5x 5x 5x 127x 127x 132x 58x 58x 58x 58x 58x 58x 58x 58x 19x 19x 19x 19x 58x 108x 108x 108x 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;
}
} |