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