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, Copy, 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}
432
433#[derive(Clone, Debug, PartialEq, Eq)]
434pub enum Cond {
435    Rel(Expr, RelOp, Expr),
436    Class(Expr, Class),
437    Name(Ref),
438    /// After AND or OR, a bare name that is either a condition-name or the object of an
439    /// abbreviated relation; which one depends on what the name resolves to. `negated` is a NOT
440    /// the relational operator carries, as in NOT =.
441    NameOrRel { subject: Expr, op: RelOp, negated: bool, name: Ref },
442    Not(Box<Cond>),
443    And(Box<Cond>, Box<Cond>),
444    Or(Box<Cond>, Box<Cond>),
445}
446
447#[derive(Clone, Debug, PartialEq, Eq)]
448pub struct Target {
449    pub r: Ref,
450    pub rounded: bool,
451}
452
453#[derive(Clone, Debug, PartialEq, Eq)]
454pub struct Varying {
455    pub var: Ref,
456    pub from: Expr,
457    pub by: Expr,
458    pub until: Cond,
459}
460
461#[derive(Clone, Debug, PartialEq, Eq)]
462pub enum Loop {
463    Once,
464    Times(Expr),
465    Until { cond: Cond, test_after: bool },
466    /// VARYING, and each AFTER phrase, outermost first.
467    Varying { varying: Box<Varying>, after: Vec<Varying>, test_after: bool },
468}
469
470#[derive(Clone, Debug, PartialEq, Eq)]
471pub enum Stmt {
472    Move { from: Operand, to: Vec<Ref>, pos: Pos },
473    Compute { targets: Vec<Target>, expr: Expr, size_error: Option<SizeError>, pos: Pos },
474    /// ADD, SUBTRACT, MULTIPLY and DIVIDE, reduced to their arithmetic.
475    Arith(Box<Arith>),
476    /// MOVE, ADD or SUBTRACT CORRESPONDING, which the compiler expands into a MOVE per pair of
477    /// corresponding items, or one ADD or SUBTRACT over them all.
478    Corresponding(Box<Corresponding>),
479    If { cond: Cond, then: Vec<Stmt>, otherwise: Vec<Stmt>, pos: Pos },
480    PerformInline { body: Vec<Stmt>, repeat: Loop, pos: Pos },
481    PerformProc { from: ProcName, thru: Option<ProcName>, repeat: Loop, pos: Pos },
482    Evaluate { subjects: Vec<Subject>, whens: Vec<When>, other: Vec<Stmt>, pos: Pos },
483    Display { items: Vec<Operand>, upon: Option<Upon>, no_advancing: bool, pos: Pos },
484    Open { files: Vec<(OpenMode, String)>, pos: Pos },
485    Close { files: Vec<(String, Option<Closing>)>, pos: Pos },
486    Read(Box<ReadStmt>),
487    Write { record: Ref, from: Option<Operand>, advancing: Option<Advancing>, invalid: Handlers, end_of_page: Handlers, pos: Pos },
488    Rewrite { record: Ref, from: Option<Operand>, invalid: Handlers, pos: Pos },
489    Delete { file: String, invalid: Handlers, pos: Pos },
490    Start { file: String, key: Option<(RelOp, Ref)>, invalid: Handlers, pos: Pos },
491    /// INITIALIZE; `with` holds its FILLER, VALUE, REPLACING and DEFAULT phrases, None without any.
492    Initialize { targets: Vec<Ref>, with: Option<Box<InitializeWith>>, pos: Pos },
493    /// GO TO; with no target, the altered GO TO that only an ALTER gives one.
494    GoTo { target: Option<ProcName>, pos: Pos },
495    /// GO TO ... DEPENDING ON: the procedure the item's value numbers, or on when none does.
496    GoToDepending { targets: Vec<ProcName>, on: Ref, pos: Pos },
497    /// ALTER: each paragraph whose GO TO is to go instead to the procedure paired with it.
498    Alter { pairs: Vec<(ProcName, ProcName)>, pos: Pos },
499    /// ENTRY: where a CALL of `name` begins, and the LINKAGE items its USING list addresses.
500    Entry { name: String, using: Vec<Param>, pos: Pos },
501    Goback { pos: Pos },
502    /// EXIT PROGRAM: returns from a called program; in the first program it does nothing.
503    ExitProgram { pos: Pos },
504    Call(Box<Call>),
505    Cancel { targets: Vec<Operand>, pos: Pos },
506    Set { set: SetStmt, pos: Pos },
507    Accept { target: Ref, from: AcceptFrom, pos: Pos },
508    String(Box<StringStmt>),
509    Unstring(Box<Unstring>),
510    Inspect(Box<Inspect>),
511    Search(Box<Search>),
512    /// NEXT SENTENCE: control passes to the statement after the next separator period.
513    NextSentence,
514    /// A separator period in the PROCEDURE DIVISION: where NEXT SENTENCE resumes.
515    SentenceEnd,
516    Exec(Box<ExecBlock>),
517    Report(Box<crate::report::ReportStmt>),
518    Invoke(Box<Invoke>),
519    JsonGenerate(Box<JsonGenerate>),
520    JsonParse(Box<JsonParse>),
521    XmlParse(Box<XmlParse>),
522    XmlGenerate(Box<XmlGenerate>),
523    ExitMethod { pos: Pos },
524    Sorting(Box<Sorting>),
525    StopRun { pos: Pos },
526    Continue,
527    Exit { kind: ExitKind, pos: Pos },
528}
529
530#[derive(Clone, Debug, PartialEq, Eq)]
531pub struct SizeError {
532    pub on: Vec<Stmt>,
533    pub not_on: Vec<Stmt>,
534}
535
536#[derive(Clone, Copy, Debug, PartialEq, Eq)]
537pub enum ArithVerb {
538    Add,
539    Subtract,
540    Multiply,
541    Divide,
542}
543
544/// One arithmetic statement: `targets` each receive `expr`, which is written in terms of the
545/// statement's operands and, for the forms without GIVING, the target itself (`Operand::Ref` of
546/// the target, placed by the parser).
547#[derive(Clone, Debug, PartialEq, Eq)]
548pub struct Arith {
549    pub verb: ArithVerb,
550    pub computations: Vec<(Target, Expr)>,
551    pub remainder: Option<(Target, Expr, Expr)>,
552    pub size_error: Option<SizeError>,
553    pub pos: Pos,
554}
555
556#[derive(Clone, Copy, Debug, PartialEq, Eq)]
557pub enum CorrespondingVerb {
558    Move,
559    Add,
560    Subtract,
561}
562
563/// `from` is the sending group and `to` the receiving one; `rounded` and `size_error` are ADD's
564/// and SUBTRACT's.
565#[derive(Clone, Debug, PartialEq, Eq)]
566pub struct Corresponding {
567    pub verb: CorrespondingVerb,
568    pub from: Ref,
569    pub to: Ref,
570    pub rounded: bool,
571    pub size_error: Option<SizeError>,
572    pub pos: Pos,
573}
574
575#[derive(Clone, Copy, Debug, PartialEq, Eq)]
576pub enum ArgMode {
577    Reference,
578    Content,
579    Value,
580}
581
582/// One CALL argument; `value` is None for OMITTED.
583#[derive(Clone, Debug, PartialEq, Eq)]
584pub struct Arg {
585    pub mode: ArgMode,
586    pub value: Option<Operand>,
587}
588
589#[derive(Clone, Debug, PartialEq, Eq)]
590pub struct Call {
591    pub target: Operand,
592    pub using: Vec<Arg>,
593    pub returning: Option<Ref>,
594    pub on_exception: Option<Vec<Stmt>>,
595    pub not_on_exception: Option<Vec<Stmt>>,
596    pub pos: Pos,
597}
598
599#[derive(Clone, Debug, PartialEq, Eq)]
600pub enum SetStmt {
601    ConditionTrue(Vec<Ref>),
602    /// SET condition-names TO FALSE: each conditional variable gets its WHEN SET TO FALSE value.
603    ConditionFalse(Vec<Ref>),
604    /// SET targets TO value: an index or integer to a number, a pointer to ADDRESS OF, NULL or another pointer.
605    To { targets: Vec<Ref>, value: Operand },
606    /// SET procedure-pointers or function-pointers TO ENTRY, the entry named by a literal or identifier.
607    Entry { targets: Vec<Ref>, entry: Operand },
608    /// SET ADDRESS OF targets TO pointer.
609    AddressOf { targets: Vec<Ref>, value: Operand },
610    UpDown { targets: Vec<Ref>, down: bool, by: Expr },
611}
612
613#[derive(Clone, Debug, PartialEq, Eq)]
614pub enum Delimiter {
615    Size,
616    By(Operand),
617}
618
619#[derive(Clone, Debug, PartialEq, Eq)]
620pub struct StringStmt {
621    pub sources: Vec<(Operand, Delimiter)>,
622    pub into: Ref,
623    pub pointer: Option<Ref>,
624    pub on_overflow: Option<Vec<Stmt>>,
625    pub not_on_overflow: Option<Vec<Stmt>>,
626    pub pos: Pos,
627}
628
629#[derive(Clone, Debug, PartialEq, Eq)]
630pub struct UnstringInto {
631    pub target: Ref,
632    pub delimiter_in: Option<Ref>,
633    pub count_in: Option<Ref>,
634}
635
636#[derive(Clone, Debug, PartialEq, Eq)]
637pub struct Unstring {
638    pub source: Ref,
639    /// Each delimiter, and whether ALL makes a run of it one delimiter.
640    pub delimiters: Vec<(bool, Operand)>,
641    pub into: Vec<UnstringInto>,
642    pub pointer: Option<Ref>,
643    pub tallying: Option<Ref>,
644    pub on_overflow: Option<Vec<Stmt>>,
645    pub not_on_overflow: Option<Vec<Stmt>>,
646    pub pos: Pos,
647}
648
649/// BEFORE or AFTER INITIAL value: where in the inspected item a phrase applies.
650#[derive(Clone, Debug, PartialEq, Eq)]
651pub struct Bound {
652    pub after: bool,
653    pub value: Operand,
654}
655
656pub use rt::vocab::InspectMode;
657
658/// One TALLYING or REPLACING phrase. `pattern` is None for CHARACTERS; `by` is None for TALLYING.
659#[derive(Clone, Debug, PartialEq, Eq)]
660pub struct InspectPhrase {
661    pub mode: InspectMode,
662    pub pattern: Option<Operand>,
663    pub by: Option<Operand>,
664    pub counter: Option<Ref>,
665    pub bounds: Vec<Bound>,
666}
667
668/// The categories INITIALIZE's VALUE and REPLACING phrases name (Language Reference SC27-8713-03,
669/// p. 350); EGCS is DBCS.
670#[derive(Clone, Copy, Debug, PartialEq, Eq)]
671pub enum DataCategory {
672    Alphabetic,
673    Alphanumeric,
674    AlphanumericEdited,
675    Dbcs,
676    National,
677    NationalEdited,
678    Numeric,
679    NumericEdited,
680    Utf8,
681}
682
683impl DataCategory {
684    pub const ALL: [Self; 9] = [
685        Self::Alphabetic,
686        Self::Alphanumeric,
687        Self::AlphanumericEdited,
688        Self::Dbcs,
689        Self::National,
690        Self::NationalEdited,
691        Self::Numeric,
692        Self::NumericEdited,
693        Self::Utf8,
694    ];
695
696    pub fn from_word(word: &str) -> Option<Self> {
697        Some(match word {
698            "ALPHABETIC" => Self::Alphabetic,
699            "ALPHANUMERIC" => Self::Alphanumeric,
700            "ALPHANUMERIC-EDITED" => Self::AlphanumericEdited,
701            "DBCS" | "EGCS" => Self::Dbcs,
702            "NATIONAL" => Self::National,
703            "NATIONAL-EDITED" => Self::NationalEdited,
704            "NUMERIC" => Self::Numeric,
705            "NUMERIC-EDITED" => Self::NumericEdited,
706            "UTF-8" => Self::Utf8,
707            _ => return None,
708        })
709    }
710
711    pub fn word(self) -> &'static str {
712        match self {
713            Self::Alphabetic => "ALPHABETIC",
714            Self::Alphanumeric => "ALPHANUMERIC",
715            Self::AlphanumericEdited => "ALPHANUMERIC-EDITED",
716            Self::Dbcs => "DBCS",
717            Self::National => "NATIONAL",
718            Self::NationalEdited => "NATIONAL-EDITED",
719            Self::Numeric => "NUMERIC",
720            Self::NumericEdited => "NUMERIC-EDITED",
721            Self::Utf8 => "UTF-8",
722        }
723    }
724}
725
726/// INITIALIZE's phrases. `value` lists the VALUE phrase's categories, every one for ALL TO VALUE.
727#[derive(Clone, Debug, Default, PartialEq, Eq)]
728pub struct InitializeWith {
729    pub filler: bool,
730    pub value: Vec<DataCategory>,
731    pub replacing: Vec<(DataCategory, Operand)>,
732    pub default: bool,
733}
734
735/// What an elementary receiver of INITIALIZE is sent.
736#[derive(Clone, Copy, Debug, PartialEq, Eq)]
737pub enum InitialValue<'a> {
738    /// The literal of the item's own VALUE clause.
739    Value,
740    Replacing(&'a Operand),
741    /// SPACE, ZERO or NULL, by the item's category.
742    Default,
743}
744
745impl InitializeWith {
746    /// Rules 1c and 2 of INITIALIZE (Language Reference SC27-8713-03, pp. 352-353) for an
747    /// elementary item of `category`, which `has_value` when its entry has a VALUE clause; None when
748    /// the item is not a receiver.
749    pub fn initial_value(&self, category: Option<DataCategory>, has_value: bool) -> Option<InitialValue<'_>> {
750        let named = |c: DataCategory| category == Some(c);
751        if has_value && self.value.iter().any(|&c| named(c)) {
752            return Some(InitialValue::Value);
753        }
754        if let Some((_, by)) = self.replacing.iter().find(|(c, _)| named(*c)) {
755            return Some(InitialValue::Replacing(by));
756        }
757        (self.default || self.value.is_empty() && self.replacing.is_empty()).then_some(InitialValue::Default)
758    }
759}
760
761/// `target` is a data item or, for TALLYING alone, a function's value.
762#[derive(Clone, Debug, PartialEq, Eq)]
763pub struct Inspect {
764    pub target: Operand,
765    pub tallying: Vec<InspectPhrase>,
766    pub replacing: Vec<InspectPhrase>,
767    pub converting: Option<(Operand, Operand, Vec<Bound>)>,
768    pub pos: Pos,
769}
770
771#[derive(Clone, Debug, PartialEq, Eq)]
772pub struct Search {
773    pub table: Ref,
774    pub all: bool,
775    pub varying: Option<Ref>,
776    pub at_end: Option<Vec<Stmt>>,
777    pub whens: Vec<(Cond, Vec<Stmt>)>,
778    pub pos: Pos,
779}
780
781#[derive(Clone, Copy, Debug, PartialEq, Eq)]
782pub enum ExecKind {
783    Sql,
784    Cics,
785    Dli,
786    Other,
787}
788
789#[derive(Clone, Debug, PartialEq, Eq)]
790pub enum ExecArg {
791    Operand(Operand),
792    /// An argument that is not data: a paragraph for HANDLE CONDITION, or text that did not parse.
793    Text(String),
794}
795
796/// An EXEC ... END-EXEC block, read but not translated.
797#[derive(Clone, Debug, PartialEq, Eq)]
798pub struct ExecBlock {
799    pub kind: ExecKind,
800    /// The command: SELECT, INCLUDE, DECLARE CURSOR, LINK, SEND MAP and so on.
801    pub command: String,
802    /// CICS options, each with its argument.
803    pub options: Vec<(String, Option<ExecArg>)>,
804    /// SQL host variables and indicator variables.
805    pub host_variables: Vec<Ref>,
806    /// The typed SQL statement, for EXEC SQL.
807    pub sql: Option<crate::sql::Sql>,
808    pub text: String,
809    pub pos: Pos,
810}
811
812impl ExecBlock {
813    /// Whether the block only declares, so a precompiler turns it into data or nothing.
814    pub fn declarative(&self) -> bool {
815        self.kind == ExecKind::Sql
816            && matches!(self.command.as_str(), "INCLUDE" | "BEGIN DECLARE SECTION" | "END DECLARE SECTION" | "WHENEVER" | "DECLARE CURSOR" | "DECLARE TABLE" | "DECLARE STATEMENT")
817    }
818}
819
820/// The statements of an ON phrase (AT END, INVALID KEY, ...) and of its NOT ON phrase.
821#[derive(Clone, Debug, Default, PartialEq, Eq)]
822pub struct Handlers {
823    pub on: Option<Vec<Stmt>>,
824    pub not_on: Option<Vec<Stmt>>,
825}
826
827#[derive(Clone, Debug, PartialEq, Eq)]
828pub struct ReadStmt {
829    pub file: String,
830    /// READ NEXT or PREVIOUS: the next record by the key of reference, even under dynamic access.
831    pub next: bool,
832    pub previous: bool,
833    pub into: Option<Ref>,
834    /// READ ... KEY IS: the key of reference for a random read.
835    pub key: Option<Ref>,
836    pub at_end: Handlers,
837    pub invalid: Handlers,
838    pub pos: Pos,
839}
840
841/// What an ALPHABET clause relates its alphabet-name to.
842#[derive(Clone, Debug, PartialEq, Eq)]
843pub enum Alphabet {
844    Ebcdic,
845    Native,
846    Standard1,
847    Standard2,
848    /// A collating sequence of the program's own, lowest position first.
849    Literal(Vec<AlphabetEntry>),
850}
851
852impl Alphabet {
853    pub fn name(&self) -> &'static str {
854        match self {
855            Self::Ebcdic => "EBCDIC",
856            Self::Native => "NATIVE",
857            Self::Standard1 => "STANDARD-1",
858            Self::Standard2 => "STANDARD-2",
859            Self::Literal(_) => "literal",
860        }
861    }
862}
863
864/// One literal of an ALPHABET clause: its characters in successive positions, a THROUGH range of
865/// characters in successive positions, or characters that ALSO share one position.
866#[derive(Clone, Debug, PartialEq, Eq)]
867pub enum AlphabetEntry {
868    Literal(Literal),
869    Through(Literal, Literal),
870    Also(Vec<Literal>),
871}
872
873/// ENVIRONMENT DIVISION clauses beyond SELECT that the program's meaning depends on.
874#[derive(Clone, Debug, Default, PartialEq, Eq)]
875pub struct Environment {
876    /// SPECIAL-NAMES ALPHABET: each alphabet-name and what it is.
877    pub alphabets: Vec<(String, Alphabet)>,
878    /// OBJECT-COMPUTER PROGRAM COLLATING SEQUENCE.
879    pub collating_sequence: Option<String>,
880    /// I-O-CONTROL SAME RECORD AREA and SAME AREA clauses, each with the files it names.
881    pub same_record_areas: Vec<Vec<String>>,
882    pub same_areas: Vec<Vec<String>>,
883    /// SPECIAL-NAMES entries naming a printer channel, space suppression, a punch pocket, AFP, or
884    /// a device ACCEPT or DISPLAY takes: each mnemonic-name and its environment-name.
885    pub mnemonics: Vec<(String, String)>,
886    /// SOURCE-COMPUTER ... WITH DEBUGGING MODE: debugging lines and USE FOR DEBUGGING sections are
887    /// compiled rather than read as comments.
888    pub debugging_mode: bool,
889    /// SPECIAL-NAMES DECIMAL-POINT IS COMMA: the comma and the period exchange roles in PICTURE
890    /// character-strings, numeric literals and the arguments of NUMVAL and NUMVAL-C.
891    pub decimal_point_comma: bool,
892    /// SPECIAL-NAMES CURRENCY SIGN clauses in order; none means the symbol and value $.
893    pub currency: Vec<CurrencySign>,
894}
895
896/// DISPLAY's UPON phrase: the name as written, and the environment-name it stands for, a
897/// SPECIAL-NAMES mnemonic-name's or the name itself.
898#[derive(Clone, Debug, PartialEq, Eq)]
899pub struct Upon {
900    pub name: String,
901    pub device: String,
902}
903
904/// A CURRENCY SIGN clause: the currency sign value, and the PICTURE symbol that stands for it.
905#[derive(Clone, Debug, PartialEq, Eq)]
906pub struct CurrencySign {
907    pub value: String,
908    pub symbol: char,
909    /// CURRENCY SIGN IS X'...': the literal's bytes, which the program's code page makes `value`,
910    /// and `symbol` too when there is no PICTURE SYMBOL (`symbol` is [`HEX_SYMBOL`] until then).
911    pub hex: Option<Vec<u8>>,
912}
913
914/// The symbol of a hexadecimal CURRENCY SIGN without PICTURE SYMBOL before its code page decodes it.
915pub const HEX_SYMBOL: char = '\0';
916
917#[derive(Clone, Debug, PartialEq, Eq)]
918pub enum Sorting {
919    Sort(SortStmt),
920    Release { record: Ref, from: Option<Operand>, pos: Pos },
921    Return { file: String, into: Option<Ref>, at_end: Handlers, pos: Pos },
922}
923
924/// SORT or MERGE of an SD file, or SORT of a table (no input or output then).
925#[derive(Clone, Debug, PartialEq, Eq)]
926pub struct SortStmt {
927    pub merge: bool,
928    /// The SD file, or the table.
929    pub subject: Ref,
930    /// Each key and whether it ascends, most significant first.
931    pub keys: Vec<(bool, Ref)>,
932    pub duplicates: bool,
933    pub collating: Option<String>,
934    pub input: Option<SortIo>,
935    pub output: Option<SortIo>,
936    pub pos: Pos,
937}
938
939/// USING or GIVING files, or an input or output procedure.
940#[derive(Clone, Debug, PartialEq, Eq)]
941pub enum SortIo {
942    Files(Vec<String>),
943    Procedure { from: ProcName, thru: Option<ProcName> },
944}
945
946
947/// JSON GENERATE (Language Reference SC27-8713-03, pp. 369-382).
948#[derive(Clone, Debug, PartialEq, Eq)]
949pub struct JsonGenerate {
950    pub receiver: Ref,
951    pub from: Ref,
952    pub count: Option<Ref>,
953    /// NAME OF item IS literal; `None` for OMITTED.
954    pub names: Vec<(Ref, Option<Literal>)>,
955    pub suppress: Vec<Suppression>,
956    pub converting: Vec<(Ref, JsonConversion)>,
957    pub indicating: Vec<NullIndicator>,
958    pub encoding: Option<Encoding>,
959    pub on_exception: Option<Vec<Stmt>>,
960    pub not_on_exception: Option<Vec<Stmt>>,
961    pub pos: Pos,
962}
963
964/// A SUPPRESS phrase: an item, or EVERY item of a class (`Some(true)` NUMERIC, `Some(false)`
965/// NONNUMERIC, `None` both) and, for XML GENERATE, of a form, suppressed always or only WHEN it
966/// equals one of the figurative constants.
967#[derive(Clone, Debug, PartialEq, Eq)]
968pub enum Suppression {
969    Item { item: Ref, when: Vec<Figurative> },
970    Every { numeric: Option<bool>, form: Option<XmlForm>, when: Vec<Figurative> },
971}
972
973/// A value that stands for true (CONVERTING ... TO JSON BOOLEAN) or for null (INDICATING): a
974/// condition-name, or a one-character literal.
975#[derive(Clone, Debug, PartialEq, Eq)]
976pub enum Marker {
977    Condition(Ref),
978    Literal(Literal),
979}
980
981#[derive(Clone, Debug, PartialEq, Eq)]
982pub enum JsonConversion {
983    Boolean(Marker),
984    Null(Figurative),
985}
986
987/// INDICATING item IS JSON NULL USING a condition-name of the indicator, or a literal IN it.
988#[derive(Clone, Debug, PartialEq, Eq)]
989pub struct NullIndicator {
990    pub item: Ref,
991    pub marker: Marker,
992    pub indicator: Option<Ref>,
993}
994
995#[derive(Clone, Debug, PartialEq, Eq)]
996pub enum Encoding {
997    Ccsid(Operand),
998    FromCodepage,
999}
1000
1001/// JSON PARSE (Language Reference SC27-8713-03, pp. 382-396).
1002#[derive(Clone, Debug, PartialEq, Eq)]
1003pub struct JsonParse {
1004    pub source: Ref,
1005    pub into: Ref,
1006    pub detail: bool,
1007    /// IGNORING JSON NULL FOR an item, or `None` FOR ALL.
1008    pub ignoring: Vec<Option<Ref>>,
1009    /// INDICATING item IS JSON NULL USING values, with IN and the indicator for two literals.
1010    pub indicating: Vec<(Ref, Flag, Option<Ref>)>,
1011    pub encoding: Option<Encoding>,
1012    /// NAME OF item IS literal; `None` for OMITTED.
1013    pub names: Vec<(Ref, Option<Literal>)>,
1014    pub suppress: Vec<Ref>,
1015    pub converting: Vec<(Ref, ParseConversion)>,
1016    pub on_exception: Option<Vec<Stmt>>,
1017    pub not_on_exception: Option<Vec<Stmt>>,
1018    pub pos: Pos,
1019}
1020
1021/// What a USING phrase of JSON PARSE sets for true, or for null, and for false, or not null: a
1022/// condition-name set to true or to its WHEN SET TO FALSE value, one of two condition-names set to
1023/// true, or one of two literals moved in.
1024#[derive(Clone, Debug, PartialEq, Eq)]
1025pub enum Flag {
1026    Condition(Ref),
1027    Conditions(Ref, Ref),
1028    Literals(Literal, Literal),
1029}
1030
1031#[derive(Clone, Debug, PartialEq, Eq)]
1032pub enum ParseConversion {
1033    Boolean(Box<Flag>),
1034    Null(Figurative),
1035}
1036
1037/// XML GENERATE (Language Reference SC27-8713-03, pp. 484-494).
1038#[derive(Clone, Debug, PartialEq, Eq)]
1039pub struct XmlGenerate {
1040    pub receiver: Ref,
1041    pub from: Ref,
1042    pub count: Option<Ref>,
1043    /// WITH ENCODING: the document's CCSID.
1044    pub encoding: Option<Operand>,
1045    pub declaration: bool,
1046    pub attributes: bool,
1047    pub namespace: Option<Operand>,
1048    pub prefix: Option<Operand>,
1049    pub names: Vec<(Ref, Literal)>,
1050    pub types: Vec<(Ref, XmlForm)>,
1051    pub suppress: Vec<Suppression>,
1052    pub on_exception: Option<Vec<Stmt>>,
1053    pub not_on_exception: Option<Vec<Stmt>>,
1054    pub pos: Pos,
1055}
1056
1057/// How XML GENERATE expresses an item: as an attribute or an element, or as its parent's content.
1058#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1059pub enum XmlForm {
1060    Attribute,
1061    Element,
1062    Content,
1063}
1064
1065/// XML PARSE under XMLPARSE(XMLSS) (Language Reference SC27-8713-03, pp. 489-494).
1066#[derive(Clone, Debug, PartialEq, Eq)]
1067pub struct XmlParse {
1068    pub document: Ref,
1069    /// WITH ENCODING: the document's CCSID.
1070    pub encoding: Option<Operand>,
1071    pub returning_national: bool,
1072    pub procedure: ProcName,
1073    pub thru: Option<ProcName>,
1074    pub on_exception: Option<Vec<Stmt>>,
1075    pub not_on_exception: Option<Vec<Stmt>>,
1076    pub pos: Pos,
1077}