/* ** Module : preproc-q.txt ** Abstract : Developer notes of preprocessor investigations and testing ** ** Copyright (c) 2004-2017, Golden Code Development Corporation. ** ** -#- -I- --Date-- -T- --JPRM-- ----------------Description----------------- ** 001 ECF 20041201 NEW @18845 Added intro notes to NVS' document. ** 002 GES 20070328 CHG @32630 Added research and rewrote section XIV to ** fully reflect findings. */ /* ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU Affero General Public License as ** published by the Free Software Foundation, either version 3 of the ** License, or (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU Affero General Public License for more details. ** ** You may find a copy of the GNU Affero GPL version 3 at the following ** location: https://www.gnu.org/licenses/agpl-3.0.en.html ** ** Additional terms under GNU Affero GPL version 3 section 7: ** ** Under Section 7 of the GNU Affero GPL version 3, the following additional ** terms apply to the works covered under the License. These additional terms ** are non-permissive additional terms allowed under Section 7 of the GNU ** Affero GPL version 3 and may not be removed by you. ** ** 0. Attribution Requirement. ** ** You must preserve all legal notices or author attributions in the covered ** work or Appropriate Legal Notices displayed by works containing the covered ** work. You may not remove from the covered work any author or developer ** credit already included within the covered work. ** ** 1. No License To Use Trademarks. ** ** This license does not grant any license or rights to use the trademarks ** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks ** of Golden Code Development Corporation. You are not authorized to use the ** name Golden Code, FWD, or the names of any author or contributor, for ** publicity purposes without written authorization. ** ** 2. No Misrepresentation of Affiliation. ** ** You may not represent yourself as Golden Code Development Corporation or FWD. ** ** You may not represent yourself for publicity purposes as associated with ** Golden Code Development Corporation, FWD, or any author or contributor to ** the covered work, without written authorization. ** ** 3. No Misrepresentation of Source or Origin. ** ** You may not represent the covered work as solely your work. All modified ** versions of the covered work must be marked in a reasonable way to make it ** clear that the modified work is not originating from Golden Code Development ** Corporation or FWD. All modified versions must contain the notices of ** attribution required in this license. */ ***************************************************************************** The data in this "Developer's Notebook" represent a running log of various investigations which went on during the development of the Golden Code implementation of its preprocessor for Progress source code. Please note that this document is relatively unstructured, since items were appended as they were encountered during development. It is not intended to be formal documentation, but may be helpful for anyone trying to understand specific details about the implementation. "Progress" is a trademark of Progress Software Corporation ***************************************************************************** PROGRESS PREPROCESSOR NOTES =========================== I All statements not producing a text -------------------------------------- These are: &GLOBAL-DEFINE, &IF-&THEN-&ELSEIF-&ELSE-&ENDIF, &SCOPED-DEFINE, &MESSAGE, &UNDEFINE, &ANALYZE-SUSPEND, &ANALYZE-RESUME. They take some space in the source stream. Do they produce any blanks on output? A1. [anything] &GLOBAL-DEFINE [comments] name rest NL and [anything] &SCOPED-DEFINE [comments] name rest NL [anything] portion is preserved on output. '&' may be anywhere within a line; it gets deleted through the NL (inclusive). comments are allowed before and after the name; they are deleted from the definition of 'name' but left on the output. Example 1: aaa /* line 4a*/ &global-define /*xx*/x line 4 /* line 4b*/ bbb /* line 5*/ produces aaa /* line 4a*/ /*xx*//* line 4b*//* line 5*/ Example 2: aaa &scoped-define y line 6 /* line 6*/ ccc /* line 7*/ produces aaa /* line 6*//* line 7*/ line 6 ccc A2. [anything] &UNDEFINE [comments] name [rest] NL [anything], [comments], [rest] and NL are left on output. '&' may be anywhere within a line; it gets deleted through the NL. NL is deleted if [rest] is empty, i.e NL is the delimiter for the name (even a single space after name preserves NL). A3. [anything] &MESSAGE rest NL [anything] portion is preserved on output. '&' may be anywhere within a line; it gets deleted through the NL (inclusive). comments are allowed within the rest; they are deleted from the message but left on the output. A4. [anything] &IF condition [anything] portion is preserved on output. '&' may be anywhere within a line; it gets deleted up to &THEN. condition may span multiple lines. comments are allowed within the condition; they are deleted from the condition but left on the output. A5. &THEN [comments1] [text] [comments2] and &ELSE [comments1] [text] [comments2] '&' may be anywhere within a line; it gets deleted up to &ELSE/&ELSEIF/&ENDIF and possibly replaced with some inserted output. [comments1] are all comments that preceed [text]; they ALWAYS are left on output (no spaces between them though). [text] and [comments2] are only left on output if the preceeding &IF condition is true/false. Example: &scop a aa &scop b bb ttt &i/*x */f /*y*/defi/*z*/ned(a)/*u*/ &then/*v*/ nnn /*p*/ do-1 &if /*l2-1*/ defined(c) &then /*l2-2 more*/ /*some more*/ ooo/*even more*/ /*l2-3*/ do2-1 &else /*l2-4*/ lll /*l2-5*/ do2-2 &endif /*l2-6*/ &else /*q*/ mmm /*r*/ do-2 &endif lineX produces ttt /*x *//*y*//*z*//*u*//*v*/nnn /*p*/ do-1 /*l2-1*//*l2-2 more*//*some more*//*l2-4*/ lll /*l2-5*/ do2-2 /*l2-6*/ /*q*/ lineX A6. &ELSEIF condition '&' may be anywhere within a line; it gets deleted up to &THEN. condition may span multiple lines. comments are allowed within the condition; they are deleted from the condition but left on the output. Example: &scop a aa &scop b bb &if defined(a) &then/*v*/ nnn /*p*/ do-1 &if /*l2-1*/ defined(c) &then /*l2-2 more*/ /*some more*/ ooo/*even more*/ /*l2-3*/ do2-1 &else /*l2-4*/ lll /*l2-5*/ do2-2 &endif /*l2-6*/ &elseif /*q*/defined(b) mmm /*rrr*/ &then &else /*q*/ mmm /*r*/ do-2 &endif lineX produces /*v*/nnn /*p*/ do-1 /*l2-1*//*l2-2 more*//*some more*//*l2-4*/ lll /*l2-5*/ do2-2 /*l2-6*/ /*q*//*rrr*//*q*/ lineX A7. [anything] &ANALYZE-SUSPEND [comments1] [text] [comments2] NL and [anything] &ANALYZE-RESUME [comments1] [text] [comments2] NL [anything], [comments1] and [comments2] are preserved on output. '&' may be anywhere within a line; it gets deleted through the NL (exclusive). Example: Line 1 &ANALYZE-SUSPEND _VERSION-NUMBER UIB_v9r12 aaa &ANALYZE-SUSPEND _VERSION-NUMBER UIB_v9r12 aaa &ANALYZE-SUSPEND aaa/*aaa*/ &ANALYZE-SUSPEND /*ccc*/bbb /*bbb*/ Line 6 &ANALYZE-RESUME aaa &ANALYZE-RESUME aaa /*aaa*/&ANALYZE-RESUME/*bbb*/ bbb Line 10 produces Line 1 aaa aaa aaa/*aaa*/ /*ccc*//*bbb*/ Line 6 aaa aaa /*aaa*//*bbb*/ Line 10 II &GLOBAL-DEFINE, &SCOPED-DEFINE -------------------------------- Multiple line definitions: &GLOBAL-DEFINE name line 1 ~ line 2 A single line definition like this: &GLOBAL-DEFINE name line 1 would strip spaces around the value and define 'name' as 'line 1'. What is the value of 'name' in this case? 'line 1 ' (spaces between 1 and ~) || ' line 2'? 'line 1line 2'? something else? A1. Leading blanks from the first line and trailing blanks from the last line are deleted. The tilde effectively deletes itself and the NL that follows from the input stream. III &UNDEFINE ------------- File A: &SCOPED-DEFINE x {include file B} File B: &SCOPED-DEFINE y {include file C} File C: &SCOPED-DEFINE z ... &UNDEFINE z &UNDEFINE y &UNDEFINE x Are &UNDEFINE y and &UNDEFINE x valid? A1. Yes. &UNDEFINE looks up all nested scopes until it finds the name, no matter scoped or global. Example: &scop x a {t7b.i} line a.3 {&x} &scop y b {t7c.i} line b.3 {&x} {&y} &scop z c Line c.2 {&x} {&y} {&z} &undefine z &undefine y &undefine x produces line c.2 a b c line b.3 line a.3 IV Spaces and comments between & and keyword/name ------------------------------------ 1. Is it valid: & GLOBAL-DEFINE? 2. Is it a valid name reference: {& name}? 3. Is it a valid name reference: {&/*comment*/name}? 4. Is it a valid name reference: {&na/*comment*/me}? 5. Is it a valid name reference: {&name/*comment*/}? 6. Is it a valid name reference: {&name /*comment*/}? A1. It is not valid. '& ' is deleted from the stream and the rest is there. Example 1: '& glob a aa' '{&a}' produces 'glob a aa' Example 2: '& glob a aa' '{&a}' produces ' glob a aa' A2. No, it's not a valid reference. The whole {&...} thing is deleted from the input stream. This happens also if the name is syntactically correct but doesn't exist. Example: '& glob a aa' '&glob b bb' '{&a}' '{&b}/{& b}.' produces ' glob a aa' '' 'bb/.' A3. Yes, but the referenced name is '/*comment*/name', not 'name'. A4. Yes, but the referenced name is 'na/*comment*/me', not 'name'. A5. Yes, but the referenced name is 'name/*comment*/', not 'name'. A6. Unclear. Not an include file &a. Probably, a reference to 'name /*comment*/' which can't be created. Deleted. V Spaces and comments in references ----------------------------------- 1. Argument reference: {1} or {1 } or { 1} or { 1 }? Similarly, {&name} or { &name} or {&name } or { &name }? 2. Include file reference: {file-name} or {file-name } or { file-name} or { file-name }? 3. Multiline {}? A1. Argument reference {n} is valid only without spaces or comments and if n refers to an existing parameter. Using spaces or comments before or after 'n' makes it an include file reference. Otherwise the whole {...} thing is deleted. Valid named references are {&name} and {&name }. { &name ...} is the include. The case of {&name x} etc is unclear. It is not a reference to 'name', nor is it a reference to 'namex'. It is not an include. It gets deleted. Perhaps, Progress thinks it's a reference to 'name x' which can't be resolved. A2. Argument reference {*} is valid in these forms: {*} or {* } or {*anything}. Otherwise the whole {...} thing is deleted. A3. Include file reference allows free use of spaces { [spaces] file [spaces]p1 ...[spaces] }. A4. Comments are not recognized as such within positional {}. Everything is parsed by spaces. Example 1. Valid uses: 1:{t9a.i a b c} 2:{ t9a.i a b c} 3:{t9a.i a b c } 4:{ t9a.i a b c } Example 2. Invalid uses: 5:{/*f*/t9a.i a b c} 6:{t9a.i/*f*/ a b c} 7:{/*f*/ t9a.i a b c} 8:{ /*a*/ t9a.i a b c} Example 3: t9c.i: 1:{t9c.i a b c/*b*/} {1},{2},{3}. --p3-- 2:{t9c.i /*a*/a b c} --p1-- 3:{t9c.i /*f*/ /*a1*/a/*a2*/ /*a-b*/ /*b1*/b/*b2*/ /*b-c*/ /*c1*/c/*c2*/ /*c-*/} --p1- ------p2----- ---p3-- ------p4----- ---p5-- -----p6------ --p7-- produces '1:a,b,c/*b*/.' '' '2:/*a*/a,b,c.' '' '3:/*f*/,/*a1*/a/*a2*/,/*a-b*/.' A5. In the keyworded cases space separated comments are recognized within {} after '='. They are deleted. Values may be coded with or without "". Example 1: t9b.i: 1:{t9b.i &pa=A &pb=B &pc=C} {&pa},{&pb},{&pc}. 2:{t9b.i &pa = A &pb = B &pc = C} 3:{t9b.i &pa="A" &pb="B" &pc="C"} 4:{t9b.i &pa = "A" &pb = "B" &pc = "C"} produces '1:A,B,C.' '' '2:A,B,C.' '' '3:A,B,C.' '' '4:A,B,C.' '' Example 2: 5:{t9b.i /*a*/&pa = A &pb = B &pc = C} 6:{t9b.i &pa/*a*/ = A &pb = B &pc = C} ----- not a comment 7:{t9b.i &pa /*a*/ = A &pb = B &pc = C} ----- not a comment 8:{t9b.i &pa = A /*a-b*/ &pb = B &pc = C} ------- recognized comment 9:{t9b.i &pa = "A" /*a-b*/ &pb = B &pc = C} ------- recognized comment produces '5:,,.' '' '6:,B,C.' '' '7:,B,C.' '' '8:A,B,C.' '' '9:A,B,C.' Example 3: 8:{t9b.i &pa = A/*a-b*/ &pb = B &pc = C} -------- value 9:{t9b.i &pa = "A"/*a-b*/ &pb = B &pc = C} ---------- value produces '8:A/*a-b*/,B,C.' '' '9:A/*a-b*/,B,C.' '' Example 4: t9b.i: 6:{t9b.i &pa/*a*/ = A &pb = B &pc = C} {&pa},{&pb},{&pc}.{&pa/*a*/},{&pa /*a*/} 7:{t9b.i &pa /*a*/ = A &pb = B &pc = C} ------- produces '6:,B,C.A,' in both cases 6 and 7, the include created '' a name 'pa/*a*/', which was properly '7:,B,C.A,' referenced here '' A6. Multiline {} are allowed. Example: &scop a A 1:{&a }: produces 1:A: VI Quotes and double quotes in values ------------------------------------- 1. Are double quotes required in {file &name = "value"}? 2. How to pass single quotes? {file &name = "'value'"}? 3. How to pass double quotes? 4. Do the double quotes have to be balanced? 5. Any difference between "" and ~", '' and ~'? A1. No. {file &name = value} is equivalent to {file &name = "value"} provided there are no spaces inside the value. These are also identical: {file &name = ab} and {file &name = "a"b}. A2. As any other character. {file &name = abc'cde} or {file &name = 'abc'}. A3. Double every double quote. {file &name=""value""} A4. No. Single " is omitted. Double " is replaced with the single ". Example 1: 9:{t9b.i &pa = ""A"/*a-b*/" /*a+b*/ &pb = B &pc = C /*c*/} produces '9:"A/*a-b*/,B,C.,;"A/*a-b*/ B C;' '' A5. Yes. ~" is treated as a single " (see A4). No. '' and ~' are equivalent (produce single ' when inside '...'). Example 1: 9:{t9b.i &pa = ~"A/*a-b*/~" /*a+b*/ &pb = B &pc = C /*c*/} produces '9:A/*a-b*/,B,C.,;A/*a-b*/ B C;' '' Example: 9:{t9b.i &pa = '~"A/*a-b*/~"' /*a+b*/ &pb = B &pc = C /*c*/} produces '9:'A/*a-b*/',B,C.,;'A/*a-b*/' B C;' '' VII Abbreviated keywords ------------------------ 1. If &GLOBAL-DEFINE and &GLOB both are valid, is anything in between valid as well? (like &GLOBA or &GLOBAL-DEFIN) 2. Are any other abbreviations there besides &GLOB and &SCOP? 3. What about misspelled statements? A1. Anything between &GLOB and &GLOBAL-DEFINE is valid. Anything less that &GLOB is invalid and gets deleted. A2. Yes. &UNDEFINE --> &UNDEF. The rest must be spelled in full. A3. They are deleted from the input stream (misspelled word and one space). VIII Tabs --------- 1. Are tabs interchangeable with spaces? 2. Any difference between using tabs and ~t? A1. The preprocessor output contains no tabs. That means, tabs are expanded with spaces on input, even within strings. Unconditionally. Even outside of any preprocessor related statements. Example 1: &if defined(a) &then aa &else bb &endif cc produces ' bb' <-- space, not a tab 'cc' Example 2: &scop a aa &if defined(a) &then " aa" &else bb &endif cc produces " aa" <-- spaces cc Example 3: " a" "~ta" produces " a" "~ a" a bug? ~t is treated as TAB, but ~ is still there. A2. Yes. It looks like a bug, but ~t leaves ~ around AND gets expanded. IX Syntactically incorrect names in defines, undefines and references --------------------------------------------------------------------- 1. What is the effect of '&glob 1' (1 is an invalid name for a preprocessor variable)? 2. '&glob *' etc? 3. '&undefine *'? 4. {&name text}? 5. {&-}? 6. {&a/}? 7. What characters are valid in PP variable names? A1-A3. The preprocessor issues an error message and deletes the statement from input. A4. This reference is invalid even if both 'name' and 'nametext' variables are defined. It gets deleted from the input. A5. This reference is invalid. It is silently deleted from the input. A6. This reference is invalid. The preprocessor issues an error message and deletes it. A7. The first character must be alphabetic. The example below works. Used are all characters known to be valid. Any of the following characters makes the name invalid: !@^:|*()[]`;'"/=+<>{}, Example: &scop a~#$-_.\%& A 1:{&a~#$-_.\%&}: produces 1:A: X Syntactically incorrect expressions in &IF, &ELSEIF ----------------------------------------------------- 1. What is the reaction? What part of the if-then-else, if any, is generated? 2. Are comments allowed within the expression? A1. An error message is issued and everything from &if through &endif is deleted. A2. Yes. They are preserved on output. See I.A4. XI Special combinations ----------------------- 1. These combinations have special meaning: ;& ;< ;> ;* ;' ;( ;% ;) ;? meaning @ [ ] ^ ` { | } ~ Are they converted on output or left unchanged? 2. Are EQ LT LE NE GE GT allowed in PP expressions? Are they converted on output if outside of PP expressions? A1. Converted outside the strings, even in comments. Example: ;& ;< ;> ;* ;' ;( ;% ;) ;? produces @ [ ] ^ ` and an error message about | being not found. First, internal conversion gives: @ [ ] ^ ` { | } ~ and the interpretation is obvious. A2. EQ LT LE NE GE GT are allowed in PP expressions. They are not converted to = < <= <> >= > if used outside. Examples: /* ;& ;< ;> ;* ;' ;( ;% ;) ;? ~;& ~;< ~;> ~;* ~;' ~;( ~;% ~;) ~;? x = ";& ;< ;> ;* ;' ;( ;% ;) ;? ~;& ~;< ~;> ~;* ~;' ~;( ~;% ~;) ~;?". ;?;& ;?;< */ def var x ~ as char. x = ";& ;< ;> ;* ;' ;( ;% ;) ;? ~;& ~;< ~;> ~;* ~;' ~;( ~;% ~;) ~;?". message x view-as alert-box. x = ";?;& ;?;< ;?;> ;?;* ;?;' ;?;( ;?;% ;?;) ;?;?". message x view-as alert-box. produces: /* @ [ ] ^ ` { | } ~ ~;& ~;< ~;> ~;* ~;' ~;( ~;% ~;) ~;? x = "@ [ ] ^ ` { | } ~ ~;& ~;< ~;> ~;* ~;' ~;( ~;% ~;) ~;?". ~;& ~;< */ def var x as char. x = ";& ;< ;> ;* ;' ;( ;% ;) ;? ~;& ~;< ~;> ~;* ~;' ~;( ~;% ~;) ~;?". message x view-as alert-box. x = ";?;& ;?;< ;?;> ;?;* ;?;' ;?;( ;?;% ;?;) ;?;?". message x view-as alert-box. and these messages: msg1: ;& ;< ;> ;* ;' ;( ;% ;) ;? ;& ;< ;> ;* ;' ;( ;% ;) ;? msg2: ;?;& ;?;< ;?;> ;?;* ;?;' ;?;( ;?;% ;?;) ;?;? XII Strings ----------- 1. Are &statements recognized within "" and '' strings? 2. Are {} references recognized? A1. No. A2. Yes. Example: &scop a 3 "&scop b 4" '&scop c 5' &scop g 9 "{&a}" '{&g}' produces "&scop b 4" '&scop c 5' "3" '9' XIII References to {&*} ----------------------- Valid reference is either {&*} or {&*anything}. File tc.p: {ti.i &a=c &b=d} File ti.i: def var x as char. x = {&*}. x = '{&*}'. x = "{&*}". x = {*}. x = '{*}'. x = "{*}". produce: def var x as char. x = "c" "d". x = '&a="c" &b="d"'. x = "&a="c" "d". x = c d. x = 'c d'. x = "c d". so: x = {&*}. produces x = "c" "d". but x = '{&*}'. produces x = '&a="c" &b="d"'. and x = "{&*}". produces x = "&a="c" "d". <=== ??? for the same arguments! XIV Mixed positional and named arguments ---------------------------------------- Whatever argument form comes first, defines how the list is processed. If the first argument looks like positional, all space separated words are considered positional arguments and no named arguments are defined. If there are named arguments in the form name=value, they are parsed the same exact way that positional parameters are parsed so the name= part is part of the resulting output. Example: {ti.i p1 p2 &a="c" p3 &b=d p4} where ti.i: def var x as char. x = {&*}. x = '{&*}'. x = "{&*}". x = {*}. x = '{*}'. x = "{*}". pos0:{0}; pos1:{1}; pos2:{2}; pos3:{3}; pos4:{4}; pos5:{5}; pos6:{6}; named-a:{&a}; named-b:{&b}; produces: def var x as char. x = . {&*} is empty x = ''. {&*} is empty x = "". {&*} is empty x = p1 p2 c p3 d p4. {*} expanded (&a= is missing because of a compile quirk) x = 'p1 p2 &a=c p3 &b=d p4'. {*} expanded x = "p1 p2 &a=c p3 &b=d p4". {*} expanded pos0:ti.i; {0} expanded pos1:p1; {1} expanded pos2:p2; {2} expanded pos3:c; {3} expanded (&a= is missing because of a compile quirk) pos4:p3; {4} expanded pos5:d; {5} expanded (&b= is missing because of a compile quirk) pos6:p4; {6} expanded named-a:; {&a} undefined named-b:; {&b} undefined WARNING: when this testcase (and others below based on this template) is compiled with the "preprocess" option (to force the output to be saved), several errors occur. These errors cause the saved output to be malformed. In fact, when in positional mode, there is no special parsing of the parameters that appear to be named. So the missing &a= and &b= in the results is only an artifact of the failure condition, but is not what is really done by the preprocessor. In fact if one writes code that is correct Progress 4GL instead of code like this example, then one can see that it is not the compile error itself that causes this condition. One indication of this is that when the argument is referenced inside a string, the expansion is valid 4GL and the result has the &a= and &b= present. This same behavior occurs if there is any non-space character immediately preceding the argument reference. So when there are 3 references to positional argument number 3 in these forms: " {3} " T{3} {3} Then the saved preprocessor output will look like this (assuming the 3rd positional argument is "&a=txt": " &a=txt " T&a=txt txt Notice that in the 1st reference (which is inside a string) and 2nd reference (which is outside a string but is immediately preceded by any non-space character) both fully emit the argument with the &name= portion included. BUT in the 3rd case, the &name= portion is missing. It turns out that this quirky behavior is purely an artifact of the compile option to force the saving of the preprocessor output. This output is flawed and does not reflect the real preprocessor output that occurs internally when a program is run. To prove this more definitively: shell.p: {shell.i p1 p2 &a="txt" p4 &b=d p6} shell.i: os-command echo \" {3} \". When one runs shell.p directly, the following is printed to the top of the terminal (and then there is a "Press space bar to continue." message on the status line: &a=txt Clearly, the &name= portion appears in the preprocessor output. But we can't examine that output because it is in memory only. When one uses "compile shell.p preprocess shell.lst" to force the save of the preprocessor output to file, the result is a file with: os-command echo \" txt \". This is missing the &name= portion that we know is output normally. SO: the missing text is a "quirk" of the preprocessor save output mode and is NOT what happens when the preprocessor emits output for the real compiler. Thus in every case, what looks like a named argument is treated as a positional argument when the first argument is found to be positional (if the first argument doesn't start with an ampersand). The following case (omitted & in &a="c"... ) is considered positional: {ti.i a="c" p1 p2 p3 &b=d p4} If the first argument looks like named, only those words that look like named arguments are taken into account. They define both the named arguments and the corresponding positional arguments. In this mode it there are 3 other behaviors to note: - the value of a valid named argument extends to (but does not include) the first unquoted whitespace or to the next right brace character - anything after this point that is not an ampersand or right brace will be discarded - this can be called the "discard region" - in the discard region (between valid named arguments), double quotes lose their special behavior - the double quote character is treated as any other and cannot be used to "hide" ampersands or spaces as it normally would - there is no concept of matching or paired sets of double quotes in this part of the parsing) - once encountering the discard region, whitespace is not required in the character immediately preceding the next ampersand, but rather as soon as the ampersand is encountered, the discard region is exited and the ampersand is treated as the beginning of the next valid named argument Example: {ti.i &a="c" p1 p2 p3 &b=d p4} p1, p2, p3 and p4 are completely ignored! produces: x = "c" "d". {&*} expanded x = '&a="c" &b="d"'. '{&*}' expanded x = "&a="c" "d". "{&*}" expanded INCORRECTLY because of a compile error x = c d. {*} expanded x = 'c d'. {*} expanded x = "c d". {*} expanded pos0:ti.i; {0} expanded pos1:c; {1} expanded pos2:d; {2} expanded pos3:; {3} undefined pos4:; {4} undefined pos5:; {5} undefined pos6:; {6} undefined named-a:c; {&a} expanded named-b:d; {&b} expanded The following case is equivalent: {ti.i & a="c" p1 p2 p3 &b =d p4} Spaces are ignored between & and =. For both named and positional arguments, double quotes may be used to hide spaces. Ampersands and equals can be used as well. Multiple groups can be joined like in abc"d e f"ghi"j k l"&asd etc. Example of positional coding: {ti.i "b c"def"w z" qwe"r t y"} {1} refers to "b cdefw z" {2} refers to "qwer t y" Example of named coding: {ti.i &a="b &c=t"def"w z" &b=qwe"r t y"&c} {&a} refers to "b &c=tdefw z" {&b} refers to "qwer t y&c" The case like {ti.i &a=} is unusual. There is no preprocessor error message, but the generated code is not correct either. Example: {ti.i &a=} and ti.t is "{&a}" generates: " with no closing ". It shold have generated an error message. And, by the way, {file &1=x} works. The value of POSITIONAL(!) argument 1 is set to "x". XV Tildes --------- This combination ~NL joins two lines by deleting ~NL from the stream. It works everywhere in strings, comments etc. It works even for sequences like ~~NLNL, so the innermost combination is processed first. The Progess PP has a bug that shows tildes on output where they are deleted from the file to be compiled (the PP output file fails to compile). ;? is an alternative coding for ~. But sequences like ;?;? work differently because the first substitution gives ~;? and that means keeping ;? literally. Example: /* ~" ~' ~~ ~\ x~t y~r z~n p~Eq a~bc r~fs ~101 ~102 */ ~" ~' ~~ ~\ x~t y~r z~n p~Eq a~bc r~fs ~101 ~102". def var x as char. x = "~" ~' ~~ ~\ x~t y~r z~n p~Eq a~bc r~fs ~101 ~102". message x view-as alert-box. produces: /* ~" ~' ~~ ~\ x~ y~ z~ p~ q a~ c r~ s ~A ~B */ ~" ~' ~~ ~\ x~ y~ z~ p~ q a~ c r~ s ~A ~B". def var x as char. x = "~" ~' ~~ ~\ x~ y~ z~ p~ q a~ c r~ s ~A ~B". message x view-as alert-box. and msg (in <>): < " ' ~ \> < c > ~nnn nnn ~t 011 ~r 015 ~n are translated everywhere (~ left in). 012 ~E 033 ~b 010 ~f 014 ~" " ~' ' ~\ \ ~n3n2n1 checks only the first char n3 for being a digit and then calculates the value as: (n3 - '0') * 64 + (n2 - '0') * 8 + (n1 - '0'). The resulting character as well as the usual ones is translated by Progress. Details are not available, although it's clear than characters below ox20 and above 0x7f can be deleted or replaced. Example: ~080 0x40 ~090 0x48 ~0:0 0x50 ~0;0 0x58 ~0<0 0x60 ~0=0 0x68 ~0>0 0x70 ~0?0 0x78 ~0@0 deleted ~1A0 0x98 ~1B0 0xa0 ~190 deleted ~1:0 deleted ~1;0 deleted ~1<0 0xff ~1=0 0x7f ~1>0 0x80 ~1?0 0x88 ~1@0 0x90 ~1AB 0xAA XVI Built-in preprocessor variables ----------------------------------- These are: BATCH-MODE FILE-NAME LINE-NUMBER OPSYS SEQUENCE WINDOW-SYSTEM DISPLAY END OUT WEBSTREAM (Tried with OPSYS): &GLOB and &SCOP can't redefine it (error message), but {include &opsys=...} can. SEQUENCE can be redefined through include, too, but then it looses its built-in nature. In all likelyhood, redefined built-in variables become regular arguments. It's still impossible to redefine them afterwards using &GLOB or &SCOP, though.