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