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