Skip to main content

ironwork_syntax/
ast.rs

1use crate::Pos;
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub struct Program {
5    pub id: String,
6    /// Options from CBL and PROCESS cards, in the order written.
7    pub options: Vec<String>,
8    /// PROGRAM-ID ... IS INITIAL: WORKING-STORAGE starts afresh on every CALL.
9    pub initial: bool,
10    pub recursive: bool,
11    pub working_storage: Vec<DataEntry>,
12    /// LOCAL-STORAGE: fresh for every activation of the program.
13    pub local_storage: Vec<DataEntry>,
14    pub linkage: Vec<DataEntry>,
15    /// PROCEDURE DIVISION USING: the LINKAGE items the caller's arguments address.
16    pub using: Vec<Param>,
17    pub returning: Option<String>,
18    pub paragraphs: Vec<Paragraph>,
19    /// Files declared by SELECT and described by FD, with their record descriptions.
20    pub files: Vec<FileDecl>,
21    /// The program's source file, then each COPY member, as positions index them.
22    pub sources: Vec<String>,
23    /// EXEC blocks in the DATA DIVISION: SQL declarations, cursors and DECLARE SECTION markers.
24    pub exec_declarations: Vec<ExecBlock>,
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct Param {
29    pub by_value: bool,
30    pub name: String,
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum Organization {
35    Sequential,
36    LineSequential,
37    Indexed,
38    Relative,
39}
40
41#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
42pub enum Access {
43    #[default]
44    Sequential,
45    Random,
46    Dynamic,
47}
48
49#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct FileDecl {
51    pub name: String,
52    /// The DD name ASSIGN gives, with any `UT-S-` style prefix removed.
53    pub assign: String,
54    pub organization: Organization,
55    pub access: Access,
56    pub record_key: Option<Ref>,
57    /// ALTERNATE RECORD KEY items, and whether each allows duplicates.
58    pub alternate_keys: Vec<(Ref, bool)>,
59    pub relative_key: Option<Ref>,
60    pub optional: bool,
61    pub status: Option<Ref>,
62    /// RECORDING MODE: F, V, U or S.
63    pub recording: Option<char>,
64    pub record_min: Option<u32>,
65    pub record_max: Option<u32>,
66    pub records: Vec<DataEntry>,
67    pub pos: Pos,
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub enum OpenMode {
72    Input,
73    Output,
74    Extend,
75    InputOutput,
76}
77
78#[derive(Clone, Debug, PartialEq, Eq)]
79pub enum Advancing {
80    Lines { before: bool, count: Expr },
81    Page { before: bool },
82}
83
84#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
85pub enum Usage {
86    #[default]
87    Display,
88    Binary,
89    /// COMP-5: binary, never truncated to the PICTURE.
90    NativeBinary,
91    Packed,
92    Float1,
93    Float2,
94    National,
95    Pointer,
96    Index,
97}
98
99#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub enum SignPosition {
101    Leading,
102    Trailing,
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq)]
106pub struct SignClause {
107    pub position: SignPosition,
108    pub separate: bool,
109}
110
111#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct DataEntry {
113    pub level: u8,
114    /// None for FILLER or an unnamed entry.
115    pub name: Option<String>,
116    pub picture: Option<String>,
117    /// None when no USAGE is written; the item then inherits its group's.
118    pub usage: Option<Usage>,
119    pub value: Option<Literal>,
120    pub redefines: Option<String>,
121    /// OCCURS: the number of occurrences, or the most of them for OCCURS DEPENDING ON.
122    pub occurs: Option<u32>,
123    /// OCCURS ... DEPENDING ON: the item that holds how many occurrences there are.
124    pub depending_on: Option<Ref>,
125    pub sign: Option<SignClause>,
126    pub justified: bool,
127    pub sync: bool,
128    pub blank_when_zero: bool,
129    /// OCCURS ... INDEXED BY: the index names the table declares.
130    pub indexed_by: Vec<String>,
131    /// OCCURS ... ASCENDING/DESCENDING KEY: each key and whether it ascends, for SEARCH ALL.
132    pub keys: Vec<(bool, Ref)>,
133    /// Level 88: the values that make the condition true.
134    /// Each value, or the low and high ends of a THRU range.
135    pub condition_values: Vec<(Literal, Option<Literal>)>,
136    pub pos: Pos,
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub enum Figurative {
141    Zero,
142    Space,
143    HighValue,
144    LowValue,
145    Quote,
146    Null,
147}
148
149#[derive(Clone, Debug, PartialEq, Eq)]
150pub enum Literal {
151    Alnum(String),
152    Hex(Vec<u8>),
153    National(String),
154    /// As written: optional sign, digits, optional decimal point.
155    Number(String),
156    Figurative(Figurative),
157    All(Box<Literal>),
158}
159
160/// A paragraph, or a section header holding the statements before the section's first paragraph.
161#[derive(Clone, Debug, PartialEq, Eq)]
162pub struct Paragraph {
163    pub name: String,
164    pub statements: Vec<Stmt>,
165    /// The section the paragraph belongs to, or the section's own name for its header.
166    pub section: Option<String>,
167    pub is_section: bool,
168    pub pos: Pos,
169}
170
171/// A procedure name as written, with the section that qualifies it.
172#[derive(Clone, Debug, PartialEq, Eq)]
173pub struct ProcName {
174    pub name: String,
175    pub section: Option<String>,
176}
177
178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
179pub enum ExitKind {
180    Plain,
181    Paragraph,
182    Section,
183    Perform,
184    PerformCycle,
185}
186
187#[derive(Clone, Debug, PartialEq, Eq)]
188pub enum Subject {
189    Bool(bool),
190    Expr(Expr),
191    Cond(Cond),
192}
193
194#[derive(Clone, Debug, PartialEq, Eq)]
195pub enum Object {
196    Any,
197    Bool(bool),
198    Cond(Cond),
199    Value { not: bool, from: Expr, thru: Option<Expr> },
200}
201
202/// One WHEN group: any of its alternatives, each with an object per subject, selects the body.
203#[derive(Clone, Debug, PartialEq, Eq)]
204pub struct When {
205    pub alternatives: Vec<Vec<Object>>,
206    pub body: Vec<Stmt>,
207}
208
209#[derive(Clone, Debug, PartialEq, Eq)]
210pub struct Ref {
211    pub name: String,
212    pub qualifiers: Vec<String>,
213    pub subscripts: Vec<Expr>,
214    pub refmod: Option<RefMod>,
215    pub pos: Pos,
216}
217
218#[derive(Clone, Debug, PartialEq, Eq)]
219pub struct RefMod {
220    pub start: Box<Expr>,
221    pub length: Option<Box<Expr>>,
222}
223
224#[derive(Clone, Debug, PartialEq, Eq)]
225pub struct FunctionCall {
226    pub name: String,
227    pub args: Vec<Expr>,
228    /// A keyword argument, as in FUNCTION TRIM(X LEADING).
229    pub modifier: Option<String>,
230    pub refmod: Option<RefMod>,
231    pub pos: Pos,
232}
233
234#[derive(Clone, Debug, PartialEq, Eq)]
235pub enum Operand {
236    Ref(Ref),
237    Literal(Literal),
238    Function(FunctionCall),
239    LengthOf(Ref),
240    AddressOf(Ref),
241}
242
243#[derive(Clone, Copy, Debug, PartialEq, Eq)]
244pub enum BinOp {
245    Add,
246    Sub,
247    Mul,
248    Div,
249    Pow,
250}
251
252#[derive(Clone, Debug, PartialEq, Eq)]
253pub enum Expr {
254    Operand(Operand),
255    Neg(Box<Expr>),
256    Bin(Box<Expr>, BinOp, Box<Expr>),
257}
258
259#[derive(Clone, Copy, Debug, PartialEq, Eq)]
260pub enum RelOp {
261    Eq,
262    Ne,
263    Lt,
264    Le,
265    Gt,
266    Ge,
267}
268
269#[derive(Clone, Copy, Debug, PartialEq, Eq)]
270pub enum Class {
271    Numeric,
272    Alphabetic,
273    Positive,
274    Negative,
275    Zero,
276}
277
278#[derive(Clone, Debug, PartialEq, Eq)]
279pub enum Cond {
280    Rel(Expr, RelOp, Expr),
281    Class(Expr, Class),
282    Name(Ref),
283    /// After AND or OR, a bare name that is either a condition-name or the object of an
284    /// abbreviated relation; which one depends on what the name resolves to.
285    NameOrRel { subject: Expr, op: RelOp, name: Ref },
286    Not(Box<Cond>),
287    And(Box<Cond>, Box<Cond>),
288    Or(Box<Cond>, Box<Cond>),
289}
290
291#[derive(Clone, Debug, PartialEq, Eq)]
292pub struct Target {
293    pub r: Ref,
294    pub rounded: bool,
295}
296
297#[derive(Clone, Debug, PartialEq, Eq)]
298pub struct Varying {
299    pub var: Ref,
300    pub from: Expr,
301    pub by: Expr,
302    pub until: Cond,
303}
304
305#[derive(Clone, Debug, PartialEq, Eq)]
306pub enum Loop {
307    Once,
308    Times(Expr),
309    Until { cond: Cond, test_after: bool },
310    Varying { varying: Box<Varying>, test_after: bool },
311}
312
313#[derive(Clone, Debug, PartialEq, Eq)]
314pub enum Stmt {
315    Move { from: Operand, to: Vec<Ref>, pos: Pos },
316    Compute { targets: Vec<Target>, expr: Expr, size_error: Option<SizeError>, pos: Pos },
317    /// ADD, SUBTRACT, MULTIPLY and DIVIDE, reduced to their arithmetic.
318    Arith(Box<Arith>),
319    If { cond: Cond, then: Vec<Stmt>, otherwise: Vec<Stmt>, pos: Pos },
320    PerformInline { body: Vec<Stmt>, repeat: Loop, pos: Pos },
321    PerformProc { from: ProcName, thru: Option<ProcName>, repeat: Loop, pos: Pos },
322    Evaluate { subjects: Vec<Subject>, whens: Vec<When>, other: Vec<Stmt>, pos: Pos },
323    Display { items: Vec<Operand>, no_advancing: bool, pos: Pos },
324    Open { files: Vec<(OpenMode, String)>, pos: Pos },
325    Close { files: Vec<String>, pos: Pos },
326    Read(Box<ReadStmt>),
327    Write { record: Ref, from: Option<Operand>, advancing: Option<Advancing>, invalid: Handlers, pos: Pos },
328    Rewrite { record: Ref, from: Option<Operand>, invalid: Handlers, pos: Pos },
329    Delete { file: String, invalid: Handlers, pos: Pos },
330    Start { file: String, key: Option<(RelOp, Ref)>, invalid: Handlers, pos: Pos },
331    Initialize { targets: Vec<Ref>, pos: Pos },
332    GoTo { target: ProcName, pos: Pos },
333    Goback { pos: Pos },
334    /// EXIT PROGRAM: returns from a called program; in the first program it does nothing.
335    ExitProgram { pos: Pos },
336    Call(Box<Call>),
337    Cancel { targets: Vec<Operand>, pos: Pos },
338    Set { set: SetStmt, pos: Pos },
339    Accept { target: Ref, from: AcceptFrom, pos: Pos },
340    String(Box<StringStmt>),
341    Unstring(Box<Unstring>),
342    Inspect(Box<Inspect>),
343    Search(Box<Search>),
344    /// NEXT SENTENCE: control passes to the statement after the next separator period.
345    NextSentence,
346    /// A separator period in the PROCEDURE DIVISION: where NEXT SENTENCE resumes.
347    SentenceEnd,
348    Exec(Box<ExecBlock>),
349    StopRun { pos: Pos },
350    Continue,
351    Exit(ExitKind),
352}
353
354#[derive(Clone, Debug, PartialEq, Eq)]
355pub struct SizeError {
356    pub on: Vec<Stmt>,
357    pub not_on: Vec<Stmt>,
358}
359
360#[derive(Clone, Copy, Debug, PartialEq, Eq)]
361pub enum ArithVerb {
362    Add,
363    Subtract,
364    Multiply,
365    Divide,
366}
367
368/// One arithmetic statement: `targets` each receive `expr`, which is written in terms of the
369/// statement's operands and, for the forms without GIVING, the target itself (`Operand::Ref` of
370/// the target, placed by the parser).
371#[derive(Clone, Debug, PartialEq, Eq)]
372pub struct Arith {
373    pub verb: ArithVerb,
374    pub computations: Vec<(Target, Expr)>,
375    pub remainder: Option<(Target, Expr, Expr)>,
376    pub size_error: Option<SizeError>,
377    pub pos: Pos,
378}
379
380#[derive(Clone, Copy, Debug, PartialEq, Eq)]
381pub enum ArgMode {
382    Reference,
383    Content,
384    Value,
385}
386
387/// One CALL argument; `value` is None for OMITTED.
388#[derive(Clone, Debug, PartialEq, Eq)]
389pub struct Arg {
390    pub mode: ArgMode,
391    pub value: Option<Operand>,
392}
393
394#[derive(Clone, Debug, PartialEq, Eq)]
395pub struct Call {
396    pub target: Operand,
397    pub using: Vec<Arg>,
398    pub returning: Option<Ref>,
399    pub on_exception: Option<Vec<Stmt>>,
400    pub not_on_exception: Option<Vec<Stmt>>,
401    pub pos: Pos,
402}
403
404#[derive(Clone, Debug, PartialEq, Eq)]
405pub enum SetStmt {
406    ConditionTrue(Vec<Ref>),
407    /// SET targets TO value: an index or integer to a number, a pointer to ADDRESS OF, NULL or another pointer.
408    To { targets: Vec<Ref>, value: Operand },
409    /// SET ADDRESS OF targets TO pointer.
410    AddressOf { targets: Vec<Ref>, value: Operand },
411    UpDown { targets: Vec<Ref>, down: bool, by: Expr },
412}
413
414#[derive(Clone, Copy, Debug, PartialEq, Eq)]
415pub enum AcceptFrom {
416    Sysin,
417    Date { four_digit_year: bool },
418    Day { four_digit_year: bool },
419    DayOfWeek,
420    Time,
421}
422
423#[derive(Clone, Debug, PartialEq, Eq)]
424pub enum Delimiter {
425    Size,
426    By(Operand),
427}
428
429#[derive(Clone, Debug, PartialEq, Eq)]
430pub struct StringStmt {
431    pub sources: Vec<(Operand, Delimiter)>,
432    pub into: Ref,
433    pub pointer: Option<Ref>,
434    pub on_overflow: Option<Vec<Stmt>>,
435    pub not_on_overflow: Option<Vec<Stmt>>,
436    pub pos: Pos,
437}
438
439#[derive(Clone, Debug, PartialEq, Eq)]
440pub struct UnstringInto {
441    pub target: Ref,
442    pub delimiter_in: Option<Ref>,
443    pub count_in: Option<Ref>,
444}
445
446#[derive(Clone, Debug, PartialEq, Eq)]
447pub struct Unstring {
448    pub source: Ref,
449    /// Each delimiter, and whether ALL makes a run of it one delimiter.
450    pub delimiters: Vec<(bool, Operand)>,
451    pub into: Vec<UnstringInto>,
452    pub pointer: Option<Ref>,
453    pub tallying: Option<Ref>,
454    pub on_overflow: Option<Vec<Stmt>>,
455    pub not_on_overflow: Option<Vec<Stmt>>,
456    pub pos: Pos,
457}
458
459/// BEFORE or AFTER INITIAL value: where in the inspected item a phrase applies.
460#[derive(Clone, Debug, PartialEq, Eq)]
461pub struct Bound {
462    pub after: bool,
463    pub value: Operand,
464}
465
466#[derive(Clone, Copy, Debug, PartialEq, Eq)]
467pub enum InspectMode {
468    Characters,
469    All,
470    Leading,
471    First,
472}
473
474/// One TALLYING or REPLACING phrase. `pattern` is None for CHARACTERS; `by` is None for TALLYING.
475#[derive(Clone, Debug, PartialEq, Eq)]
476pub struct InspectPhrase {
477    pub mode: InspectMode,
478    pub pattern: Option<Operand>,
479    pub by: Option<Operand>,
480    pub counter: Option<Ref>,
481    pub bounds: Vec<Bound>,
482}
483
484#[derive(Clone, Debug, PartialEq, Eq)]
485pub struct Inspect {
486    pub target: Ref,
487    pub tallying: Vec<InspectPhrase>,
488    pub replacing: Vec<InspectPhrase>,
489    pub converting: Option<(Operand, Operand, Vec<Bound>)>,
490    pub pos: Pos,
491}
492
493#[derive(Clone, Debug, PartialEq, Eq)]
494pub struct Search {
495    pub table: Ref,
496    pub all: bool,
497    pub varying: Option<Ref>,
498    pub at_end: Option<Vec<Stmt>>,
499    pub whens: Vec<(Cond, Vec<Stmt>)>,
500    pub pos: Pos,
501}
502
503#[derive(Clone, Copy, Debug, PartialEq, Eq)]
504pub enum ExecKind {
505    Sql,
506    Cics,
507    Dli,
508    Other,
509}
510
511#[derive(Clone, Debug, PartialEq, Eq)]
512pub enum ExecArg {
513    Operand(Operand),
514    /// An argument that is not data: a paragraph for HANDLE CONDITION, or text that did not parse.
515    Text(String),
516}
517
518/// An EXEC ... END-EXEC block, read but not translated.
519#[derive(Clone, Debug, PartialEq, Eq)]
520pub struct ExecBlock {
521    pub kind: ExecKind,
522    /// The command: SELECT, INCLUDE, DECLARE CURSOR, LINK, SEND MAP and so on.
523    pub command: String,
524    /// CICS options, each with its argument.
525    pub options: Vec<(String, Option<ExecArg>)>,
526    /// SQL host variables and indicator variables.
527    pub host_variables: Vec<Ref>,
528    pub text: String,
529    pub pos: Pos,
530}
531
532impl ExecBlock {
533    /// Whether the block only declares, so a precompiler turns it into data or nothing.
534    pub fn declarative(&self) -> bool {
535        self.kind == ExecKind::Sql
536            && matches!(self.command.as_str(), "INCLUDE" | "BEGIN DECLARE SECTION" | "END DECLARE SECTION" | "WHENEVER" | "DECLARE CURSOR" | "DECLARE TABLE" | "DECLARE STATEMENT")
537    }
538}
539
540/// The statements of an ON phrase (AT END, INVALID KEY, ...) and of its NOT ON phrase.
541#[derive(Clone, Debug, Default, PartialEq, Eq)]
542pub struct Handlers {
543    pub on: Option<Vec<Stmt>>,
544    pub not_on: Option<Vec<Stmt>>,
545}
546
547#[derive(Clone, Debug, PartialEq, Eq)]
548pub struct ReadStmt {
549    pub file: String,
550    /// READ NEXT or PREVIOUS: the next record by the key of reference, even under dynamic access.
551    pub next: bool,
552    pub previous: bool,
553    pub into: Option<Ref>,
554    /// READ ... KEY IS: the key of reference for a random read.
555    pub key: Option<Ref>,
556    pub at_end: Handlers,
557    pub invalid: Handlers,
558    pub pos: Pos,
559}
560