1use crate::ast::*;
2use crate::lexer::{Tok, Token};
3use crate::{Error, Pos};
4
5pub fn parse(tokens: &[Token], options: Vec<String>) -> Result<Vec<Program>, Error> {
7 let mut parser = Parser { tokens, at: 0, exec_declarations: Vec::new(), cics: false };
8 let mut programs = Vec::new();
9 parser.program(&options, &mut programs)?;
10 while parser.peek().is_some() {
11 if !parser.at_division(&["IDENTIFICATION", "ID"]) {
12 return Err(parser.error("another program, or the end of the source"));
13 }
14 parser.program(&options, &mut programs)?;
15 }
16 Ok(programs)
17}
18
19const VERBS: &[&str] = &[
21 "MOVE", "COMPUTE", "ADD", "SUBTRACT", "MULTIPLY", "DIVIDE", "IF", "PERFORM", "DISPLAY", "INITIALIZE", "GO", "GOBACK", "STOP",
22 "CONTINUE", "EXIT", "EVALUATE", "SET", "CALL", "ACCEPT", "STRING", "UNSTRING", "INSPECT", "READ", "WRITE", "OPEN", "CLOSE",
23 "REWRITE", "DELETE", "START", "SEARCH", "SORT", "MERGE", "RETURN", "RELEASE", "CANCEL", "EXEC", "NEXT",
24];
25
26const PHRASE_WORDS: &[&str] = &[
28 "ELSE", "END-IF", "END-PERFORM", "END-COMPUTE", "END-ADD", "END-SUBTRACT", "END-MULTIPLY", "END-DIVIDE", "END-DISPLAY", "WHEN",
29 "TO", "FROM", "BY", "INTO", "GIVING", "REMAINDER", "ROUNDED", "ON", "NOT", "SIZE", "UNTIL", "VARYING", "TIMES", "THRU", "THROUGH",
30 "AND", "OR", "THEN", "UPON", "WITH", "IS", "END-EVALUATE", "ALSO", "OTHER", "OF", "IN", "AT", "END", "END-READ", "END-WRITE",
31 "BEFORE", "AFTER", "ADVANCING", "INPUT", "OUTPUT", "EXTEND", "I-O", "REVERSED", "USING", "RETURNING", "EXCEPTION", "OVERFLOW",
32 "END-CALL", "OMITTED", "CONTENT", "REFERENCE", "VALUE", "UP", "DOWN", "DELIMITED", "DELIMITER", "COUNT", "POINTER", "TALLYING",
33 "REPLACING", "CONVERTING", "INITIAL", "FOR", "CHARACTERS", "LEADING", "FIRST", "ALL", "END-STRING", "END-UNSTRING", "END-SEARCH",
34 "NEXT", "INVALID", "KEY", "END-REWRITE", "END-DELETE", "END-START",
35];
36
37fn figurative(word: &str) -> Option<Figurative> {
38 Some(match word {
39 "ZERO" | "ZEROS" | "ZEROES" => Figurative::Zero,
40 "SPACE" | "SPACES" => Figurative::Space,
41 "HIGH-VALUE" | "HIGH-VALUES" => Figurative::HighValue,
42 "LOW-VALUE" | "LOW-VALUES" => Figurative::LowValue,
43 "QUOTE" | "QUOTES" => Figurative::Quote,
44 "NULL" | "NULLS" => Figurative::Null,
45 _ => return None,
46 })
47}
48
49fn usage_word(word: &str) -> Option<Usage> {
50 Some(match word {
51 "DISPLAY" => Usage::Display,
52 "BINARY" | "COMP" | "COMPUTATIONAL" | "COMP-4" | "COMPUTATIONAL-4" => Usage::Binary,
53 "COMP-5" | "COMPUTATIONAL-5" => Usage::NativeBinary,
54 "PACKED-DECIMAL" | "COMP-3" | "COMPUTATIONAL-3" => Usage::Packed,
55 "COMP-1" | "COMPUTATIONAL-1" => Usage::Float1,
56 "COMP-2" | "COMPUTATIONAL-2" => Usage::Float2,
57 "NATIONAL" => Usage::National,
58 "POINTER" => Usage::Pointer,
59 "INDEX" => Usage::Index,
60 _ => return None,
61 })
62}
63
64struct Parser<'a> {
65 tokens: &'a [Token],
66 at: usize,
67 exec_declarations: Vec<ExecBlock>,
69 cics: bool,
71}
72
73type R<T> = Result<T, Error>;
74
75type OnPhrases = (Option<Vec<Stmt>>, Option<Vec<Stmt>>);
77
78impl Parser<'_> {
79 fn peek(&self) -> Option<&Tok> {
80 self.tokens.get(self.at).map(|t| &t.tok)
81 }
82
83 fn peek_at(&self, ahead: usize) -> Option<&Tok> {
84 self.tokens.get(self.at + ahead).map(|t| &t.tok)
85 }
86
87 fn pos(&self) -> Pos {
88 self.tokens.get(self.at).or(self.tokens.last()).map(|t| t.pos).unwrap_or_default()
89 }
90
91 fn word(&self) -> Option<&str> {
92 match self.peek() {
93 Some(Tok::Word(w)) => Some(w),
94 _ => None,
95 }
96 }
97
98 fn word_at(&self, ahead: usize) -> Option<&str> {
99 match self.peek_at(ahead) {
100 Some(Tok::Word(w)) => Some(w),
101 _ => None,
102 }
103 }
104
105 fn is_word(&self, w: &str) -> bool {
106 self.word() == Some(w)
107 }
108
109 fn accept_word(&mut self, w: &str) -> bool {
110 let yes = self.is_word(w);
111 if yes {
112 self.at += 1;
113 }
114 yes
115 }
116
117 fn accept_any(&mut self, words: &[&str]) -> Option<String> {
118 let w = self.word().filter(|w| words.contains(w))?.to_owned();
119 self.at += 1;
120 Some(w)
121 }
122
123 fn expect_word(&mut self, w: &str) -> R<()> {
124 if self.accept_word(w) { Ok(()) } else { Err(self.error(format!("expected {w}"))) }
125 }
126
127 fn accept(&mut self, tok: &Tok) -> bool {
128 let yes = self.peek() == Some(tok);
129 if yes {
130 self.at += 1;
131 }
132 yes
133 }
134
135 fn expect(&mut self, tok: &Tok, what: &str) -> R<()> {
136 if self.accept(tok) { Ok(()) } else { Err(self.error(format!("expected {what}"))) }
137 }
138
139 fn error(&self, message: impl Into<String>) -> Error {
140 let found = match self.peek() {
141 None => "end of source".to_owned(),
142 Some(Tok::Word(w)) => w.clone(),
143 Some(t) => format!("{t:?}"),
144 };
145 Error::at(self.pos(), format!("{}, found {found}", message.into()))
146 }
147
148 fn name(&mut self, what: &str) -> R<String> {
149 match self.peek() {
150 Some(Tok::Word(w)) => {
151 let w = w.clone();
152 self.at += 1;
153 Ok(w)
154 }
155 _ => Err(self.error(format!("expected {what}"))),
156 }
157 }
158
159 fn at_division(&self, names: &[&str]) -> bool {
160 self.word().is_some_and(|w| names.contains(&w)) && self.word_at(1) == Some("DIVISION")
161 }
162
163 fn program(&mut self, options: &[String], out: &mut Vec<Program>) -> R<()> {
164 let outer = (std::mem::take(&mut self.exec_declarations), std::mem::take(&mut self.cics));
165 let parsed = self.one_program(options, out);
166 (self.exec_declarations, self.cics) = outer;
167 parsed
168 }
169
170 fn one_program(&mut self, options: &[String], out: &mut Vec<Program>) -> R<()> {
171 if !self.accept_word("IDENTIFICATION") {
172 self.expect_word("ID")?;
173 }
174 self.expect_word("DIVISION")?;
175 self.expect(&Tok::Period, "a period")?;
176 self.expect_word("PROGRAM-ID")?;
177 self.accept(&Tok::Period);
178 let id = match self.peek() {
179 Some(Tok::Alnum(s)) => {
180 let s = s.clone();
181 self.at += 1;
182 s
183 }
184 _ => self.name("a program name")?,
185 };
186 let (mut initial, mut recursive) = (false, false);
187 while let Some(t) = self.peek() {
188 if *t == Tok::Period {
189 self.at += 1;
190 break;
191 }
192 initial |= self.is_word("INITIAL");
193 recursive |= self.is_word("RECURSIVE");
194 self.at += 1;
195 }
196 while self.peek().is_some() && !self.at_division(&["ENVIRONMENT", "DATA", "PROCEDURE", "IDENTIFICATION", "ID"]) && !self.at_end_program() {
197 self.at += 1;
198 }
199 let mut files = Vec::new();
200 if self.at_division(&["ENVIRONMENT"]) {
201 files = self.environment()?;
202 }
203 let (mut working_storage, mut local_storage, mut linkage) = (Vec::new(), Vec::new(), Vec::new());
204 if self.at_division(&["DATA"]) {
205 self.at += 2;
206 self.expect(&Tok::Period, "a period")?;
207 while !self.at_division(&["PROCEDURE", "IDENTIFICATION", "ID"]) && !self.at_end_program() && self.peek().is_some() {
208 if self.data_exec()? {
209 continue;
210 }
211 let section = self.name("a DATA DIVISION section")?;
212 self.expect_word("SECTION")?;
213 self.expect(&Tok::Period, "a period")?;
214 match section.as_str() {
215 "WORKING-STORAGE" => working_storage = self.data_entries()?,
216 "LINKAGE" => linkage = self.data_entries()?,
217 "LOCAL-STORAGE" => local_storage = self.data_entries()?,
218 "FILE" => self.file_section(&mut files)?,
219 other => return Err(self.error(format!("the {other} SECTION is not supported yet"))),
220 }
221 }
222 }
223 let (mut using, mut returning) = (Vec::new(), None);
224 let paragraphs = if self.at_division(&["PROCEDURE"]) {
225 self.at += 2;
226 if self.accept_word("USING") {
227 let mut by_value = false;
228 loop {
229 if self.accept_word("BY") {
230 by_value = self.accept_any(&["REFERENCE", "VALUE"]).as_deref() == Some("VALUE");
231 continue;
232 }
233 if !self.starts_ref() {
234 break;
235 }
236 using.push(Param { by_value, name: self.name("a LINKAGE item")? });
237 }
238 }
239 if self.accept_word("RETURNING") {
240 returning = Some(self.name("a RETURNING item")?);
241 }
242 self.expect(&Tok::Period, "a period after the PROCEDURE DIVISION header")?;
243 self.paragraphs()?
244 } else {
245 Vec::new()
246 };
247 if let Some(f) = files.iter().find(|f| f.assign.is_empty()) {
248 return Err(Error::at(f.pos, format!("{} has no SELECT ... ASSIGN", f.name)));
249 }
250 if self.cics {
251 self.translator_additions(&mut linkage, &mut using)?;
252 }
253 let exec_declarations = std::mem::take(&mut self.exec_declarations);
254 let mut nested = Vec::new();
255 while self.at_division(&["IDENTIFICATION", "ID"]) {
256 self.program(options, &mut nested)?;
257 }
258 if self.at_end_program() {
259 self.at += 2;
260 if self.word().is_some() || matches!(self.peek(), Some(Tok::Alnum(_))) {
261 self.at += 1;
262 }
263 self.accept(&Tok::Period);
264 }
265 out.push(Program {
266 id,
267 options: options.to_vec(),
268 initial,
269 recursive,
270 working_storage,
271 local_storage,
272 linkage,
273 using,
274 returning,
275 paragraphs,
276 files,
277 sources: Vec::new(),
278 exec_declarations,
279 });
280 out.extend(nested);
281 Ok(())
282 }
283
284 fn at_end_program(&self) -> bool {
285 self.is_word("END") && self.word_at(1) == Some("PROGRAM")
286 }
287
288 fn environment(&mut self) -> R<Vec<FileDecl>> {
291 let mut files = Vec::new();
292 while self.peek().is_some() && !self.at_division(&["DATA", "PROCEDURE"]) {
293 if self.is_word("DECIMAL-POINT") {
294 return Err(self.error("DECIMAL-POINT IS COMMA is not supported yet"));
295 }
296 if self.accept_word("SELECT") {
297 files.push(self.select()?);
298 continue;
299 }
300 self.at += 1;
301 }
302 Ok(files)
303 }
304
305 fn select(&mut self) -> R<FileDecl> {
306 let pos = self.pos();
307 let optional = self.accept_word("OPTIONAL");
308 let name = self.name("a file name")?;
309 let mut f = FileDecl {
310 name,
311 assign: String::new(),
312 organization: Organization::Sequential,
313 access: Access::Sequential,
314 record_key: None,
315 alternate_keys: Vec::new(),
316 relative_key: None,
317 optional,
318 status: None,
319 recording: None,
320 record_min: None,
321 record_max: None,
322 records: Vec::new(),
323 pos,
324 };
325 while !self.accept(&Tok::Period) {
326 let clause = self.name("a SELECT clause or a period")?;
327 match clause.as_str() {
328 "ASSIGN" => {
329 self.accept_word("TO");
330 let target = match self.peek().cloned() {
331 Some(Tok::Word(w)) | Some(Tok::Alnum(w)) => w,
332 _ => return Err(self.error("a DD name after ASSIGN")),
333 };
334 self.at += 1;
335 let target = target.to_ascii_uppercase();
336 f.assign = target.rsplit('-').next().filter(|_| target.contains("-S-") || target.starts_with("S-") || target.starts_with("AS-")).unwrap_or(&target).to_owned();
337 }
338 "RECORD" if !self.is_word("SEQUENTIAL") => {
339 self.accept_word("KEY");
340 self.accept_word("IS");
341 f.record_key = Some(self.reference()?);
342 }
343 "RELATIVE" if self.is_word("KEY") => {
344 self.at += 1;
345 self.accept_word("IS");
346 f.relative_key = Some(self.reference()?);
347 }
348 "ALTERNATE" => {
349 self.accept_word("RECORD");
350 self.expect_word("KEY")?;
351 self.accept_word("IS");
352 let key = self.reference()?;
353 let duplicates = self.accept_word("WITH") | self.is_word("DUPLICATES");
354 if duplicates {
355 self.expect_word("DUPLICATES")?;
356 }
357 f.alternate_keys.push((key, duplicates));
358 }
359 "ORGANIZATION" | "LINE" | "RECORD" | "SEQUENTIAL" | "INDEXED" | "RELATIVE" => {
360 if clause == "ORGANIZATION" {
361 self.accept_word("IS");
362 }
363 let first = if clause == "ORGANIZATION" { self.name("an organization")? } else { clause.clone() };
364 f.organization = match first.as_str() {
365 "LINE" => {
366 self.expect_word("SEQUENTIAL")?;
367 Organization::LineSequential
368 }
369 "RECORD" => {
370 self.expect_word("SEQUENTIAL")?;
371 Organization::Sequential
372 }
373 "SEQUENTIAL" => Organization::Sequential,
374 "INDEXED" => Organization::Indexed,
375 "RELATIVE" => Organization::Relative,
376 other => return Err(self.error(format!("ORGANIZATION {other} is not supported yet"))),
377 };
378 }
379 "ACCESS" => {
380 self.accept_word("MODE");
381 self.accept_word("IS");
382 f.access = match self.name("an access mode")?.as_str() {
383 "SEQUENTIAL" => Access::Sequential,
384 "RANDOM" => Access::Random,
385 "DYNAMIC" => Access::Dynamic,
386 other => return Err(self.error(format!("ACCESS MODE {other} is not an access mode"))),
387 };
388 }
389 "FILE" | "STATUS" => {
390 if clause == "FILE" {
391 self.expect_word("STATUS")?;
392 }
393 self.accept_word("IS");
394 f.status = Some(self.reference()?);
395 if self.starts_ref() && !self.word().is_some_and(|w| SELECT_CLAUSES.contains(&w)) {
396 self.reference()?;
397 }
398 }
399 "RESERVE" | "PADDING" => {
400 while self.peek().is_some() && self.peek() != Some(&Tok::Period) && !self.word().is_some_and(|w| SELECT_CLAUSES.contains(&w)) {
401 self.at += 1;
402 }
403 }
404 other => return Err(self.error(format!("{other} is not a SELECT clause ironwork for COBOL supports yet"))),
405 }
406 }
407 Ok(f)
408 }
409
410 fn file_section(&mut self, files: &mut [FileDecl]) -> R<()> {
413 while self.is_word("FD") || self.is_word("SD") {
414 if self.is_word("SD") {
415 return Err(self.error("SD (sort files) is not supported yet"));
416 }
417 self.at += 1;
418 let pos = self.pos();
419 let name = self.name("a file name")?;
420 let Some(index) = files.iter().position(|f| f.name == name) else {
421 return Err(Error::at(pos, format!("FD {name} has no SELECT")));
422 };
423 while !self.accept(&Tok::Period) {
424 match self.name("an FD clause or a period")?.as_str() {
425 "RECORDING" => {
426 self.accept_word("MODE");
427 self.accept_word("IS");
428 let mode = self.name("F, V, U or S")?;
429 files[index].recording = mode.chars().next();
430 }
431 "RECORD" => {
432 self.accept_word("CONTAINS");
433 self.accept_word("IS");
434 if self.accept_word("VARYING") {
435 self.accept_word("IN");
436 self.accept_word("SIZE");
437 self.accept_word("FROM");
438 }
439 let number = |p: &mut Self| -> R<Option<u32>> {
440 match p.peek() {
441 Some(Tok::Number(n)) => {
442 let v = n.parse().map_err(|_| p.error("a record length"))?;
443 p.at += 1;
444 Ok(Some(v))
445 }
446 _ => Ok(None),
447 }
448 };
449 let first = number(self)?;
450 let second = if self.accept_word("TO") { number(self)? } else { None };
451 files[index].record_min = first;
452 files[index].record_max = second.or(first);
453 self.accept_word("CHARACTERS");
454 if self.accept_word("DEPENDING") {
455 self.accept_word("ON");
456 self.reference()?;
457 }
458 }
459 _ => {
460 while self.peek().is_some() && self.peek() != Some(&Tok::Period)
461 && !self.word().is_some_and(|w| matches!(w, "RECORDING" | "RECORD" | "BLOCK" | "LABEL" | "DATA" | "VALUE"))
462 {
463 self.at += 1;
464 }
465 }
466 }
467 }
468 files[index].records = self.data_entries()?;
469 }
470 Ok(())
471 }
472
473 fn data_exec(&mut self) -> R<bool> {
475 let Some(Tok::Exec(text)) = self.peek().cloned() else { return Ok(false) };
476 let block = self.exec_block(&text, self.pos());
477 self.at += 1;
478 self.accept(&Tok::Period);
479 self.exec_declarations.push(block);
480 Ok(true)
481 }
482
483 fn translator_additions(&mut self, linkage: &mut Vec<DataEntry>, using: &mut Vec<Param>) -> R<()> {
486 let defined = |l: &[DataEntry], n: &str| l.iter().any(|e| e.level == 1 && e.name.as_deref() == Some(n));
487 let mut added = Vec::new();
488 if !defined(linkage, "DFHEIBLK") {
489 added.extend(system_entries("DFHEIBLK")?);
490 }
491 if !defined(linkage, "DFHCOMMAREA") {
492 added.extend(system_text_entries(" 01 DFHCOMMAREA PIC X(1).\n")?);
493 }
494 linkage.splice(0..0, added);
495 for (i, name) in ["DFHEIBLK", "DFHCOMMAREA"].into_iter().enumerate() {
496 if using.get(i).map(|p| p.name.as_str()) != Some(name) {
497 using.insert(i, Param { by_value: false, name: name.into() });
498 }
499 }
500 Ok(())
501 }
502
503 fn exec_block(&mut self, text: &str, pos: Pos) -> ExecBlock {
504 let (kind_word, body) = text.split_once(' ').unwrap_or((text, ""));
505 let kind = match kind_word.to_ascii_uppercase().as_str() {
506 "SQL" => ExecKind::Sql,
507 "CICS" => ExecKind::Cics,
508 "DLI" => ExecKind::Dli,
509 _ => ExecKind::Other,
510 };
511 let words: Vec<String> = body.split_whitespace().map(|w| w.to_ascii_uppercase()).collect();
512 let word = |i: usize| words.get(i).map(String::as_str).unwrap_or("");
513 let mut block = ExecBlock { kind, command: word(0).to_owned(), options: Vec::new(), host_variables: Vec::new(), text: text.to_owned(), pos };
514 match kind {
515 ExecKind::Sql => {
516 block.command = match (word(0), word(1), word(2)) {
517 ("DECLARE", _, "CURSOR") => "DECLARE CURSOR".into(),
518 ("DECLARE", _, "TABLE") => "DECLARE TABLE".into(),
519 ("DECLARE", _, "STATEMENT") => "DECLARE STATEMENT".into(),
520 ("BEGIN" | "END", "DECLARE", "SECTION") => format!("{} DECLARE SECTION", word(0)),
521 (first, _, _) => first.into(),
522 };
523 block.host_variables = host_variables(body, pos);
524 }
525 ExecKind::Cics => {
526 self.cics = true;
527 block.options = cics_options(body);
528 if let Some((first, None)) = block.options.first().cloned() {
529 block.command = first.clone();
530 block.options.remove(0);
531 if let Some((second, None)) = block.options.first().cloned()
532 && crate::system::cics_two_word(&first, &second)
533 {
534 block.command = format!("{first} {second}");
535 block.options.remove(0);
536 }
537 }
538 let labels = block.command.starts_with("HANDLE");
539 for (_, arg) in &mut block.options {
540 if let Some(ExecArg::Text(t)) = arg
541 && !labels
542 && let Some(op) = operand_of(t, pos)
543 {
544 *arg = Some(ExecArg::Operand(op));
545 }
546 }
547 }
548 _ => {}
549 }
550 block
551 }
552
553 fn data_entries(&mut self) -> R<Vec<DataEntry>> {
554 let mut entries = Vec::new();
555 loop {
556 if self.data_exec()? {
557 continue;
558 }
559 let Some(Tok::Number(level)) = self.peek() else { break };
560 let pos = self.pos();
561 let level: u8 = level.parse().map_err(|_| self.error("a level number"))?;
562 self.at += 1;
563 entries.push(self.data_entry(level, pos)?);
564 }
565 Ok(entries)
566 }
567
568 fn data_entry(&mut self, level: u8, pos: Pos) -> R<DataEntry> {
569 let mut e = DataEntry {
570 level,
571 name: None,
572 picture: None,
573 usage: None,
574 value: None,
575 redefines: None,
576 occurs: None,
577 depending_on: None,
578 sign: None,
579 justified: false,
580 sync: false,
581 blank_when_zero: false,
582 indexed_by: Vec::new(),
583 keys: Vec::new(),
584 condition_values: Vec::new(),
585 pos,
586 };
587 if let Some(w) = self.word()
588 && !is_clause_word(w)
589 {
590 if w != "FILLER" {
591 e.name = Some(w.to_owned());
592 }
593 self.at += 1;
594 }
595 while !self.accept(&Tok::Period) {
596 let clause = self.name("a data description clause or a period")?;
597 match clause.as_str() {
598 "PIC" | "PICTURE" => {
599 self.accept_word("IS");
600 match self.peek() {
601 Some(Tok::Pic(p)) => {
602 e.picture = Some(p.clone());
603 self.at += 1;
604 }
605 _ => return Err(self.error("a PICTURE character-string")),
606 }
607 }
608 "USAGE" => {
609 self.accept_word("IS");
610 let w = self.name("a usage")?;
611 e.usage = Some(usage_word(&w).ok_or_else(|| Error::at(pos, format!("USAGE {w} is not supported yet")))?);
612 }
613 "VALUE" | "VALUES" => {
614 self.accept_word("IS");
615 self.accept_word("ARE");
616 if level == 88 {
617 while self.peek().is_some() && self.peek() != Some(&Tok::Period) {
618 let low = self.literal()?;
619 let high = if self.accept_any(&["THRU", "THROUGH"]).is_some() { Some(self.literal()?) } else { None };
620 e.condition_values.push((low, high));
621 }
622 } else {
623 e.value = Some(self.literal()?);
624 }
625 }
626 "REDEFINES" => e.redefines = Some(self.name("the item redefined")?),
627 "OCCURS" => {
628 let count = |p: &mut Self| -> R<u32> {
629 let n = match p.peek() {
630 Some(Tok::Number(n)) => n.parse().map_err(|_| p.error("an OCCURS count"))?,
631 _ => return Err(p.error("an OCCURS count")),
632 };
633 p.at += 1;
634 Ok(n)
635 };
636 let mut most = count(self)?;
637 if self.accept_word("TO") {
638 most = count(self)?;
639 }
640 e.occurs = Some(most);
641 self.accept_word("TIMES");
642 if self.accept_word("DEPENDING") {
643 self.accept_word("ON");
644 e.depending_on = Some(self.reference()?);
645 }
646 loop {
647 if let Some(order) = self.accept_any(&["ASCENDING", "DESCENDING"]) {
648 self.accept_word("KEY");
649 self.accept_word("IS");
650 while self.word().is_some_and(|w| !is_clause_word(w) && !matches!(w, "INDEXED" | "ASCENDING" | "DESCENDING")) {
651 e.keys.push((order == "ASCENDING", self.reference()?));
652 }
653 } else if self.accept_word("INDEXED") {
654 self.accept_word("BY");
655 while self.word().is_some_and(|w| !is_clause_word(w) && !matches!(w, "ASCENDING" | "DESCENDING")) {
656 e.indexed_by.push(self.name("an index name")?);
657 }
658 } else {
659 break;
660 }
661 }
662 }
663 "SIGN" | "LEADING" | "TRAILING" => {
664 if clause == "SIGN" {
665 self.accept_word("IS");
666 }
667 let side = if clause == "SIGN" { self.name("LEADING or TRAILING")? } else { clause.clone() };
668 let position = match side.as_str() {
669 "LEADING" => SignPosition::Leading,
670 "TRAILING" => SignPosition::Trailing,
671 _ => return Err(self.error("LEADING or TRAILING")),
672 };
673 let separate = self.accept_word("SEPARATE");
674 if separate {
675 self.accept_word("CHARACTER");
676 }
677 e.sign = Some(SignClause { position, separate });
678 }
679 "JUSTIFIED" | "JUST" => {
680 self.accept_word("RIGHT");
681 e.justified = true;
682 }
683 "SYNC" | "SYNCHRONIZED" => {
684 self.accept_any(&["LEFT", "RIGHT"]);
685 e.sync = true;
686 }
687 "BLANK" => {
688 self.accept_word("WHEN");
689 if self.accept_any(&["ZERO", "ZEROS", "ZEROES"]).is_none() {
690 return Err(self.error("ZERO after BLANK WHEN"));
691 }
692 e.blank_when_zero = true;
693 }
694 "GLOBAL" | "EXTERNAL" => {}
695 other => match usage_word(other) {
696 Some(u) => e.usage = Some(u),
697 None => return Err(Error::at(self.tokens[self.at - 1].pos, format!("{other} is not a data description clause ironwork for COBOL supports yet"))),
698 },
699 }
700 }
701 Ok(e)
702 }
703
704 fn literal(&mut self) -> R<Literal> {
705 let lit = match self.peek().cloned() {
706 Some(Tok::Alnum(s)) => Literal::Alnum(s),
707 Some(Tok::Hex(b)) => Literal::Hex(b),
708 Some(Tok::National(s)) => Literal::National(s),
709 Some(Tok::Number(n)) => Literal::Number(n),
710 Some(Tok::Word(w)) if w == "ALL" => {
711 self.at += 1;
712 return Ok(Literal::All(Box::new(self.literal()?)));
713 }
714 Some(Tok::Word(w)) => Literal::Figurative(figurative(&w).ok_or_else(|| self.error("a literal"))?),
715 _ => return Err(self.error("a literal")),
716 };
717 self.at += 1;
718 Ok(lit)
719 }
720
721 fn paragraph_header(&self) -> bool {
722 self.tokens.get(self.at).is_some_and(|t| t.area_a && matches!(t.tok, Tok::Word(_))) && self.peek_at(1) == Some(&Tok::Period)
723 }
724
725 fn section_header(&self) -> bool {
726 self.tokens.get(self.at).is_some_and(|t| matches!(t.tok, Tok::Word(_))) && self.word_at(1) == Some("SECTION")
727 }
728
729 fn paragraphs(&mut self) -> R<Vec<Paragraph>> {
730 let mut paragraphs: Vec<Paragraph> = Vec::new();
731 let mut section: Option<String> = None;
732 loop {
733 if self.peek().is_none() || self.at_end_program() || self.at_division(&["IDENTIFICATION", "ID"]) {
734 break;
735 }
736 if self.is_word("DECLARATIVES") {
737 return Err(self.error("DECLARATIVES are not supported yet"));
738 }
739 if self.section_header() {
740 let pos = self.pos();
741 let name = self.name("a section name")?;
742 self.at += 1;
743 if matches!(self.peek(), Some(Tok::Number(_))) {
744 self.at += 1;
745 }
746 self.expect(&Tok::Period, "a period after the section header")?;
747 section = Some(name.clone());
748 paragraphs.push(Paragraph { name, statements: Vec::new(), section: section.clone(), is_section: true, pos });
749 continue;
750 }
751 if self.paragraph_header() {
752 let pos = self.pos();
753 let name = self.name("a paragraph name")?;
754 self.at += 1;
755 paragraphs.push(Paragraph { name, statements: Vec::new(), section: section.clone(), is_section: false, pos });
756 continue;
757 }
758 if self.accept(&Tok::Period) {
759 if let Some(p) = paragraphs.last_mut()
760 && p.statements.last().is_some_and(|s| *s != Stmt::SentenceEnd)
761 {
762 p.statements.push(Stmt::SentenceEnd);
763 }
764 continue;
765 }
766 let block = self.block(&[])?;
767 if block.is_empty() {
768 return Err(self.error("a statement"));
769 }
770 if paragraphs.is_empty() {
771 paragraphs.push(Paragraph { name: String::new(), statements: Vec::new(), section: None, is_section: false, pos: self.pos() });
772 }
773 paragraphs.last_mut().unwrap().statements.extend(block);
774 }
775 Ok(paragraphs)
776 }
777
778 fn block(&mut self, stops: &[&str]) -> R<Vec<Stmt>> {
780 let mut out = Vec::new();
781 while let Some(tok) = self.peek() {
782 if *tok == Tok::Period || self.paragraph_header() || self.section_header() || self.word().is_some_and(|w| stops.contains(&w)) {
783 break;
784 }
785 if let Some(Tok::Exec(text)) = self.peek().cloned() {
786 let block = self.exec_block(&text, self.pos());
787 self.at += 1;
788 out.push(Stmt::Exec(Box::new(block)));
789 continue;
790 }
791 if !self.word().is_some_and(|w| VERBS.contains(&w)) {
792 break;
793 }
794 out.push(self.statement()?);
795 }
796 Ok(out)
797 }
798
799 fn statement(&mut self) -> R<Stmt> {
800 let pos = self.pos();
801 let verb = self.name("a statement")?;
802 Ok(match verb.as_str() {
803 "MOVE" => {
804 if self.accept_any(&["CORRESPONDING", "CORR"]).is_some() {
805 return Err(Error::at(pos, "MOVE CORRESPONDING is not supported yet"));
806 }
807 let from = self.operand()?;
808 self.expect_word("TO")?;
809 let to = self.refs()?;
810 Stmt::Move { from, to, pos }
811 }
812 "COMPUTE" => {
813 let targets = self.targets()?;
814 if !self.accept(&Tok::Eq) {
815 self.expect_word("EQUAL")?;
816 }
817 let expr = self.expr()?;
818 let size_error = self.size_error()?;
819 self.accept_word("END-COMPUTE");
820 Stmt::Compute { targets, expr, size_error, pos }
821 }
822 "ADD" | "SUBTRACT" | "MULTIPLY" | "DIVIDE" => Stmt::Arith(Box::new(self.arith(&verb, pos)?)),
823 "IF" => {
824 let cond = self.cond()?;
825 self.accept_word("THEN");
826 let then = self.block(&["ELSE", "END-IF"])?;
827 let otherwise = if self.accept_word("ELSE") { self.block(&["END-IF"])? } else { Vec::new() };
828 self.accept_word("END-IF");
829 Stmt::If { cond, then, otherwise, pos }
830 }
831 "PERFORM" => self.perform(pos)?,
832 "DISPLAY" => {
833 let mut items = Vec::new();
834 while self.starts_operand() {
835 items.push(self.operand()?);
836 }
837 if self.accept_word("UPON") {
838 self.name("a mnemonic name")?;
839 }
840 let no_advancing = self.accept_word("WITH") | self.is_word("NO");
841 if no_advancing {
842 self.expect_word("NO")?;
843 self.expect_word("ADVANCING")?;
844 }
845 self.accept_word("END-DISPLAY");
846 Stmt::Display { items, no_advancing, pos }
847 }
848 "INITIALIZE" => Stmt::Initialize { targets: self.refs()?, pos },
849 "CALL" => Stmt::Call(Box::new(self.call(pos)?)),
850 "CANCEL" => {
851 let mut targets = Vec::new();
852 while self.starts_operand() {
853 targets.push(self.operand()?);
854 }
855 if targets.is_empty() {
856 return Err(self.error("a program to CANCEL"));
857 }
858 Stmt::Cancel { targets, pos }
859 }
860 "SET" => Stmt::Set { set: self.set()?, pos },
861 "STRING" => Stmt::String(Box::new(self.string(pos)?)),
862 "UNSTRING" => Stmt::Unstring(Box::new(self.unstring(pos)?)),
863 "INSPECT" => Stmt::Inspect(Box::new(self.inspect(pos)?)),
864 "SEARCH" => Stmt::Search(Box::new(self.search(pos)?)),
865 "NEXT" => {
866 self.expect_word("SENTENCE")?;
867 Stmt::NextSentence
868 }
869 "ACCEPT" => {
870 let target = self.reference()?;
871 let from = if self.accept_word("FROM") {
872 match self.name("SYSIN, DATE, DAY, DAY-OF-WEEK or TIME")?.as_str() {
873 "DATE" => AcceptFrom::Date { four_digit_year: self.accept_word("YYYYMMDD") },
874 "DAY" => AcceptFrom::Day { four_digit_year: self.accept_word("YYYYDDD") },
875 "DAY-OF-WEEK" => AcceptFrom::DayOfWeek,
876 "TIME" => AcceptFrom::Time,
877 _ => AcceptFrom::Sysin,
878 }
879 } else {
880 AcceptFrom::Sysin
881 };
882 self.accept_word("END-ACCEPT");
883 Stmt::Accept { target, from, pos }
884 }
885 "OPEN" => {
886 let mut files = Vec::new();
887 while let Some(mode) = self.accept_any(&["INPUT", "OUTPUT", "EXTEND", "I-O"]) {
888 let mode = match mode.as_str() {
889 "INPUT" => OpenMode::Input,
890 "OUTPUT" => OpenMode::Output,
891 "EXTEND" => OpenMode::Extend,
892 _ => OpenMode::InputOutput,
893 };
894 while self.starts_ref() {
895 files.push((mode, self.name("a file name")?));
896 self.accept_any(&["REVERSED"]);
897 if self.accept_word("WITH") {
898 self.expect_word("NO")?;
899 self.expect_word("REWIND")?;
900 }
901 }
902 }
903 if files.is_empty() {
904 return Err(self.error("INPUT, OUTPUT, EXTEND or I-O and a file"));
905 }
906 Stmt::Open { files, pos }
907 }
908 "CLOSE" => {
909 let mut files = Vec::new();
910 while self.starts_ref() {
911 files.push(self.name("a file name")?);
912 if self.accept_word("WITH") {
913 self.accept_any(&["LOCK", "NO"]);
914 self.accept_word("REWIND");
915 }
916 }
917 Stmt::Close { files, pos }
918 }
919 "READ" => {
920 let file = self.name("a file name")?;
921 let previous = self.accept_word("PREVIOUS");
922 let next = previous || self.accept_word("NEXT");
923 self.accept_word("RECORD");
924 let into = if self.accept_word("INTO") { Some(self.reference()?) } else { None };
925 let key = if self.accept_word("KEY") {
926 self.accept_word("IS");
927 Some(self.reference()?)
928 } else {
929 None
930 };
931 let (mut at_end, mut invalid) = (Handlers::default(), Handlers::default());
932 loop {
933 let negated = self.is_word("NOT") && matches!(self.word_at(1), Some("AT" | "END" | "INVALID"));
934 let start = if negated { 1 } else { 0 };
935 let word = self.word_at(start).unwrap_or("").to_owned();
936 if !matches!(word.as_str(), "AT" | "END" | "INVALID") {
937 break;
938 }
939 self.at += start;
940 let target = if word == "INVALID" {
941 self.at += 1;
942 self.accept_word("KEY");
943 &mut invalid
944 } else {
945 self.accept_word("AT");
946 self.expect_word("END")?;
947 &mut at_end
948 };
949 let body = self.block(&["NOT", "END-READ"])?;
950 if negated { target.not_on = Some(body) } else { target.on = Some(body) }
951 }
952 self.accept_word("END-READ");
953 Stmt::Read(Box::new(ReadStmt { file, next, previous, into, key, at_end, invalid, pos }))
954 }
955 "REWRITE" => {
956 let record = self.reference()?;
957 let from = if self.accept_word("FROM") { Some(self.operand()?) } else { None };
958 let invalid = self.invalid_key("END-REWRITE")?;
959 Stmt::Rewrite { record, from, invalid, pos }
960 }
961 "DELETE" => {
962 let file = self.name("a file name")?;
963 self.accept_word("RECORD");
964 let invalid = self.invalid_key("END-DELETE")?;
965 Stmt::Delete { file, invalid, pos }
966 }
967 "START" => {
968 let file = self.name("a file name")?;
969 let key = if self.accept_word("KEY") {
970 self.accept_word("IS");
971 let op = if self.accept_word("NOT") {
972 match self.relop()? {
973 Some(RelOp::Lt) => RelOp::Ge,
974 _ => return Err(self.error("NOT < in START KEY")),
975 }
976 } else {
977 self.relop()?.ok_or_else(|| self.error("a relation after START KEY"))?
978 };
979 Some((op, self.reference()?))
980 } else {
981 None
982 };
983 let invalid = self.invalid_key("END-START")?;
984 Stmt::Start { file, key, invalid, pos }
985 }
986 "WRITE" => {
987 let record = self.reference()?;
988 let from = if self.accept_word("FROM") { Some(self.operand()?) } else { None };
989 let mut advancing = None;
990 if let Some(side) = self.accept_any(&["BEFORE", "AFTER"]) {
991 let before = side == "BEFORE";
992 self.accept_word("ADVANCING");
993 advancing = Some(if self.accept_word("PAGE") {
994 Advancing::Page { before }
995 } else {
996 let count = self.expr()?;
997 self.accept_any(&["LINE", "LINES"]);
998 Advancing::Lines { before, count }
999 });
1000 }
1001 let invalid = self.invalid_key("END-WRITE")?;
1002 Stmt::Write { record, from, advancing, invalid, pos }
1003 }
1004 "GO" => {
1005 self.accept_word("TO");
1006 Stmt::GoTo { target: self.proc_name()?, pos }
1007 }
1008 "EVALUATE" => self.evaluate(pos)?,
1009 "GOBACK" => Stmt::Goback { pos },
1010 "STOP" => {
1011 self.expect_word("RUN")?;
1012 Stmt::StopRun { pos }
1013 }
1014 "CONTINUE" => Stmt::Continue,
1015 "EXIT" => match self.accept_any(&["PROGRAM", "PARAGRAPH", "SECTION", "PERFORM"]).as_deref() {
1016 Some("PROGRAM") => Stmt::ExitProgram { pos },
1017 Some("PARAGRAPH") => Stmt::Exit(ExitKind::Paragraph),
1018 Some("SECTION") => Stmt::Exit(ExitKind::Section),
1019 Some(_) if self.accept_word("CYCLE") => Stmt::Exit(ExitKind::PerformCycle),
1020 Some(_) => Stmt::Exit(ExitKind::Perform),
1021 None => Stmt::Exit(ExitKind::Plain),
1022 },
1023 other => return Err(Error::at(pos, format!("{other} is not a statement ironwork for COBOL supports yet"))),
1024 })
1025 }
1026
1027 fn size_error(&mut self) -> R<Option<SizeError>> {
1028 let mut found = None;
1029 loop {
1030 let negated = self.is_word("NOT") && matches!(self.word_at(1), Some("ON" | "SIZE"));
1031 let plain = self.is_word("ON") || self.is_word("SIZE");
1032 if !negated && !plain {
1033 return Ok(found);
1034 }
1035 if negated {
1036 self.at += 1;
1037 }
1038 self.accept_word("ON");
1039 self.expect_word("SIZE")?;
1040 self.expect_word("ERROR")?;
1041 let body = self.block(&["NOT", "END-COMPUTE", "END-ADD", "END-SUBTRACT", "END-MULTIPLY", "END-DIVIDE"])?;
1042 let se = found.get_or_insert(SizeError { on: Vec::new(), not_on: Vec::new() });
1043 if negated { se.not_on = body } else { se.on = body }
1044 }
1045 }
1046
1047 fn targets(&mut self) -> R<Vec<Target>> {
1048 let mut out = Vec::new();
1049 while self.starts_ref() {
1050 let r = self.reference()?;
1051 out.push(Target { r, rounded: self.accept_word("ROUNDED") });
1052 }
1053 if out.is_empty() {
1054 return Err(self.error("a receiving item"));
1055 }
1056 Ok(out)
1057 }
1058
1059 fn refs(&mut self) -> R<Vec<Ref>> {
1060 let mut out = Vec::new();
1061 while self.starts_ref() {
1062 out.push(self.reference()?);
1063 }
1064 if out.is_empty() {
1065 return Err(self.error("a data name"));
1066 }
1067 Ok(out)
1068 }
1069
1070 fn operands_until(&mut self, stops: &[&str]) -> R<Vec<Expr>> {
1071 let mut out = Vec::new();
1072 while self.starts_operand() && !self.word().is_some_and(|w| stops.contains(&w)) {
1073 out.push(Expr::Operand(self.operand()?));
1074 }
1075 if out.is_empty() {
1076 return Err(self.error("an operand"));
1077 }
1078 Ok(out)
1079 }
1080
1081 fn arith(&mut self, verb: &str, pos: Pos) -> R<Arith> {
1082 let sum = |mut es: Vec<Expr>| {
1083 let first = es.remove(0);
1084 es.into_iter().fold(first, |acc, e| Expr::Bin(Box::new(acc), BinOp::Add, Box::new(e)))
1085 };
1086 let of = |t: &Target| Expr::Operand(Operand::Ref(t.r.clone()));
1087 let bin = |a: Expr, op: BinOp, b: Expr| Expr::Bin(Box::new(a), op, Box::new(b));
1088 let mut remainder = None;
1089 let (verb, computations) = match verb {
1090 "ADD" => {
1091 let addends = sum(self.operands_until(&["TO", "GIVING"])?);
1092 if self.accept_word("TO") {
1093 if self.words_ahead_include("GIVING") {
1094 let to = sum(self.operands_until(&["GIVING"])?);
1095 self.expect_word("GIVING")?;
1096 let targets = self.targets()?;
1097 let total = bin(addends, BinOp::Add, to);
1098 (ArithVerb::Add, targets.into_iter().map(|t| (t, total.clone())).collect())
1099 } else {
1100 let targets = self.targets()?;
1101 (ArithVerb::Add, targets.into_iter().map(|t| (t.clone(), bin(of(&t), BinOp::Add, addends.clone()))).collect())
1102 }
1103 } else {
1104 self.expect_word("GIVING")?;
1105 let targets = self.targets()?;
1106 (ArithVerb::Add, targets.into_iter().map(|t| (t, addends.clone())).collect())
1107 }
1108 }
1109 "SUBTRACT" => {
1110 let subtrahend = sum(self.operands_until(&["FROM"])?);
1111 self.expect_word("FROM")?;
1112 if self.words_ahead_include("GIVING") {
1113 let minuend = sum(self.operands_until(&["GIVING"])?);
1114 self.expect_word("GIVING")?;
1115 let targets = self.targets()?;
1116 let diff = bin(minuend, BinOp::Sub, subtrahend);
1117 (ArithVerb::Subtract, targets.into_iter().map(|t| (t, diff.clone())).collect())
1118 } else {
1119 let targets = self.targets()?;
1120 (ArithVerb::Subtract, targets.into_iter().map(|t| (t.clone(), bin(of(&t), BinOp::Sub, subtrahend.clone()))).collect())
1121 }
1122 }
1123 "MULTIPLY" => {
1124 let a = Expr::Operand(self.operand()?);
1125 self.expect_word("BY")?;
1126 if self.words_ahead_include("GIVING") {
1127 let b = Expr::Operand(self.operand()?);
1128 self.expect_word("GIVING")?;
1129 let targets = self.targets()?;
1130 (ArithVerb::Multiply, targets.into_iter().map(|t| (t, bin(a.clone(), BinOp::Mul, b.clone()))).collect())
1131 } else {
1132 let targets = self.targets()?;
1133 (ArithVerb::Multiply, targets.into_iter().map(|t| (t.clone(), bin(a.clone(), BinOp::Mul, of(&t)))).collect())
1134 }
1135 }
1136 _ => {
1137 let first = Expr::Operand(self.operand()?);
1138 let into = match self.accept_any(&["INTO", "BY"]).as_deref() {
1139 Some("INTO") => true,
1140 Some(_) => false,
1141 None => return Err(self.error("INTO or BY")),
1142 };
1143 if !into || self.words_ahead_include("GIVING") {
1144 let second = Expr::Operand(self.operand()?);
1145 let (dividend, divisor) = if into { (second, first) } else { (first, second) };
1146 self.expect_word("GIVING")?;
1147 let targets = self.targets()?;
1148 if self.accept_word("REMAINDER") {
1149 let r = self.reference()?;
1150 remainder = Some((Target { r, rounded: false }, dividend.clone(), divisor.clone()));
1151 }
1152 (ArithVerb::Divide, targets.into_iter().map(|t| (t, bin(dividend.clone(), BinOp::Div, divisor.clone()))).collect())
1153 } else {
1154 let targets = self.targets()?;
1155 (ArithVerb::Divide, targets.into_iter().map(|t| (t.clone(), bin(of(&t), BinOp::Div, first.clone()))).collect())
1156 }
1157 }
1158 };
1159 let size_error = self.size_error()?;
1160 self.accept_any(&["END-ADD", "END-SUBTRACT", "END-MULTIPLY", "END-DIVIDE"]);
1161 Ok(Arith { verb, computations, remainder, size_error, pos })
1162 }
1163
1164 fn words_ahead_include(&self, word: &str) -> bool {
1166 self.tokens[self.at..]
1167 .iter()
1168 .take_while(|t| t.tok != Tok::Period && !matches!(&t.tok, Tok::Word(w) if VERBS.contains(&w.as_str()) && w != word))
1169 .any(|t| matches!(&t.tok, Tok::Word(w) if w == word))
1170 }
1171
1172 fn perform(&mut self, pos: Pos) -> R<Stmt> {
1173 let named = self.word().is_some_and(|w| !VERBS.contains(&w) && !PHRASE_WORDS.contains(&w))
1174 && self.word_at(1) != Some("TIMES")
1175 && !matches!(self.peek(), Some(Tok::Number(_)));
1176 if named {
1177 let from = self.proc_name()?;
1178 let thru = if self.accept_any(&["THRU", "THROUGH"]).is_some() { Some(self.proc_name()?) } else { None };
1179 let repeat = self.repeat()?;
1180 return Ok(Stmt::PerformProc { from, thru, repeat, pos });
1181 }
1182 let repeat = self.repeat()?;
1183 let body = self.block(&["END-PERFORM"])?;
1184 self.expect_word("END-PERFORM")?;
1185 Ok(Stmt::PerformInline { body, repeat, pos })
1186 }
1187
1188 fn invalid_key(&mut self, end: &str) -> R<Handlers> {
1190 let mut h = Handlers::default();
1191 loop {
1192 let negated = self.is_word("NOT") && self.word_at(1) == Some("INVALID");
1193 if !negated && !self.is_word("INVALID") {
1194 break;
1195 }
1196 self.at += if negated { 2 } else { 1 };
1197 self.accept_word("KEY");
1198 let body = self.block(&["NOT", end])?;
1199 if negated { h.not_on = Some(body) } else { h.on = Some(body) }
1200 }
1201 self.accept_word(end);
1202 Ok(h)
1203 }
1204
1205 fn call(&mut self, pos: Pos) -> R<Call> {
1206 let target = self.operand()?;
1207 let mut using = Vec::new();
1208 if self.accept_word("USING") {
1209 let mut mode = ArgMode::Reference;
1210 loop {
1211 if self.accept_word("BY") {
1212 mode = match self.accept_any(&["REFERENCE", "CONTENT", "VALUE"]).as_deref() {
1213 Some("CONTENT") => ArgMode::Content,
1214 Some("VALUE") => ArgMode::Value,
1215 Some(_) => ArgMode::Reference,
1216 None => return Err(self.error("REFERENCE, CONTENT or VALUE after BY")),
1217 };
1218 } else if self.accept_word("OMITTED") {
1219 using.push(Arg { mode, value: None });
1220 } else if self.starts_operand() {
1221 using.push(Arg { mode, value: Some(self.operand()?) });
1222 } else {
1223 break;
1224 }
1225 }
1226 }
1227 let returning = if self.accept_word("RETURNING") { Some(self.reference()?) } else { None };
1228 let (mut on_exception, mut not_on_exception) = (None, None);
1229 loop {
1230 let negated = self.is_word("NOT") && matches!(self.word_at(1), Some("ON" | "EXCEPTION" | "OVERFLOW"));
1231 if !negated && !(self.is_word("ON") || self.is_word("EXCEPTION") || self.is_word("OVERFLOW")) {
1232 break;
1233 }
1234 if negated {
1235 self.at += 1;
1236 }
1237 self.accept_word("ON");
1238 if self.accept_any(&["EXCEPTION", "OVERFLOW"]).is_none() {
1239 return Err(self.error("EXCEPTION or OVERFLOW"));
1240 }
1241 let body = self.block(&["NOT", "END-CALL"])?;
1242 if negated { not_on_exception = Some(body) } else { on_exception = Some(body) }
1243 }
1244 self.accept_word("END-CALL");
1245 Ok(Call { target, using, returning, on_exception, not_on_exception, pos })
1246 }
1247
1248 fn overflow(&mut self, end: &str) -> R<OnPhrases> {
1250 let (mut on, mut not_on) = (None, None);
1251 loop {
1252 let negated = self.is_word("NOT") && matches!(self.word_at(1), Some("ON" | "OVERFLOW"));
1253 if !negated && !(self.is_word("ON") || self.is_word("OVERFLOW")) {
1254 break;
1255 }
1256 if negated {
1257 self.at += 1;
1258 }
1259 self.accept_word("ON");
1260 self.expect_word("OVERFLOW")?;
1261 let body = self.block(&["NOT", end])?;
1262 if negated { not_on = Some(body) } else { on = Some(body) }
1263 }
1264 self.accept_word(end);
1265 Ok((on, not_on))
1266 }
1267
1268 fn string(&mut self, pos: Pos) -> R<StringStmt> {
1269 let mut sources = Vec::new();
1270 while !self.is_word("INTO") {
1271 let mut group = Vec::new();
1272 while self.starts_operand() && !self.is_word("DELIMITED") {
1273 group.push(self.operand()?);
1274 }
1275 if group.is_empty() {
1276 return Err(self.error("a sending item"));
1277 }
1278 let delimiter = if self.accept_word("DELIMITED") {
1279 self.accept_word("BY");
1280 if self.accept_word("SIZE") { Delimiter::Size } else { Delimiter::By(self.operand()?) }
1281 } else {
1282 Delimiter::Size
1283 };
1284 sources.extend(group.into_iter().map(|op| (op, delimiter.clone())));
1285 }
1286 self.expect_word("INTO")?;
1287 let into = self.reference()?;
1288 let pointer = if self.accept_word("WITH") || self.is_word("POINTER") {
1289 self.expect_word("POINTER")?;
1290 Some(self.reference()?)
1291 } else {
1292 None
1293 };
1294 let (on_overflow, not_on_overflow) = self.overflow("END-STRING")?;
1295 Ok(StringStmt { sources, into, pointer, on_overflow, not_on_overflow, pos })
1296 }
1297
1298 fn unstring(&mut self, pos: Pos) -> R<Unstring> {
1299 let source = self.reference()?;
1300 let mut delimiters = Vec::new();
1301 if self.accept_word("DELIMITED") {
1302 self.accept_word("BY");
1303 loop {
1304 let all = self.accept_word("ALL");
1305 delimiters.push((all, self.operand()?));
1306 if !self.accept_word("OR") {
1307 break;
1308 }
1309 }
1310 }
1311 self.expect_word("INTO")?;
1312 let mut into = Vec::new();
1313 while self.starts_ref() {
1314 let target = self.reference()?;
1315 let delimiter_in = if self.accept_word("DELIMITER") {
1316 self.accept_word("IN");
1317 Some(self.reference()?)
1318 } else {
1319 None
1320 };
1321 let count_in = if self.accept_word("COUNT") {
1322 self.accept_word("IN");
1323 Some(self.reference()?)
1324 } else {
1325 None
1326 };
1327 into.push(UnstringInto { target, delimiter_in, count_in });
1328 }
1329 if into.is_empty() {
1330 return Err(self.error("a receiving item after INTO"));
1331 }
1332 let pointer = if self.accept_word("WITH") || self.is_word("POINTER") {
1333 self.expect_word("POINTER")?;
1334 Some(self.reference()?)
1335 } else {
1336 None
1337 };
1338 let tallying = if self.accept_word("TALLYING") {
1339 self.accept_word("IN");
1340 Some(self.reference()?)
1341 } else {
1342 None
1343 };
1344 let (on_overflow, not_on_overflow) = self.overflow("END-UNSTRING")?;
1345 Ok(Unstring { source, delimiters, into, pointer, tallying, on_overflow, not_on_overflow, pos })
1346 }
1347
1348 fn bounds(&mut self) -> R<Vec<Bound>> {
1349 let mut bounds = Vec::new();
1350 while let Some(side) = self.accept_any(&["BEFORE", "AFTER"]) {
1351 self.accept_word("INITIAL");
1352 bounds.push(Bound { after: side == "AFTER", value: self.operand()? });
1353 }
1354 Ok(bounds)
1355 }
1356
1357 fn inspect(&mut self, pos: Pos) -> R<Inspect> {
1358 let target = self.reference()?;
1359 let (mut tallying, mut replacing, mut converting) = (Vec::new(), Vec::new(), None);
1360 if self.accept_word("TALLYING") {
1361 while self.starts_ref() && self.word_at(1) == Some("FOR") || self.starts_ref() && !self.is_word("REPLACING") && self.tally_counter_ahead() {
1362 let counter = self.reference()?;
1363 self.expect_word("FOR")?;
1364 loop {
1365 if self.accept_word("CHARACTERS") {
1366 tallying.push(InspectPhrase { mode: InspectMode::Characters, pattern: None, by: None, counter: Some(counter.clone()), bounds: self.bounds()? });
1367 } else if let Some(mode) = self.accept_any(&["ALL", "LEADING"]) {
1368 let mode = if mode == "ALL" { InspectMode::All } else { InspectMode::Leading };
1369 loop {
1370 let pattern = self.operand()?;
1371 tallying.push(InspectPhrase { mode, pattern: Some(pattern), by: None, counter: Some(counter.clone()), bounds: self.bounds()? });
1372 if !self.starts_operand() || self.word_at(1) == Some("FOR") || self.is_word("ALL") || self.is_word("LEADING") {
1373 break;
1374 }
1375 }
1376 } else {
1377 break;
1378 }
1379 }
1380 }
1381 }
1382 if self.accept_word("REPLACING") {
1383 loop {
1384 if self.accept_word("CHARACTERS") {
1385 self.expect_word("BY")?;
1386 let by = self.operand()?;
1387 replacing.push(InspectPhrase { mode: InspectMode::Characters, pattern: None, by: Some(by), counter: None, bounds: self.bounds()? });
1388 } else if let Some(mode) = self.accept_any(&["ALL", "LEADING", "FIRST"]) {
1389 let mode = match mode.as_str() {
1390 "ALL" => InspectMode::All,
1391 "LEADING" => InspectMode::Leading,
1392 _ => InspectMode::First,
1393 };
1394 loop {
1395 let pattern = self.operand()?;
1396 self.expect_word("BY")?;
1397 let by = self.operand()?;
1398 replacing.push(InspectPhrase { mode, pattern: Some(pattern), by: Some(by), counter: None, bounds: self.bounds()? });
1399 if !self.starts_operand() || self.is_word("ALL") || self.is_word("LEADING") || self.is_word("FIRST") {
1400 break;
1401 }
1402 }
1403 } else {
1404 break;
1405 }
1406 }
1407 }
1408 if self.accept_word("CONVERTING") {
1409 let from = self.operand()?;
1410 self.expect_word("TO")?;
1411 let to = self.operand()?;
1412 converting = Some((from, to, self.bounds()?));
1413 }
1414 if tallying.is_empty() && replacing.is_empty() && converting.is_none() {
1415 return Err(self.error("TALLYING, REPLACING or CONVERTING"));
1416 }
1417 Ok(Inspect { target, tallying, replacing, converting, pos })
1418 }
1419
1420 fn tally_counter_ahead(&self) -> bool {
1422 let mut i = self.at + 1;
1423 if self.tokens.get(i).map(|t| &t.tok) == Some(&Tok::LParen) {
1424 let mut depth = 0;
1425 while let Some(t) = self.tokens.get(i) {
1426 match t.tok {
1427 Tok::LParen => depth += 1,
1428 Tok::RParen => {
1429 depth -= 1;
1430 if depth == 0 {
1431 i += 1;
1432 break;
1433 }
1434 }
1435 _ => {}
1436 }
1437 i += 1;
1438 }
1439 }
1440 matches!(self.tokens.get(i).map(|t| &t.tok), Some(Tok::Word(w)) if w == "FOR")
1441 }
1442
1443 fn search(&mut self, pos: Pos) -> R<Search> {
1444 let all = self.accept_word("ALL");
1445 let table = self.reference()?;
1446 let varying = if self.accept_word("VARYING") { Some(self.reference()?) } else { None };
1447 let at_end = if self.accept_word("AT") || self.is_word("END") {
1448 self.expect_word("END")?;
1449 Some(self.block(&["WHEN"])?)
1450 } else {
1451 None
1452 };
1453 let mut whens = Vec::new();
1454 while self.accept_word("WHEN") {
1455 let cond = self.cond()?;
1456 let body = self.block(&["WHEN", "END-SEARCH"])?;
1457 whens.push((cond, body));
1458 }
1459 if whens.is_empty() {
1460 return Err(self.error("WHEN"));
1461 }
1462 self.accept_word("END-SEARCH");
1463 Ok(Search { table, all, varying, at_end, whens, pos })
1464 }
1465
1466 fn set(&mut self) -> R<SetStmt> {
1467 if self.is_word("ADDRESS") && self.word_at(1) == Some("OF") {
1468 let mut targets = Vec::new();
1469 while self.is_word("ADDRESS") && self.word_at(1) == Some("OF") {
1470 self.at += 2;
1471 targets.push(self.reference()?);
1472 }
1473 self.expect_word("TO")?;
1474 return Ok(SetStmt::AddressOf { targets, value: self.operand()? });
1475 }
1476 let targets = self.refs()?;
1477 if self.accept_word("TO") {
1478 if self.accept_word("TRUE") {
1479 return Ok(SetStmt::ConditionTrue(targets));
1480 }
1481 return Ok(SetStmt::To { targets, value: self.operand()? });
1482 }
1483 match self.accept_any(&["UP", "DOWN"]).as_deref() {
1484 Some(direction) => {
1485 self.expect_word("BY")?;
1486 Ok(SetStmt::UpDown { targets, down: direction == "DOWN", by: self.expr()? })
1487 }
1488 None => Err(self.error("TO, UP BY or DOWN BY")),
1489 }
1490 }
1491
1492 fn proc_name(&mut self) -> R<ProcName> {
1493 let name = self.name("a procedure name")?;
1494 let section = if self.accept_any(&["OF", "IN"]).is_some() { Some(self.name("a section name")?) } else { None };
1495 Ok(ProcName { name, section })
1496 }
1497
1498 fn evaluate(&mut self, pos: Pos) -> R<Stmt> {
1499 let mut subjects = vec![self.subject()?];
1500 while self.accept_word("ALSO") {
1501 subjects.push(self.subject()?);
1502 }
1503 let (mut whens, mut other) = (Vec::new(), Vec::new());
1504 while self.is_word("WHEN") {
1505 if self.word_at(1) == Some("OTHER") {
1506 self.at += 2;
1507 other = self.block(&["END-EVALUATE"])?;
1508 break;
1509 }
1510 let mut alternatives = Vec::new();
1511 while self.is_word("WHEN") && self.word_at(1) != Some("OTHER") {
1512 self.at += 1;
1513 let mut objects = Vec::new();
1514 for (k, subject) in subjects.iter().enumerate() {
1515 if k > 0 {
1516 self.expect_word("ALSO")?;
1517 }
1518 objects.push(self.object(subject)?);
1519 }
1520 alternatives.push(objects);
1521 }
1522 let body = self.block(&["WHEN", "END-EVALUATE"])?;
1523 whens.push(When { alternatives, body });
1524 }
1525 self.accept_word("END-EVALUATE");
1526 Ok(Stmt::Evaluate { subjects, whens, other, pos })
1527 }
1528
1529 fn subject(&mut self) -> R<Subject> {
1530 if let Some(b) = self.accept_any(&["TRUE", "FALSE"]) {
1531 return Ok(Subject::Bool(b == "TRUE"));
1532 }
1533 let save = self.at;
1534 self.expr()?;
1535 let conditional = self.relop_ahead(0) || self.is_word("IS") || self.is_word("NOT")
1536 || self.word().is_some_and(|w| matches!(w, "NUMERIC" | "ALPHABETIC" | "POSITIVE" | "NEGATIVE" | "ZERO"));
1537 self.at = save;
1538 Ok(if conditional { Subject::Cond(self.cond()?) } else { Subject::Expr(self.expr()?) })
1539 }
1540
1541 fn object(&mut self, subject: &Subject) -> R<Object> {
1542 if self.accept_word("ANY") {
1543 return Ok(Object::Any);
1544 }
1545 if let Some(b) = self.accept_any(&["TRUE", "FALSE"]) {
1546 return Ok(Object::Bool(b == "TRUE"));
1547 }
1548 if !matches!(subject, Subject::Expr(_)) {
1549 return Ok(Object::Cond(self.cond()?));
1550 }
1551 let not = self.accept_word("NOT");
1552 let from = self.expr()?;
1553 let thru = if self.accept_any(&["THRU", "THROUGH"]).is_some() { Some(self.expr()?) } else { None };
1554 Ok(Object::Value { not, from, thru })
1555 }
1556
1557 fn repeat(&mut self) -> R<Loop> {
1558 let mut test_after = false;
1559 if self.accept_word("WITH") || self.is_word("TEST") {
1560 self.expect_word("TEST")?;
1561 test_after = self.accept_any(&["BEFORE", "AFTER"]).as_deref() == Some("AFTER");
1562 }
1563 if self.accept_word("UNTIL") {
1564 return Ok(Loop::Until { cond: self.cond()?, test_after });
1565 }
1566 if self.accept_word("VARYING") {
1567 let var = self.reference()?;
1568 self.expect_word("FROM")?;
1569 let from = self.expr()?;
1570 self.expect_word("BY")?;
1571 let by = self.expr()?;
1572 self.expect_word("UNTIL")?;
1573 let until = self.cond()?;
1574 if self.is_word("AFTER") {
1575 return Err(self.error("PERFORM VARYING ... AFTER is not supported yet"));
1576 }
1577 return Ok(Loop::Varying { varying: Box::new(Varying { var, from, by, until }), test_after });
1578 }
1579 if self.starts_operand() && self.word_at(1) == Some("TIMES") || matches!(self.peek(), Some(Tok::Number(_))) {
1580 let count = self.expr()?;
1581 self.expect_word("TIMES")?;
1582 return Ok(Loop::Times(count));
1583 }
1584 Ok(Loop::Once)
1585 }
1586
1587 fn starts_ref(&self) -> bool {
1588 self.word().is_some_and(|w| !VERBS.contains(&w) && !PHRASE_WORDS.contains(&w) && figurative(w).is_none() && w != "FUNCTION")
1589 && !self.paragraph_header()
1590 }
1591
1592 fn starts_operand(&self) -> bool {
1593 match self.peek() {
1594 Some(Tok::Alnum(_) | Tok::Hex(_) | Tok::National(_) | Tok::Number(_)) => true,
1595 Some(Tok::Word(w)) => {
1596 (figurative(w).is_some() || matches!(w.as_str(), "ALL" | "FUNCTION" | "LENGTH" | "ADDRESS" | "DFHRESP") || self.starts_ref()) && !self.paragraph_header()
1597 }
1598 _ => false,
1599 }
1600 }
1601
1602 fn operand(&mut self) -> R<Operand> {
1603 let pos = self.pos();
1604 match self.peek() {
1605 Some(Tok::Word(w)) if w == "FUNCTION" => {
1606 self.at += 1;
1607 let name = self.name("a function name")?;
1608 let (mut args, mut modifier) = (Vec::new(), None);
1609 if self.peek() == Some(&Tok::LParen) && !self.refmod_ahead() {
1610 self.at += 1;
1611 while !self.accept(&Tok::RParen) {
1612 if let Some(m) = self.accept_any(&["LEADING", "TRAILING"]) {
1613 modifier = Some(m);
1614 continue;
1615 }
1616 args.push(self.expr()?);
1617 }
1618 }
1619 let refmod = self.refmod()?;
1620 Ok(Operand::Function(FunctionCall { name, args, modifier, refmod, pos }))
1621 }
1622 Some(Tok::Word(w)) if w == "LENGTH" && self.word_at(1) == Some("OF") => {
1623 self.at += 2;
1624 Ok(Operand::LengthOf(self.reference()?))
1625 }
1626 Some(Tok::Word(w)) if w == "DFHRESP" && self.peek_at(1) == Some(&Tok::LParen) => {
1627 self.at += 2;
1628 let condition = self.name("a CICS condition")?;
1629 self.expect(&Tok::RParen, "')'")?;
1630 let code = crate::system::resp_code(&condition).ok_or_else(|| Error::at(pos, format!("DFHRESP({condition}): not a CICS condition ironwork for COBOL knows")))?;
1631 Ok(Operand::Literal(Literal::Number(code.to_string())))
1632 }
1633 Some(Tok::Word(w)) if w == "ADDRESS" && self.word_at(1) == Some("OF") => {
1634 self.at += 2;
1635 Ok(Operand::AddressOf(self.reference()?))
1636 }
1637 Some(Tok::Word(w)) if figurative(w).is_some() || w == "ALL" => Ok(Operand::Literal(self.literal()?)),
1638 Some(Tok::Word(_)) => Ok(Operand::Ref(self.reference()?)),
1639 Some(Tok::Alnum(_) | Tok::Hex(_) | Tok::National(_) | Tok::Number(_)) => Ok(Operand::Literal(self.literal()?)),
1640 _ => Err(self.error("an operand")),
1641 }
1642 }
1643
1644 fn refmod_ahead(&self) -> bool {
1646 let mut depth = 0;
1647 for t in &self.tokens[self.at..] {
1648 match t.tok {
1649 Tok::LParen => depth += 1,
1650 Tok::RParen => {
1651 depth -= 1;
1652 if depth == 0 {
1653 return false;
1654 }
1655 }
1656 Tok::Colon if depth == 1 => return true,
1657 Tok::Period => return false,
1658 _ => {}
1659 }
1660 }
1661 false
1662 }
1663
1664 fn refmod(&mut self) -> R<Option<RefMod>> {
1665 if self.peek() != Some(&Tok::LParen) || !self.refmod_ahead() {
1666 return Ok(None);
1667 }
1668 self.at += 1;
1669 let start = Box::new(self.expr()?);
1670 self.expect(&Tok::Colon, "':'")?;
1671 let length = if self.peek() == Some(&Tok::RParen) { None } else { Some(Box::new(self.expr()?)) };
1672 self.expect(&Tok::RParen, "')'")?;
1673 Ok(Some(RefMod { start, length }))
1674 }
1675
1676 fn reference(&mut self) -> R<Ref> {
1677 let pos = self.pos();
1678 let name = self.name("a data name")?;
1679 let mut qualifiers = Vec::new();
1680 while self.accept_any(&["OF", "IN"]).is_some() {
1681 qualifiers.push(self.name("a qualifier")?);
1682 }
1683 let mut subscripts = Vec::new();
1684 if self.peek() == Some(&Tok::LParen) && !self.refmod_ahead() {
1685 self.at += 1;
1686 while !self.accept(&Tok::RParen) {
1687 subscripts.push(self.expr()?);
1688 }
1689 }
1690 let refmod = self.refmod()?;
1691 Ok(Ref { name, qualifiers, subscripts, refmod, pos })
1692 }
1693
1694 fn expr(&mut self) -> R<Expr> {
1695 let mut left = self.term()?;
1696 loop {
1697 let op = match self.peek() {
1698 Some(Tok::Plus) => BinOp::Add,
1699 Some(Tok::Minus) => BinOp::Sub,
1700 _ => return Ok(left),
1701 };
1702 self.at += 1;
1703 left = Expr::Bin(Box::new(left), op, Box::new(self.term()?));
1704 }
1705 }
1706
1707 fn term(&mut self) -> R<Expr> {
1708 let mut left = self.power()?;
1709 loop {
1710 let op = match self.peek() {
1711 Some(Tok::Star) => BinOp::Mul,
1712 Some(Tok::Slash) => BinOp::Div,
1713 _ => return Ok(left),
1714 };
1715 self.at += 1;
1716 left = Expr::Bin(Box::new(left), op, Box::new(self.power()?));
1717 }
1718 }
1719
1720 fn power(&mut self) -> R<Expr> {
1721 let mut left = self.unary()?;
1722 while self.accept(&Tok::Power) {
1723 left = Expr::Bin(Box::new(left), BinOp::Pow, Box::new(self.unary()?));
1724 }
1725 Ok(left)
1726 }
1727
1728 fn unary(&mut self) -> R<Expr> {
1729 if self.accept(&Tok::Minus) {
1730 return Ok(Expr::Neg(Box::new(self.unary()?)));
1731 }
1732 self.accept(&Tok::Plus);
1733 if self.accept(&Tok::LParen) {
1734 let e = self.expr()?;
1735 self.expect(&Tok::RParen, "')'")?;
1736 return Ok(e);
1737 }
1738 Ok(Expr::Operand(self.operand()?))
1739 }
1740
1741 fn cond(&mut self) -> R<Cond> {
1742 let mut last = None;
1743 self.or_cond(&mut last)
1744 }
1745
1746 fn or_cond(&mut self, last: &mut Option<(Expr, RelOp)>) -> R<Cond> {
1747 let mut left = self.and_cond(last)?;
1748 while self.accept_word("OR") {
1749 left = Cond::Or(Box::new(left), Box::new(self.and_cond(last)?));
1750 }
1751 Ok(left)
1752 }
1753
1754 fn and_cond(&mut self, last: &mut Option<(Expr, RelOp)>) -> R<Cond> {
1755 let mut left = self.not_cond(last)?;
1756 while self.accept_word("AND") {
1757 left = Cond::And(Box::new(left), Box::new(self.not_cond(last)?));
1758 }
1759 Ok(left)
1760 }
1761
1762 fn not_cond(&mut self, last: &mut Option<(Expr, RelOp)>) -> R<Cond> {
1763 if self.is_word("NOT") && !self.relop_ahead(1) {
1764 self.at += 1;
1765 return Ok(Cond::Not(Box::new(self.not_cond(last)?)));
1766 }
1767 self.primary_cond(last)
1768 }
1769
1770 fn relop_ahead(&self, ahead: usize) -> bool {
1771 matches!(self.peek_at(ahead), Some(Tok::Eq | Tok::Lt | Tok::Gt | Tok::Le | Tok::Ge))
1772 || matches!(self.word_at(ahead), Some("EQUAL" | "GREATER" | "LESS"))
1773 }
1774
1775 fn primary_cond(&mut self, last: &mut Option<(Expr, RelOp)>) -> R<Cond> {
1776 if self.peek() == Some(&Tok::LParen) {
1777 let save = self.at;
1778 self.at += 1;
1779 let mut inner_last = None;
1780 if let Ok(c) = self.or_cond(&mut inner_last)
1781 && self.accept(&Tok::RParen)
1782 && !matches!(self.peek(), Some(Tok::Plus | Tok::Minus | Tok::Star | Tok::Slash | Tok::Power))
1783 && !self.relop_ahead(0)
1784 && !self.is_word("IS")
1785 {
1786 return Ok(c);
1787 }
1788 self.at = save;
1789 }
1790 let left = self.expr()?;
1791 self.accept_word("IS");
1792 let negated = self.is_word("NOT") && {
1793 self.at += 1;
1794 true
1795 };
1796 let wrap = |c: Cond| if negated { Cond::Not(Box::new(c)) } else { c };
1797 if let Some(op) = self.relop()? {
1798 let right = self.expr()?;
1799 *last = Some((left.clone(), op));
1800 return Ok(wrap(Cond::Rel(left, op, right)));
1801 }
1802 if let Some(class) = self.accept_any(&["NUMERIC", "ALPHABETIC", "POSITIVE", "NEGATIVE", "ZERO"]) {
1803 let class = match class.as_str() {
1804 "NUMERIC" => Class::Numeric,
1805 "ALPHABETIC" => Class::Alphabetic,
1806 "POSITIVE" => Class::Positive,
1807 "NEGATIVE" => Class::Negative,
1808 _ => Class::Zero,
1809 };
1810 return Ok(wrap(Cond::Class(left, class)));
1811 }
1812 if negated {
1813 return Err(self.error("a relational operator or class after NOT"));
1814 }
1815 match (left, last.clone()) {
1816 (Expr::Operand(Operand::Ref(name)), Some((subject, op))) if self.abbreviation_context() => Ok(Cond::NameOrRel { subject, op, name }),
1817 (right, Some((subject, op))) if !matches!(&right, Expr::Operand(Operand::Ref(_))) => Ok(Cond::Rel(subject, op, right)),
1818 (Expr::Operand(Operand::Ref(r)), _) => Ok(Cond::Name(r)),
1819 (_, _) => Err(self.error("a relational operator")),
1820 }
1821 }
1822
1823 fn abbreviation_context(&self) -> bool {
1825 self.at > 1 && matches!(self.tokens.get(self.at.saturating_sub(2)).map(|t| &t.tok), Some(Tok::Word(w)) if w == "OR" || w == "AND")
1826 }
1827
1828 fn relop(&mut self) -> R<Option<RelOp>> {
1829 let op = match self.peek() {
1830 Some(Tok::Eq) => RelOp::Eq,
1831 Some(Tok::Lt) => RelOp::Lt,
1832 Some(Tok::Gt) => RelOp::Gt,
1833 Some(Tok::Le) => RelOp::Le,
1834 Some(Tok::Ge) => RelOp::Ge,
1835 Some(Tok::Word(w)) if w == "EQUAL" => {
1836 self.at += 1;
1837 self.accept_word("TO");
1838 return Ok(Some(RelOp::Eq));
1839 }
1840 Some(Tok::Word(w)) if w == "GREATER" || w == "LESS" => {
1841 let greater = w == "GREATER";
1842 self.at += 1;
1843 self.accept_word("THAN");
1844 let or_equal = self.accept_word("OR");
1845 if or_equal {
1846 self.expect_word("EQUAL")?;
1847 self.accept_word("TO");
1848 }
1849 return Ok(Some(match (greater, or_equal) {
1850 (true, false) => RelOp::Gt,
1851 (true, true) => RelOp::Ge,
1852 (false, false) => RelOp::Lt,
1853 (false, true) => RelOp::Le,
1854 }));
1855 }
1856 _ => return Ok(None),
1857 };
1858 self.at += 1;
1859 Ok(Some(op))
1860 }
1861}
1862
1863fn host_variables(sql: &str, pos: Pos) -> Vec<Ref> {
1865 let chars: Vec<char> = sql.chars().collect();
1866 let (mut out, mut i, mut quote) = (Vec::new(), 0, None);
1867 while i < chars.len() {
1868 let c = chars[i];
1869 match quote {
1870 Some(q) if c == q => quote = None,
1871 Some(_) => {}
1872 None if c == '\'' || c == '"' => quote = Some(c),
1873 None if c == ':' && chars.get(i + 1).is_some_and(|n| n.is_ascii_alphanumeric()) => {
1874 let start = i + 1;
1875 let mut end = start;
1876 while end < chars.len() && (chars[end].is_ascii_alphanumeric() || matches!(chars[end], '-' | '_' | '.')) {
1877 end += 1;
1878 }
1879 let path: String = chars[start..end].iter().collect::<String>().to_ascii_uppercase();
1880 let mut parts: Vec<String> = path.trim_end_matches('.').split('.').map(str::to_owned).collect();
1881 let name = parts.pop().unwrap_or_default();
1882 parts.reverse();
1883 out.push(Ref { name, qualifiers: parts, subscripts: Vec::new(), refmod: None, pos });
1884 i = end;
1885 continue;
1886 }
1887 None => {}
1888 }
1889 i += 1;
1890 }
1891 out
1892}
1893
1894fn cics_options(body: &str) -> Vec<(String, Option<ExecArg>)> {
1896 let chars: Vec<char> = body.chars().collect();
1897 let (mut out, mut i) = (Vec::new(), 0);
1898 while i < chars.len() {
1899 if chars[i].is_whitespace() {
1900 i += 1;
1901 continue;
1902 }
1903 let start = i;
1904 while i < chars.len() && !chars[i].is_whitespace() && chars[i] != '(' {
1905 i += 1;
1906 }
1907 let name: String = chars[start..i].iter().collect::<String>().to_ascii_uppercase();
1908 let mut j = i;
1909 while j < chars.len() && chars[j].is_whitespace() {
1910 j += 1;
1911 }
1912 if j < chars.len() && chars[j] == '(' {
1913 let (mut depth, mut quote, mut k) = (0, None, j);
1914 while k < chars.len() {
1915 match (quote, chars[k]) {
1916 (Some(q), c) if c == q => quote = None,
1917 (Some(_), _) => {}
1918 (None, '\'' | '"') => quote = Some(chars[k]),
1919 (None, '(') => depth += 1,
1920 (None, ')') => {
1921 depth -= 1;
1922 if depth == 0 {
1923 break;
1924 }
1925 }
1926 _ => {}
1927 }
1928 k += 1;
1929 }
1930 let arg: String = chars[j + 1..k.min(chars.len())].iter().collect();
1931 out.push((name, Some(ExecArg::Text(arg.trim().to_owned()))));
1932 i = (k + 1).min(chars.len());
1933 } else {
1934 out.push((name, None));
1935 }
1936 }
1937 out
1938}
1939
1940fn operand_of(text: &str, pos: Pos) -> Option<Operand> {
1942 let source = crate::source::Source { text: text.to_owned(), positions: vec![pos; text.chars().count()], options: Vec::new() };
1943 let tokens = crate::lexer::lex(&source).ok()?;
1944 let mut p = Parser { tokens: &tokens, at: 0, exec_declarations: Vec::new(), cics: false };
1945 let op = p.operand().ok()?;
1946 (p.at == tokens.len()).then_some(op)
1947}
1948
1949fn system_entries(member: &str) -> R<Vec<DataEntry>> {
1951 system_text_entries(&crate::system::member(member).unwrap_or_default())
1952}
1953
1954fn system_text_entries(text: &str) -> R<Vec<DataEntry>> {
1955 let source = crate::source::read(text)?;
1956 let tokens = crate::lexer::lex(&source)?;
1957 Parser { tokens: &tokens, at: 0, exec_declarations: Vec::new(), cics: false }.data_entries()
1958}
1959
1960const SELECT_CLAUSES: &[&str] = &[
1962 "ASSIGN", "ORGANIZATION", "ACCESS", "FILE", "STATUS", "RECORD", "ALTERNATE", "RELATIVE", "LINE", "SEQUENTIAL", "INDEXED", "RESERVE",
1963 "PADDING", "LOCK", "SHARING",
1964];
1965
1966fn is_clause_word(w: &str) -> bool {
1967 matches!(
1968 w,
1969 "PIC" | "PICTURE" | "USAGE" | "VALUE" | "VALUES" | "REDEFINES" | "OCCURS" | "SIGN" | "LEADING" | "TRAILING" | "JUSTIFIED"
1970 | "JUST" | "SYNC" | "SYNCHRONIZED" | "GLOBAL" | "EXTERNAL" | "BLANK"
1971 ) || usage_word(w).is_some()
1972}
1973
1974#[cfg(test)]
1975mod tests {
1976 use super::*;
1977
1978 fn program(body: &str) -> Program {
1979 let text = format!(
1980 " IDENTIFICATION DIVISION.\n PROGRAM-ID. T.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n{body}"
1981 );
1982 crate::parse(&text).unwrap_or_else(|e| panic!("{e}"))
1983 }
1984
1985 #[test]
1986 fn data_entries_with_clauses_in_any_order() {
1987 let p = program(
1988 " 01 G.\n 05 A PIC S9(3)V99 COMP-3 VALUE -1.5.\n 05 B REDEFINES A PIC X(3).\n 05 FILLER PIC X(2) VALUE SPACES.\n 05 T PIC 9 OCCURS 3 TIMES.\n PROCEDURE DIVISION.\n GOBACK.\n",
1989 );
1990 let ws = &p.working_storage;
1991 assert_eq!(ws.len(), 5);
1992 assert_eq!((ws[1].picture.as_deref(), ws[1].usage, &ws[1].value), (Some("S9(3)V99"), Some(Usage::Packed), &Some(Literal::Number("-1.5".into()))));
1993 assert_eq!(ws[2].redefines.as_deref(), Some("A"));
1994 assert_eq!((ws[3].name.as_deref(), &ws[3].value), (None, &Some(Literal::Figurative(Figurative::Space))));
1995 assert_eq!(ws[4].occurs, Some(3));
1996 }
1997
1998 #[test]
1999 fn paragraphs_and_nested_if() {
2000 let p = program(
2001 " 01 A PIC X.\n PROCEDURE DIVISION.\n MAIN-LINE.\n PERFORM P2\n GOBACK.\n P2.\n IF A < 'B'\n MOVE 'L' TO A\n ELSE\n IF A = 'B'\n MOVE 'E' TO A\n ELSE\n MOVE 'G' TO A\n END-IF\n END-IF.\n",
2002 );
2003 assert_eq!(p.paragraphs.iter().map(|p| p.name.as_str()).collect::<Vec<_>>(), ["MAIN-LINE", "P2"]);
2004 let Stmt::If { otherwise, .. } = &p.paragraphs[1].statements[0] else { panic!() };
2005 assert!(matches!(otherwise[0], Stmt::If { .. }));
2006 }
2007
2008 #[test]
2009 fn inline_perform_varying_with_a_compound_condition() {
2010 let p = program(
2011 " 01 I PIC 9(4) COMP.\n 01 J PIC 9(4) COMP.\n PROCEDURE DIVISION.\n PERFORM VARYING J FROM 0 BY 1\n UNTIL J > 31 OR I + J > 64\n CONTINUE\n END-PERFORM.\n",
2012 );
2013 let Stmt::PerformInline { repeat: Loop::Varying { varying, .. }, .. } = &p.paragraphs[0].statements[0] else { panic!() };
2014 assert!(matches!(varying.until, Cond::Or(..)));
2015 }
2016
2017 #[test]
2018 fn functions_reference_modification_and_length_of() {
2019 let p = program(
2020 " 01 X PIC X(9).\n 01 N PIC 9(4) COMP.\n PROCEDURE DIVISION.\n COMPUTE N = FUNCTION ORD(X(N + 1:1)) - 1\n MOVE FUNCTION NATIONAL-OF(X, 1047) TO X\n MOVE LENGTH OF X TO N.\n",
2021 );
2022 let Stmt::Compute { expr: Expr::Bin(left, BinOp::Sub, _), .. } = &p.paragraphs[0].statements[0] else { panic!() };
2023 let Expr::Operand(Operand::Function(f)) = left.as_ref() else { panic!() };
2024 let Expr::Operand(Operand::Ref(r)) = &f.args[0] else { panic!() };
2025 assert!(r.refmod.is_some());
2026 assert!(matches!(&p.paragraphs[0].statements[2], Stmt::Move { from: Operand::LengthOf(_), .. }));
2027 }
2028
2029 #[test]
2030 fn divide_giving_remainder_and_add_to() {
2031 let p = program(
2032 " 01 N PIC 9(4) COMP.\n 01 H PIC 9(4) COMP.\n 01 L PIC 9(4) COMP.\n PROCEDURE DIVISION.\n DIVIDE N BY 16 GIVING H REMAINDER L\n ADD 1 TO H ROUNDED.\n",
2033 );
2034 let Stmt::Arith(a) = &p.paragraphs[0].statements[0] else { panic!() };
2035 assert_eq!(a.verb, ArithVerb::Divide);
2036 assert!(a.remainder.is_some());
2037 let Stmt::Arith(add) = &p.paragraphs[0].statements[1] else { panic!() };
2038 assert!(add.computations[0].0.rounded);
2039 }
2040
2041 #[test]
2042 fn abbreviated_combined_relation() {
2043 let p = program(" 01 A PIC 9.\n PROCEDURE DIVISION.\n IF A = 1 OR 2 CONTINUE END-IF.\n");
2044 let Stmt::If { cond: Cond::Or(_, right), .. } = &p.paragraphs[0].statements[0] else { panic!() };
2045 assert!(matches!(right.as_ref(), Cond::Rel(_, RelOp::Eq, _)));
2046 }
2047
2048 #[test]
2049 fn indexed_select_clauses_and_keyed_statements() {
2050 let text = [
2051 " IDENTIFICATION DIVISION.\n PROGRAM-ID. T.\n ENVIRONMENT DIVISION.\n INPUT-OUTPUT SECTION.\n FILE-CONTROL.\n",
2052 " SELECT K ASSIGN TO KDD ORGANIZATION IS INDEXED\n",
2053 " ACCESS MODE IS DYNAMIC RECORD KEY IS K-ID\n",
2054 " ALTERNATE KEY K-ALT WITH DUPLICATES.\n",
2055 " SELECT N ASSIGN TO NDD STATUS N-FS N-VS ORGANIZATION\n",
2056 " INDEXED FILE STATUS IS N-FS RECORD N-ID.\n",
2057 " DATA DIVISION.\n FILE SECTION.\n FD K.\n 01 K-REC.\n 05 K-ID PIC XX.\n 05 K-ALT PIC XX.\n",
2058 " FD N.\n 01 N-ID PIC XX.\n WORKING-STORAGE SECTION.\n 01 N-FS PIC XX.\n 01 N-VS PIC X(6).\n",
2059 " PROCEDURE DIVISION.\n",
2060 " READ K NEXT RECORD AT END CONTINUE\n",
2061 " NOT AT END CONTINUE END-READ\n",
2062 " READ K KEY IS K-ALT INVALID KEY CONTINUE END-READ\n",
2063 " START K KEY IS NOT LESS THAN K-ID\n",
2064 " INVALID KEY CONTINUE NOT INVALID KEY CONTINUE\n",
2065 " END-START\n",
2066 " REWRITE K-REC INVALID KEY CONTINUE END-REWRITE\n",
2067 " DELETE K RECORD END-DELETE.\n",
2068 ]
2069 .concat();
2070 let p = crate::parse(&text).unwrap_or_else(|e| panic!("{e}"));
2071 let f = &p.files[0];
2072 assert_eq!((f.organization, f.access), (Organization::Indexed, Access::Dynamic));
2073 assert_eq!(f.record_key.as_ref().map(|r| r.name.as_str()), Some("K-ID"));
2074 assert_eq!(f.alternate_keys.iter().map(|(r, d)| (r.name.as_str(), *d)).collect::<Vec<_>>(), [("K-ALT", true)]);
2075 let n = &p.files[1];
2076 assert_eq!((n.organization, n.record_key.as_ref().map(|r| r.name.as_str())), (Organization::Indexed, Some("N-ID")));
2077 let s = &p.paragraphs[0].statements;
2078 let Stmt::Read(r) = &s[0] else { panic!() };
2079 assert!(r.next && !r.previous && r.at_end.on.is_some() && r.at_end.not_on.is_some());
2080 let Stmt::Read(r) = &s[1] else { panic!() };
2081 assert!(r.key.is_some() && r.invalid.on.is_some() && !r.next);
2082 let Stmt::Start { key: Some((RelOp::Ge, _)), invalid, .. } = &s[2] else { panic!() };
2083 assert!(invalid.on.is_some() && invalid.not_on.is_some());
2084 assert!(matches!(&s[3], Stmt::Rewrite { invalid, .. } if invalid.on.is_some()));
2085 assert!(matches!(&s[4], Stmt::Delete { .. }));
2086 }
2087
2088 #[test]
2089 fn unsupported_statements_are_named() {
2090 let text = " IDENTIFICATION DIVISION.\n PROGRAM-ID. T.\n PROCEDURE DIVISION.\n SORT F ON ASCENDING KEY K USING A GIVING B.\n";
2091 assert!(crate::parse(text).unwrap_err().message.contains("SORT"));
2092 }
2093}