Skip to main content

ironwork_syntax/
ast.rs

1use crate::Pos;
2
3mod oo;
4pub use oo::*;
5
6#[derive(Clone, Debug, Default, PartialEq, Eq)]
7pub struct Program {
8    pub id: String,
9    /// Options from CBL and PROCESS cards, in the order written.
10    pub options: Vec<String>,
11    /// PROGRAM-ID ... IS INITIAL, or once compiled the INITIAL option: WORKING-STORAGE starts
12    /// afresh on every CALL.
13    pub initial: bool,
14    pub recursive: bool,
15    /// PROGRAM-ID ... IS COMMON, for a program another contains.
16    pub common: bool,
17    pub working_storage: Vec<DataEntry>,
18    /// LOCAL-STORAGE: fresh for every activation of the program.
19    pub local_storage: Vec<DataEntry>,
20    pub linkage: Vec<DataEntry>,
21    /// PROCEDURE DIVISION USING: the LINKAGE items the caller's arguments address.
22    pub using: Vec<Param>,
23    pub returning: Option<String>,
24    pub paragraphs: Vec<Paragraph>,
25    /// Files declared by SELECT and described by FD, with their record descriptions.
26    pub files: Vec<FileDecl>,
27    /// The program's source file, then each COPY member, as positions index them.
28    pub sources: Vec<String>,
29    /// EXEC blocks in the DATA DIVISION: SQL declarations, cursors and DECLARE SECTION markers.
30    pub exec_declarations: Vec<ExecBlock>,
31    /// The REPORT SECTION, and the DECLARATIVES that serve it.
32    pub report_writer: crate::report::ReportWriter,
33    /// The DECLARATIVES' USE AFTER EXCEPTION/ERROR and USE FOR DEBUGGING procedures.
34    pub declaratives: Declaratives,
35    /// The REPOSITORY's classes, and for a class definition or a method what it is.
36    pub oo: Option<Box<Oo>>,
37    pub environment: Environment,
38    /// The PROGRAM-IDs of the programs it directly contains.
39    pub nested: Vec<String>,
40    /// The programs that contain it, innermost first, with the names each declares GLOBAL.
41    pub containers: Vec<Container>,
42    /// The messages reading its source gave that did not stop the parse, in the order found.
43    pub messages: Vec<crate::Error>,
44    /// FUNCTION-ID in place of PROGRAM-ID: a user-defined function, or a prototype of one.
45    pub function: Option<Function>,
46    /// The user-defined functions it may invoke: those defined or prototyped before it in its
47    /// source, and a function itself.
48    pub prototypes: Vec<Prototype>,
49}
50
51impl Program {
52    /// The name a CALL or a function invocation loads it by: a function's external name.
53    pub fn load_name(&self) -> &str {
54        self.function.as_ref().map_or(&self.id, |f| &f.external)
55    }
56
57    pub fn is_prototype(&self) -> bool {
58        self.function.as_ref().is_some_and(|f| f.prototype)
59    }
60}
61
62#[derive(Clone, Debug, PartialEq, Eq)]
63pub struct Function {
64    /// AS literal-1, else the function-name.
65    pub external: String,
66    /// IS PROTOTYPE: a description for invocations to be checked against, with no code.
67    pub prototype: bool,
68    pub pos: Pos,
69}
70
71/// A user-defined function as its definition or prototype describes it to an invocation.
72#[derive(Clone, Debug, PartialEq, Eq)]
73pub struct Prototype {
74    pub name: String,
75    pub external: String,
76    pub using: Vec<Param>,
77    pub returning: Option<String>,
78    pub linkage: Vec<DataEntry>,
79    /// The PICTURE notation of its definition: DECIMAL-POINT IS COMMA and the currency signs.
80    pub environment: Environment,
81    pub pos: Pos,
82}
83
84#[derive(Clone, Debug, PartialEq, Eq)]
85pub struct Param {
86    pub by_value: bool,
87    pub name: String,
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub enum Organization {
92    Sequential,
93    LineSequential,
94    Indexed,
95    Relative,
96}
97
98#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
99pub enum Access {
100    #[default]
101    Sequential,
102    Random,
103    Dynamic,
104}
105
106#[derive(Clone, Debug, PartialEq, Eq)]
107pub struct FileDecl {
108    pub name: String,
109    /// The DD name ASSIGN gives, with any `UT-S-` style prefix removed.
110    pub assign: String,
111    /// ASSIGN's target where it can be a data item holding the file's name at each OPEN. The
112    /// compile keeps it only where it is one, under `--compliance extended`.
113    pub assign_item: Option<AssignItem>,
114    pub organization: Organization,
115    pub access: Access,
116    pub record_key: Option<Ref>,
117    /// ALTERNATE RECORD KEY items, and whether each allows duplicates.
118    pub alternate_keys: Vec<(Ref, bool)>,
119    pub relative_key: Option<Ref>,
120    pub optional: bool,
121    pub status: Option<Ref>,
122    /// FILE STATUS's second data-name, for a VSAM file: the return, function and feedback codes.
123    pub vsam_status: Option<Ref>,
124    /// PASSWORD IS items, checked and of no effect, as files here have no passwords.
125    pub passwords: Vec<Ref>,
126    /// RECORDING MODE: F, V, U or S.
127    pub recording: Option<char>,
128    pub record_min: Option<u32>,
129    pub record_max: Option<u32>,
130    /// RECORD IS VARYING: `record_min` and `record_max` are its FROM and TO, each None when not
131    /// written (Language Reference SC27-8713-03, p. 187).
132    pub record_varying: bool,
133    /// RECORD IS VARYING ... DEPENDING ON: the item holding each record's length.
134    pub record_depending: Option<Ref>,
135    pub records: Vec<DataEntry>,
136    /// FD ... REPORT IS: the reports written to the file.
137    pub reports: Vec<String>,
138    /// FD ... LINAGE: the logical page. An SD's is read and dropped, as IBM ignores it.
139    pub linage: Option<Linage>,
140    /// Described by SD: a sort or merge file, which needs no data set.
141    pub sort: bool,
142    pub external: bool,
143    pub global: bool,
144    /// For a GLOBAL file of a program containing this one, that program's PROGRAM-ID.
145    pub declared_in: Option<String>,
146    pub pos: Pos,
147}
148
149/// ASSIGN TO a name, or ASSIGN TO DYNAMIC or USING a data-name: Micro Focus and GnuCOBOL take a
150/// name that is a data item's, and the other two forms always, as the item holding the file's
151/// name (Enterprise COBOL's assignment-name is never a data item, Language Reference
152/// SC27-8713-03, ASSIGN clause).
153#[derive(Clone, Debug, PartialEq, Eq)]
154pub struct AssignItem {
155    pub reference: Ref,
156    /// DYNAMIC or USING was written.
157    pub explicit: bool,
158}
159
160/// A program containing another, as the contained program sees it: its PROGRAM-ID, and the 01
161/// records and files it declares GLOBAL, each with what is subordinate to it.
162#[derive(Clone, Debug, Default, PartialEq, Eq)]
163pub struct Container {
164    pub id: String,
165    pub working_storage: Vec<DataEntry>,
166    pub local_storage: Vec<DataEntry>,
167    pub linkage: Vec<DataEntry>,
168    pub files: Vec<FileDecl>,
169}
170
171/// LINAGE IS lines [WITH FOOTING AT footing] [LINES AT TOP top] [LINES AT BOTTOM bottom].
172#[derive(Clone, Debug, PartialEq, Eq)]
173pub struct Linage {
174    pub lines: LinageValue,
175    pub footing: Option<LinageValue>,
176    pub top: Option<LinageValue>,
177    pub bottom: Option<LinageValue>,
178}
179
180#[derive(Clone, Debug, PartialEq, Eq)]
181pub enum LinageValue {
182    /// The digits as written, whose count sizes LINAGE-COUNTER.
183    Integer(String),
184    Data(Ref),
185}
186
187pub use rt::vocab::{Closing, OpenMode};
188
189#[derive(Clone, Debug, PartialEq, Eq)]
190pub enum Advancing {
191    Lines { before: bool, count: Expr },
192    Page { before: bool },
193    /// A mnemonic-name of SPECIAL-NAMES, with the environment-name it stands for: C01 to C12,
194    /// CSP, S01 to S05 or AFP-5A.
195    Mnemonic { before: bool, name: String, environment: String },
196}
197
198impl Advancing {
199    pub fn before(&self) -> bool {
200        match self {
201            Self::Lines { before, .. } | Self::Page { before } | Self::Mnemonic { before, .. } => *before,
202        }
203    }
204}
205
206#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
207pub enum Usage {
208    #[default]
209    Display,
210    Binary,
211    /// COMP-5: binary, never truncated to the PICTURE.
212    NativeBinary,
213    Packed,
214    Float1,
215    Float2,
216    National,
217    /// DISPLAY-1: DBCS characters, two bytes each.
218    Dbcs,
219    Pointer,
220    Index,
221    ObjectReference,
222    /// FUNCTION-POINTER or PROCEDURE-POINTER.
223    ProgramPointer,
224}
225
226pub use rt::vocab::{SignClause, SignPosition};
227
228#[derive(Clone, Debug, PartialEq, Eq)]
229pub struct DataEntry {
230    pub level: u8,
231    /// None for FILLER or an unnamed entry.
232    pub name: Option<String>,
233    /// The name as the source spells it, where that is not all capitals: JSON and XML GENERATE
234    /// keep it.
235    pub spelled: Option<String>,
236    pub picture: Option<String>,
237    /// None when no USAGE is written; the item then inherits its group's.
238    pub usage: Option<Usage>,
239    pub value: Option<Literal>,
240    pub redefines: Option<String>,
241    /// OCCURS: the number of occurrences, or the most of them for OCCURS DEPENDING ON.
242    pub occurs: Option<u32>,
243    /// OCCURS ... DEPENDING ON: the fewest occurrences, 1 when no integer-1 TO is written
244    /// (Language Reference SC27-8713-03, p. 204).
245    pub occurs_min: Option<u32>,
246    /// OCCURS ... DEPENDING ON: the item that holds how many occurrences there are.
247    pub depending_on: Option<Ref>,
248    pub sign: Option<SignClause>,
249    pub justified: bool,
250    pub sync: bool,
251    pub blank_when_zero: bool,
252    /// OCCURS ... INDEXED BY: the index names the table declares.
253    pub indexed_by: Vec<String>,
254    /// OCCURS ... ASCENDING/DESCENDING KEY: each key and whether it ascends, for SEARCH ALL.
255    pub keys: Vec<(bool, Ref)>,
256    /// Level 88: the values that make the condition true.
257    /// Each value, or the low and high ends of a THRU range.
258    pub condition_values: Vec<(Literal, Option<Literal>)>,
259    /// Level 88 WHEN SET TO FALSE: the value SET ... TO FALSE stores.
260    pub false_value: Option<Literal>,
261    /// Level 66 RENAMES: the item renamed, or the first and last of a THRU range.
262    pub renames: Option<(Ref, Option<Ref>)>,
263    /// USAGE OBJECT REFERENCE class-name: the class; None for a universal reference.
264    pub object_class: Option<String>,
265    /// EXTERNAL, written on the entry or attained from its FD.
266    pub external: bool,
267    /// GLOBAL, written on the entry or attained from its FD.
268    pub global: bool,
269    pub pos: Pos,
270}
271
272pub use rt::vocab::Figurative;
273
274#[derive(Clone, Debug, PartialEq, Eq)]
275pub enum Literal {
276    Alnum(String),
277    Hex(Vec<u8>),
278    National(String),
279    /// G'...', or N'...' under NSYMBOL(DBCS): characters the code page's DBCS part encodes.
280    Dbcs(String),
281    /// As written: optional sign, digits, optional decimal point.
282    Number(String),
283    Figurative(Figurative),
284    All(Box<Literal>),
285}
286
287/// A paragraph, or a section header holding the statements before the section's first paragraph.
288#[derive(Clone, Debug, PartialEq, Eq)]
289pub struct Paragraph {
290    pub name: String,
291    pub statements: Vec<Stmt>,
292    /// The section the paragraph belongs to, or the section's own name for its header.
293    pub section: Option<String>,
294    pub is_section: bool,
295    /// The section's priority-number, 0 when it has none: 50 to 99 is an independent segment.
296    pub priority: u8,
297    pub pos: Pos,
298}
299
300/// A procedure name as written, with the section that qualifies it.
301#[derive(Clone, Debug, PartialEq, Eq)]
302pub struct ProcName {
303    pub name: String,
304    pub section: Option<String>,
305}
306
307/// USE AFTER EXCEPTION/ERROR and USE FOR DEBUGGING sections; USE BEFORE REPORTING ones are the
308/// Report Writer's.
309#[derive(Clone, Debug, Default, PartialEq, Eq)]
310pub struct Declaratives {
311    pub errors: Vec<UseAfterError>,
312    pub debugging: Vec<UseForDebugging>,
313}
314
315/// A USE AFTER STANDARD EXCEPTION/ERROR PROCEDURE section.
316#[derive(Clone, Debug, PartialEq, Eq)]
317pub struct UseAfterError {
318    /// The section's header among the program's paragraphs.
319    pub section: usize,
320    pub global: bool,
321    pub on: ErrorUse,
322    pub pos: Pos,
323}
324
325/// The files an EXCEPTION/ERROR procedure serves: those it names, or those open in one mode.
326#[derive(Clone, Debug, PartialEq, Eq)]
327pub enum ErrorUse {
328    Files(Vec<String>),
329    Mode(OpenMode),
330}
331
332/// A USE FOR DEBUGGING section, which runs before each procedure it names.
333#[derive(Clone, Debug, PartialEq, Eq)]
334pub struct UseForDebugging {
335    pub section: usize,
336    /// Empty for ALL PROCEDURES.
337    pub procedures: Vec<ProcName>,
338    pub pos: Pos,
339}
340
341#[derive(Clone, Copy, Debug, PartialEq, Eq)]
342pub enum ExitKind {
343    Plain,
344    Paragraph,
345    Section,
346    Perform,
347    PerformCycle,
348}
349
350#[derive(Clone, Debug, PartialEq, Eq)]
351pub enum Subject {
352    Bool(bool),
353    Expr(Expr),
354    Cond(Cond),
355}
356
357#[derive(Clone, Debug, PartialEq, Eq)]
358pub enum Object {
359    Any,
360    Bool(bool),
361    Cond(Cond),
362    Value { not: bool, from: Expr, thru: Option<Expr> },
363}
364
365/// One WHEN group: any of its alternatives, each with an object per subject, selects the body.
366#[derive(Clone, Debug, PartialEq, Eq)]
367pub struct When {
368    pub alternatives: Vec<Vec<Object>>,
369    pub body: Vec<Stmt>,
370}
371
372#[derive(Clone, Debug, PartialEq, Eq)]
373pub struct Ref {
374    pub name: String,
375    pub qualifiers: Vec<String>,
376    pub subscripts: Vec<Expr>,
377    pub refmod: Option<RefMod>,
378    pub pos: Pos,
379}
380
381#[derive(Clone, Debug, PartialEq, Eq)]
382pub struct RefMod {
383    pub start: Box<Expr>,
384    pub length: Option<Box<Expr>>,
385}
386
387#[derive(Clone, Debug, PartialEq, Eq)]
388pub struct FunctionCall {
389    pub name: String,
390    pub args: Vec<Expr>,
391    /// A keyword argument, as in FUNCTION TRIM(X LEADING).
392    pub modifier: Option<String>,
393    pub refmod: Option<RefMod>,
394    /// Each argument written as a table with ALL subscripts, as in FUNCTION SUM(T(ALL)): its index
395    /// and the positions of those subscripts, where its reference holds 1.
396    pub all_subscripts: Vec<(usize, Vec<usize>)>,
397    pub pos: Pos,
398}
399
400#[derive(Clone, Debug, PartialEq, Eq)]
401pub enum Operand {
402    Ref(Ref),
403    Literal(Literal),
404    Function(FunctionCall),
405    LengthOf(Ref),
406    AddressOf(Ref),
407}
408
409pub use rt::vocab::{AcceptFrom, BinOp, RelOp};
410
411#[derive(Clone, Debug, PartialEq, Eq)]
412pub enum Expr {
413    Operand(Operand),
414    Neg(Box<Expr>),
415    Bin(Box<Expr>, BinOp, Box<Expr>),
416}
417
418#[derive(Clone, Debug, PartialEq, Eq)]
419pub enum Class {
420    Numeric,
421    Alphabetic,
422    AlphabeticLower,
423    AlphabeticUpper,
424    /// Every two bytes a DBCS character, X'41' to X'FE' each, or the DBCS space.
425    Dbcs,
426    /// Every two bytes a DBCS character with a first byte X'41' to X'7E', or the DBCS space.
427    Kanji,
428    Positive,
429    Negative,
430    Zero,
431    /// A class-name of the SPECIAL-NAMES CLASS clause.
432    Named(String),
433}
434
435#[derive(Clone, Debug, PartialEq, Eq)]
436pub enum Cond {
437    Rel(Expr, RelOp, Expr),
438    Class(Expr, Class),
439    Name(Ref),
440    /// After AND or OR, a bare name that is either a condition-name or the object of an
441    /// abbreviated relation; which one depends on what the name resolves to. `negated` is a NOT
442    /// the relational operator carries, as in NOT =.
443    NameOrRel { subject: Expr, op: RelOp, negated: bool, name: Ref },
444    Not(Box<Cond>),
445    And(Box<Cond>, Box<Cond>),
446    Or(Box<Cond>, Box<Cond>),
447}
448
449#[derive(Clone, Debug, PartialEq, Eq)]
450pub struct Target {
451    pub r: Ref,
452    pub rounded: bool,
453}
454
455#[derive(Clone, Debug, PartialEq, Eq)]
456pub struct Varying {
457    pub var: Ref,
458    pub from: Expr,
459    pub by: Expr,
460    pub until: Cond,
461}
462
463#[derive(Clone, Debug, PartialEq, Eq)]
464pub enum Loop {
465    Once,
466    Times(Expr),
467    Until { cond: Cond, test_after: bool },
468    /// VARYING, and each AFTER phrase, outermost first.
469    Varying { varying: Box<Varying>, after: Vec<Varying>, test_after: bool },
470}
471
472#[derive(Clone, Debug, PartialEq, Eq)]
473pub enum Stmt {
474    Move { from: Operand, to: Vec<Ref>, pos: Pos },
475    Compute { targets: Vec<Target>, expr: Expr, size_error: Option<SizeError>, pos: Pos },
476    /// ADD, SUBTRACT, MULTIPLY and DIVIDE, reduced to their arithmetic.
477    Arith(Box<Arith>),
478    /// MOVE, ADD or SUBTRACT CORRESPONDING, which the compiler expands into a MOVE per pair of
479    /// corresponding items, or one ADD or SUBTRACT over them all.
480    Corresponding(Box<Corresponding>),
481    If { cond: Cond, then: Vec<Stmt>, otherwise: Vec<Stmt>, pos: Pos },
482    PerformInline { body: Vec<Stmt>, repeat: Loop, pos: Pos },
483    PerformProc { from: ProcName, thru: Option<ProcName>, repeat: Loop, pos: Pos },
484    Evaluate { subjects: Vec<Subject>, whens: Vec<When>, other: Vec<Stmt>, pos: Pos },
485    Display { items: Vec<Operand>, upon: Option<Upon>, no_advancing: bool, pos: Pos },
486    Open { files: Vec<(OpenMode, String)>, pos: Pos },
487    Close { files: Vec<(String, Option<Closing>)>, pos: Pos },
488    Read(Box<ReadStmt>),
489    Write { record: Ref, from: Option<Operand>, advancing: Option<Advancing>, invalid: Handlers, end_of_page: Handlers, pos: Pos },
490    Rewrite { record: Ref, from: Option<Operand>, invalid: Handlers, pos: Pos },
491    Delete { file: String, invalid: Handlers, pos: Pos },
492    Start { file: String, key: Option<(RelOp, Ref)>, invalid: Handlers, pos: Pos },
493    /// INITIALIZE; `with` holds its FILLER, VALUE, REPLACING and DEFAULT phrases, None without any.
494    Initialize { targets: Vec<Ref>, with: Option<Box<InitializeWith>>, pos: Pos },
495    /// GO TO; with no target, the altered GO TO that only an ALTER gives one.
496    GoTo { target: Option<ProcName>, pos: Pos },
497    /// GO TO ... DEPENDING ON: the procedure the item's value numbers, or on when none does.
498    GoToDepending { targets: Vec<ProcName>, on: Ref, pos: Pos },
499    /// ALTER: each paragraph whose GO TO is to go instead to the procedure paired with it.
500    Alter { pairs: Vec<(ProcName, ProcName)>, pos: Pos },
501    /// ENTRY: where a CALL of `name` begins, and the LINKAGE items its USING list addresses.
502    Entry { name: String, using: Vec<Param>, pos: Pos },
503    Goback { pos: Pos },
504    /// EXIT PROGRAM: returns from a called program; in the first program it does nothing.
505    ExitProgram { pos: Pos },
506    Call(Box<Call>),
507    Cancel { targets: Vec<Operand>, pos: Pos },
508    Set { set: SetStmt, pos: Pos },
509    /// ACCEPT; `exception` is the ON EXCEPTION phrases `--compliance extended` reads with
510    /// ARGUMENT-VALUE.
511    Accept { target: Ref, from: AcceptFrom, exception: Handlers, pos: Pos },
512    String(Box<StringStmt>),
513    Unstring(Box<Unstring>),
514    Inspect(Box<Inspect>),
515    Search(Box<Search>),
516    /// NEXT SENTENCE: control passes to the statement after the next separator period.
517    NextSentence,
518    /// A separator period in the PROCEDURE DIVISION: where NEXT SENTENCE resumes.
519    SentenceEnd,
520    Exec(Box<ExecBlock>),
521    Report(Box<crate::report::ReportStmt>),
522    Invoke(Box<Invoke>),
523    JsonGenerate(Box<JsonGenerate>),
524    JsonParse(Box<JsonParse>),
525    XmlParse(Box<XmlParse>),
526    XmlGenerate(Box<XmlGenerate>),
527    ExitMethod { pos: Pos },
528    Sorting(Box<Sorting>),
529    StopRun { pos: Pos },
530    Continue,
531    Exit { kind: ExitKind, pos: Pos },
532}
533
534#[derive(Clone, Debug, PartialEq, Eq)]
535pub struct SizeError {
536    pub on: Vec<Stmt>,
537    pub not_on: Vec<Stmt>,
538}
539
540#[derive(Clone, Copy, Debug, PartialEq, Eq)]
541pub enum ArithVerb {
542    Add,
543    Subtract,
544    Multiply,
545    Divide,
546}
547
548/// One arithmetic statement: `targets` each receive `expr`, which is written in terms of the
549/// statement's operands and, for the forms without GIVING, the target itself (`Operand::Ref` of
550/// the target, placed by the parser).
551#[derive(Clone, Debug, PartialEq, Eq)]
552pub struct Arith {
553    pub verb: ArithVerb,
554    pub computations: Vec<(Target, Expr)>,
555    pub remainder: Option<(Target, Expr, Expr)>,
556    pub size_error: Option<SizeError>,
557    pub pos: Pos,
558}
559
560#[derive(Clone, Copy, Debug, PartialEq, Eq)]
561pub enum CorrespondingVerb {
562    Move,
563    Add,
564    Subtract,
565}
566
567/// `from` is the sending group and `to` the receiving one; `rounded` and `size_error` are ADD's
568/// and SUBTRACT's.
569#[derive(Clone, Debug, PartialEq, Eq)]
570pub struct Corresponding {
571    pub verb: CorrespondingVerb,
572    pub from: Ref,
573    pub to: Ref,
574    pub rounded: bool,
575    pub size_error: Option<SizeError>,
576    pub pos: Pos,
577}
578
579#[derive(Clone, Copy, Debug, PartialEq, Eq)]
580pub enum ArgMode {
581    Reference,
582    Content,
583    Value,
584}
585
586/// One CALL argument; `value` is None for OMITTED.
587#[derive(Clone, Debug, PartialEq, Eq)]
588pub struct Arg {
589    pub mode: ArgMode,
590    pub value: Option<Operand>,
591}
592
593#[derive(Clone, Debug, PartialEq, Eq)]
594pub struct Call {
595    pub target: Operand,
596    pub using: Vec<Arg>,
597    pub returning: Option<Ref>,
598    pub on_exception: Option<Vec<Stmt>>,
599    pub not_on_exception: Option<Vec<Stmt>>,
600    pub pos: Pos,
601}
602
603#[derive(Clone, Debug, PartialEq, Eq)]
604pub enum SetStmt {
605    ConditionTrue(Vec<Ref>),
606    /// SET condition-names TO FALSE: each conditional variable gets its WHEN SET TO FALSE value.
607    ConditionFalse(Vec<Ref>),
608    /// SET targets TO value: an index or integer to a number, a pointer to ADDRESS OF, NULL or another pointer.
609    To { targets: Vec<Ref>, value: Operand },
610    /// SET procedure-pointers or function-pointers TO ENTRY, the entry named by a literal or identifier.
611    Entry { targets: Vec<Ref>, entry: Operand },
612    /// SET ADDRESS OF targets TO pointer.
613    AddressOf { targets: Vec<Ref>, value: Operand },
614    UpDown { targets: Vec<Ref>, down: bool, by: Expr },
615    /// SET mnemonic-names TO ON or OFF, each group in order: the UPSI switches the names stand for.
616    Switches(Vec<(Vec<Ref>, bool)>),
617}
618
619#[derive(Clone, Debug, PartialEq, Eq)]
620pub enum Delimiter {
621    Size,
622    By(Operand),
623}
624
625#[derive(Clone, Debug, PartialEq, Eq)]
626pub struct StringStmt {
627    pub sources: Vec<(Operand, Delimiter)>,
628    pub into: Ref,
629    pub pointer: Option<Ref>,
630    pub on_overflow: Option<Vec<Stmt>>,
631    pub not_on_overflow: Option<Vec<Stmt>>,
632    pub pos: Pos,
633}
634
635#[derive(Clone, Debug, PartialEq, Eq)]
636pub struct UnstringInto {
637    pub target: Ref,
638    pub delimiter_in: Option<Ref>,
639    pub count_in: Option<Ref>,
640}
641
642#[derive(Clone, Debug, PartialEq, Eq)]
643pub struct Unstring {
644    pub source: Ref,
645    /// Each delimiter, and whether ALL makes a run of it one delimiter.
646    pub delimiters: Vec<(bool, Operand)>,
647    pub into: Vec<UnstringInto>,
648    pub pointer: Option<Ref>,
649    pub tallying: Option<Ref>,
650    pub on_overflow: Option<Vec<Stmt>>,
651    pub not_on_overflow: Option<Vec<Stmt>>,
652    pub pos: Pos,
653}
654
655/// BEFORE or AFTER INITIAL value: where in the inspected item a phrase applies.
656#[derive(Clone, Debug, PartialEq, Eq)]
657pub struct Bound {
658    pub after: bool,
659    pub value: Operand,
660}
661
662pub use rt::vocab::InspectMode;
663
664/// One TALLYING or REPLACING phrase. `pattern` is None for CHARACTERS; `by` is None for TALLYING.
665#[derive(Clone, Debug, PartialEq, Eq)]
666pub struct InspectPhrase {
667    pub mode: InspectMode,
668    pub pattern: Option<Operand>,
669    pub by: Option<Operand>,
670    pub counter: Option<Ref>,
671    pub bounds: Vec<Bound>,
672}
673
674/// The categories INITIALIZE's VALUE and REPLACING phrases name (Language Reference SC27-8713-03,
675/// p. 350); EGCS is DBCS.
676#[derive(Clone, Copy, Debug, PartialEq, Eq)]
677pub enum DataCategory {
678    Alphabetic,
679    Alphanumeric,
680    AlphanumericEdited,
681    Dbcs,
682    National,
683    NationalEdited,
684    Numeric,
685    NumericEdited,
686    Utf8,
687}
688
689impl DataCategory {
690    pub const ALL: [Self; 9] = [
691        Self::Alphabetic,
692        Self::Alphanumeric,
693        Self::AlphanumericEdited,
694        Self::Dbcs,
695        Self::National,
696        Self::NationalEdited,
697        Self::Numeric,
698        Self::NumericEdited,
699        Self::Utf8,
700    ];
701
702    pub fn from_word(word: &str) -> Option<Self> {
703        Some(match word {
704            "ALPHABETIC" => Self::Alphabetic,
705            "ALPHANUMERIC" => Self::Alphanumeric,
706            "ALPHANUMERIC-EDITED" => Self::AlphanumericEdited,
707            "DBCS" | "EGCS" => Self::Dbcs,
708            "NATIONAL" => Self::National,
709            "NATIONAL-EDITED" => Self::NationalEdited,
710            "NUMERIC" => Self::Numeric,
711            "NUMERIC-EDITED" => Self::NumericEdited,
712            "UTF-8" => Self::Utf8,
713            _ => return None,
714        })
715    }
716
717    pub fn word(self) -> &'static str {
718        match self {
719            Self::Alphabetic => "ALPHABETIC",
720            Self::Alphanumeric => "ALPHANUMERIC",
721            Self::AlphanumericEdited => "ALPHANUMERIC-EDITED",
722            Self::Dbcs => "DBCS",
723            Self::National => "NATIONAL",
724            Self::NationalEdited => "NATIONAL-EDITED",
725            Self::Numeric => "NUMERIC",
726            Self::NumericEdited => "NUMERIC-EDITED",
727            Self::Utf8 => "UTF-8",
728        }
729    }
730}
731
732/// INITIALIZE's phrases. `value` lists the VALUE phrase's categories, every one for ALL TO VALUE.
733#[derive(Clone, Debug, Default, PartialEq, Eq)]
734pub struct InitializeWith {
735    pub filler: bool,
736    pub value: Vec<DataCategory>,
737    pub replacing: Vec<(DataCategory, Operand)>,
738    pub default: bool,
739}
740
741/// What an elementary receiver of INITIALIZE is sent.
742#[derive(Clone, Copy, Debug, PartialEq, Eq)]
743pub enum InitialValue<'a> {
744    /// The literal of the item's own VALUE clause.
745    Value,
746    Replacing(&'a Operand),
747    /// SPACE, ZERO or NULL, by the item's category.
748    Default,
749}
750
751impl InitializeWith {
752    /// Rules 1c and 2 of INITIALIZE (Language Reference SC27-8713-03, pp. 352-353) for an
753    /// elementary item of `category`, which `has_value` when its entry has a VALUE clause; None when
754    /// the item is not a receiver.
755    pub fn initial_value(&self, category: Option<DataCategory>, has_value: bool) -> Option<InitialValue<'_>> {
756        let named = |c: DataCategory| category == Some(c);
757        if has_value && self.value.iter().any(|&c| named(c)) {
758            return Some(InitialValue::Value);
759        }
760        if let Some((_, by)) = self.replacing.iter().find(|(c, _)| named(*c)) {
761            return Some(InitialValue::Replacing(by));
762        }
763        (self.default || self.value.is_empty() && self.replacing.is_empty()).then_some(InitialValue::Default)
764    }
765}
766
767/// `target` is a data item or, for TALLYING alone, a function's value.
768#[derive(Clone, Debug, PartialEq, Eq)]
769pub struct Inspect {
770    pub target: Operand,
771    pub tallying: Vec<InspectPhrase>,
772    pub replacing: Vec<InspectPhrase>,
773    pub converting: Option<(Operand, Operand, Vec<Bound>)>,
774    pub pos: Pos,
775}
776
777#[derive(Clone, Debug, PartialEq, Eq)]
778pub struct Search {
779    pub table: Ref,
780    pub all: bool,
781    pub varying: Option<Ref>,
782    pub at_end: Option<Vec<Stmt>>,
783    pub whens: Vec<(Cond, Vec<Stmt>)>,
784    pub pos: Pos,
785}
786
787#[derive(Clone, Copy, Debug, PartialEq, Eq)]
788pub enum ExecKind {
789    Sql,
790    Cics,
791    Dli,
792    Other,
793}
794
795#[derive(Clone, Debug, PartialEq, Eq)]
796pub enum ExecArg {
797    Operand(Operand),
798    /// An argument that is not data: a paragraph for HANDLE CONDITION, or text that did not parse.
799    Text(String),
800}
801
802/// An EXEC ... END-EXEC block, read but not translated.
803#[derive(Clone, Debug, PartialEq, Eq)]
804pub struct ExecBlock {
805    pub kind: ExecKind,
806    /// The command: SELECT, INCLUDE, DECLARE CURSOR, LINK, SEND MAP and so on.
807    pub command: String,
808    /// CICS options, each with its argument.
809    pub options: Vec<(String, Option<ExecArg>)>,
810    /// SQL host variables and indicator variables.
811    pub host_variables: Vec<Ref>,
812    /// The typed SQL statement, for EXEC SQL.
813    pub sql: Option<crate::sql::Sql>,
814    pub text: String,
815    pub pos: Pos,
816}
817
818impl ExecBlock {
819    /// Whether the block only declares, so a precompiler turns it into data or nothing.
820    pub fn declarative(&self) -> bool {
821        self.kind == ExecKind::Sql
822            && matches!(self.command.as_str(), "INCLUDE" | "BEGIN DECLARE SECTION" | "END DECLARE SECTION" | "WHENEVER" | "DECLARE CURSOR" | "DECLARE TABLE" | "DECLARE STATEMENT")
823    }
824}
825
826/// The statements of an ON phrase (AT END, INVALID KEY, ...) and of its NOT ON phrase.
827#[derive(Clone, Debug, Default, PartialEq, Eq)]
828pub struct Handlers {
829    pub on: Option<Vec<Stmt>>,
830    pub not_on: Option<Vec<Stmt>>,
831}
832
833#[derive(Clone, Debug, PartialEq, Eq)]
834pub struct ReadStmt {
835    pub file: String,
836    /// READ NEXT or PREVIOUS: the next record by the key of reference, even under dynamic access.
837    pub next: bool,
838    pub previous: bool,
839    pub into: Option<Ref>,
840    /// READ ... KEY IS: the key of reference for a random read.
841    pub key: Option<Ref>,
842    pub at_end: Handlers,
843    pub invalid: Handlers,
844    pub pos: Pos,
845}
846
847/// What an ALPHABET clause relates its alphabet-name to.
848#[derive(Clone, Debug, PartialEq, Eq)]
849pub enum Alphabet {
850    Ebcdic,
851    Native,
852    Standard1,
853    Standard2,
854    /// A collating sequence of the program's own, lowest position first.
855    Literal(Vec<AlphabetEntry>),
856}
857
858impl Alphabet {
859    pub fn name(&self) -> &'static str {
860        match self {
861            Self::Ebcdic => "EBCDIC",
862            Self::Native => "NATIVE",
863            Self::Standard1 => "STANDARD-1",
864            Self::Standard2 => "STANDARD-2",
865            Self::Literal(_) => "literal",
866        }
867    }
868}
869
870/// One literal of an ALPHABET clause: its characters in successive positions, a THROUGH range of
871/// characters in successive positions, or characters that ALSO share one position.
872#[derive(Clone, Debug, PartialEq, Eq)]
873pub enum AlphabetEntry {
874    Literal(Literal),
875    Through(Literal, Literal),
876    Also(Vec<Literal>),
877}
878
879/// ENVIRONMENT DIVISION clauses beyond SELECT that the program's meaning depends on.
880#[derive(Clone, Debug, Default, PartialEq, Eq)]
881pub struct Environment {
882    /// SPECIAL-NAMES ALPHABET: each alphabet-name and what it is.
883    pub alphabets: Vec<(String, Alphabet)>,
884    /// OBJECT-COMPUTER PROGRAM COLLATING SEQUENCE.
885    pub collating_sequence: Option<String>,
886    /// I-O-CONTROL SAME RECORD AREA and SAME AREA clauses, each with the files it names.
887    pub same_record_areas: Vec<Vec<String>>,
888    pub same_areas: Vec<Vec<String>>,
889    /// SPECIAL-NAMES entries naming a printer channel, space suppression, a punch pocket, AFP, or
890    /// a device ACCEPT or DISPLAY takes: each mnemonic-name and its environment-name.
891    pub mnemonics: Vec<(String, String)>,
892    /// SOURCE-COMPUTER ... WITH DEBUGGING MODE: debugging lines and USE FOR DEBUGGING sections are
893    /// compiled rather than read as comments.
894    pub debugging_mode: bool,
895    /// SPECIAL-NAMES DECIMAL-POINT IS COMMA: the comma and the period exchange roles in PICTURE
896    /// character-strings, numeric literals and the arguments of NUMVAL and NUMVAL-C.
897    pub decimal_point_comma: bool,
898    /// SPECIAL-NAMES CURRENCY SIGN clauses in order; none means the symbol and value $.
899    pub currency: Vec<CurrencySign>,
900    /// SPECIAL-NAMES UPSI-0 to UPSI-7 entries, a contained program's being its container's.
901    pub switches: Vec<Switch>,
902    /// SPECIAL-NAMES CLASS clauses, a contained program's being its container's.
903    pub classes: Vec<ClassClause>,
904}
905
906/// SPECIAL-NAMES CLASS class-name IS: each literal, or the two ends of a THROUGH range.
907#[derive(Clone, Debug, PartialEq, Eq)]
908pub struct ClassClause {
909    pub name: String,
910    pub members: Vec<(Literal, Option<Literal>)>,
911    pub pos: Pos,
912}
913
914/// A SPECIAL-NAMES entry for an UPSI switch: UPSI-`number` [IS mnemonic-name] with the
915/// condition-names of its ON and OFF status.
916#[derive(Clone, Debug, PartialEq, Eq)]
917pub struct Switch {
918    pub number: u8,
919    pub mnemonic: Option<String>,
920    pub on: Option<String>,
921    pub off: Option<String>,
922    pub pos: Pos,
923}
924
925/// DISPLAY's UPON phrase: the name as written, and the environment-name it stands for, a
926/// SPECIAL-NAMES mnemonic-name's or the name itself.
927#[derive(Clone, Debug, PartialEq, Eq)]
928pub struct Upon {
929    pub name: String,
930    pub device: String,
931}
932
933/// A CURRENCY SIGN clause: the currency sign value, and the PICTURE symbol that stands for it.
934#[derive(Clone, Debug, PartialEq, Eq)]
935pub struct CurrencySign {
936    pub value: String,
937    pub symbol: char,
938    /// CURRENCY SIGN IS X'...': the literal's bytes, which the program's code page makes `value`,
939    /// and `symbol` too when there is no PICTURE SYMBOL (`symbol` is [`HEX_SYMBOL`] until then).
940    pub hex: Option<Vec<u8>>,
941}
942
943/// The symbol of a hexadecimal CURRENCY SIGN without PICTURE SYMBOL before its code page decodes it.
944pub const HEX_SYMBOL: char = '\0';
945
946#[derive(Clone, Debug, PartialEq, Eq)]
947pub enum Sorting {
948    Sort(SortStmt),
949    Release { record: Ref, from: Option<Operand>, pos: Pos },
950    Return { file: String, into: Option<Ref>, at_end: Handlers, pos: Pos },
951}
952
953/// SORT or MERGE of an SD file, or SORT of a table (no input or output then).
954#[derive(Clone, Debug, PartialEq, Eq)]
955pub struct SortStmt {
956    pub merge: bool,
957    /// The SD file, or the table.
958    pub subject: Ref,
959    /// Each key and whether it ascends, most significant first.
960    pub keys: Vec<(bool, Ref)>,
961    pub duplicates: bool,
962    pub collating: Option<String>,
963    pub input: Option<SortIo>,
964    pub output: Option<SortIo>,
965    pub pos: Pos,
966}
967
968/// USING or GIVING files, or an input or output procedure.
969#[derive(Clone, Debug, PartialEq, Eq)]
970pub enum SortIo {
971    Files(Vec<String>),
972    Procedure { from: ProcName, thru: Option<ProcName> },
973}
974
975
976/// JSON GENERATE (Language Reference SC27-8713-03, pp. 369-382).
977#[derive(Clone, Debug, PartialEq, Eq)]
978pub struct JsonGenerate {
979    pub receiver: Ref,
980    pub from: Ref,
981    pub count: Option<Ref>,
982    /// NAME OF item IS literal; `None` for OMITTED.
983    pub names: Vec<(Ref, Option<Literal>)>,
984    pub suppress: Vec<Suppression>,
985    pub converting: Vec<(Ref, JsonConversion)>,
986    pub indicating: Vec<NullIndicator>,
987    pub encoding: Option<Encoding>,
988    pub on_exception: Option<Vec<Stmt>>,
989    pub not_on_exception: Option<Vec<Stmt>>,
990    pub pos: Pos,
991}
992
993/// A SUPPRESS phrase: an item, or EVERY item of a class (`Some(true)` NUMERIC, `Some(false)`
994/// NONNUMERIC, `None` both) and, for XML GENERATE, of a form, suppressed always or only WHEN it
995/// equals one of the figurative constants.
996#[derive(Clone, Debug, PartialEq, Eq)]
997pub enum Suppression {
998    Item { item: Ref, when: Vec<Figurative> },
999    Every { numeric: Option<bool>, form: Option<XmlForm>, when: Vec<Figurative> },
1000}
1001
1002/// A value that stands for true (CONVERTING ... TO JSON BOOLEAN) or for null (INDICATING): a
1003/// condition-name, or a one-character literal.
1004#[derive(Clone, Debug, PartialEq, Eq)]
1005pub enum Marker {
1006    Condition(Ref),
1007    Literal(Literal),
1008}
1009
1010#[derive(Clone, Debug, PartialEq, Eq)]
1011pub enum JsonConversion {
1012    Boolean(Marker),
1013    Null(Figurative),
1014}
1015
1016/// INDICATING item IS JSON NULL USING a condition-name of the indicator, or a literal IN it.
1017#[derive(Clone, Debug, PartialEq, Eq)]
1018pub struct NullIndicator {
1019    pub item: Ref,
1020    pub marker: Marker,
1021    pub indicator: Option<Ref>,
1022}
1023
1024#[derive(Clone, Debug, PartialEq, Eq)]
1025pub enum Encoding {
1026    Ccsid(Operand),
1027    FromCodepage,
1028}
1029
1030/// JSON PARSE (Language Reference SC27-8713-03, pp. 382-396).
1031#[derive(Clone, Debug, PartialEq, Eq)]
1032pub struct JsonParse {
1033    pub source: Ref,
1034    pub into: Ref,
1035    pub detail: bool,
1036    /// IGNORING JSON NULL FOR an item, or `None` FOR ALL.
1037    pub ignoring: Vec<Option<Ref>>,
1038    /// INDICATING item IS JSON NULL USING values, with IN and the indicator for two literals.
1039    pub indicating: Vec<(Ref, Flag, Option<Ref>)>,
1040    pub encoding: Option<Encoding>,
1041    /// NAME OF item IS literal; `None` for OMITTED.
1042    pub names: Vec<(Ref, Option<Literal>)>,
1043    pub suppress: Vec<Ref>,
1044    pub converting: Vec<(Ref, ParseConversion)>,
1045    pub on_exception: Option<Vec<Stmt>>,
1046    pub not_on_exception: Option<Vec<Stmt>>,
1047    pub pos: Pos,
1048}
1049
1050/// What a USING phrase of JSON PARSE sets for true, or for null, and for false, or not null: a
1051/// condition-name set to true or to its WHEN SET TO FALSE value, one of two condition-names set to
1052/// true, or one of two literals moved in.
1053#[derive(Clone, Debug, PartialEq, Eq)]
1054pub enum Flag {
1055    Condition(Ref),
1056    Conditions(Ref, Ref),
1057    Literals(Literal, Literal),
1058}
1059
1060#[derive(Clone, Debug, PartialEq, Eq)]
1061pub enum ParseConversion {
1062    Boolean(Box<Flag>),
1063    Null(Figurative),
1064}
1065
1066/// XML GENERATE (Language Reference SC27-8713-03, pp. 484-494).
1067#[derive(Clone, Debug, PartialEq, Eq)]
1068pub struct XmlGenerate {
1069    pub receiver: Ref,
1070    pub from: Ref,
1071    pub count: Option<Ref>,
1072    /// WITH ENCODING: the document's CCSID.
1073    pub encoding: Option<Operand>,
1074    pub declaration: bool,
1075    pub attributes: bool,
1076    pub namespace: Option<Operand>,
1077    pub prefix: Option<Operand>,
1078    pub names: Vec<(Ref, Literal)>,
1079    pub types: Vec<(Ref, XmlForm)>,
1080    pub suppress: Vec<Suppression>,
1081    pub on_exception: Option<Vec<Stmt>>,
1082    pub not_on_exception: Option<Vec<Stmt>>,
1083    pub pos: Pos,
1084}
1085
1086/// How XML GENERATE expresses an item: as an attribute or an element, or as its parent's content.
1087#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1088pub enum XmlForm {
1089    Attribute,
1090    Element,
1091    Content,
1092}
1093
1094/// XML PARSE under XMLPARSE(XMLSS) (Language Reference SC27-8713-03, pp. 489-494).
1095#[derive(Clone, Debug, PartialEq, Eq)]
1096pub struct XmlParse {
1097    pub document: Ref,
1098    /// WITH ENCODING: the document's CCSID.
1099    pub encoding: Option<Operand>,
1100    pub returning_national: bool,
1101    pub procedure: ProcName,
1102    pub thru: Option<ProcName>,
1103    pub on_exception: Option<Vec<Stmt>>,
1104    pub not_on_exception: Option<Vec<Stmt>>,
1105    pub pos: Pos,
1106}