DCL symbol syntax in OpenVMS - openvms

I am really confused by some syntax in the DCL of OpenVMS. For example, these are some of the lines which confused me:
$ wo = "write sys$output"
Does it create a symbol wo for write sys$output?
$ billing_run_number == p1
Is p1 a parameter passed to the .com file when it was executed? How many parameters can it be supplied with?
$ wo "BILLING_RUN_NUMBER = ''billing_run_number'"
Is ''abc' substituted by the content of the symbol abc? Why is it ''abc' but not 'abc'? Can we use ""?
$ if ((status .nes. "P") .and. (status .nes. "M")) .or. (ftp_status .nes. "Y")
What does .nes. mean? equal? I've also seen .ne. , .eqs. too. What is the different of them?
Why are "and" and "or" surrounded by two dots? A DCL specific syntax?

from memory: $ wo = "write sys$output" is as you say, assigning wo as an alias for "write sys$output", VMS's equivalent to Unix stdout.
.nes. is "not equal to string", compared to .ne. which is a numeric "not equals".
p1 is a (the first) parameter as you guessed. I can't remember if it goes p1 through p9, or more, or if there is no arbitrary limit. p0 might be the program name, like Python's sys.argv[0].
A command procedure accepts up to 8 parameters, called P1 .. P8.
a single quote (') interpolates the following variable name, so wo "BILLING_RUN_NUMBER = ''billing_run_number'" would output, for example, BILLING_RUN_NUMBER = '42', assuming p1 was equal to 42. I can't remember exactly how DCL knows what to do when it sees two single quotes in a row like that...
The official incantation is ''symbol' to have the actual DCL text replaced by the value of symbol
that'll get you started at least... most shops that use VMS have a few hundred pounds of documentation in 3-ring binders. ask around.

In addition to the documentation referenced above, there is also extensive information via the HELP HINTS, HELP :=, HELP =, and HELP # commands. P9-P16 became available with OpenVMS V8.4, I believe.
Also, pay careful attention to the difference between global symbols (defined with a doubled equal sign {== or :==}) and local symbols (defined with a single equal sign {= or :=}). Like in case-sensitive languages a symbol defined A = 1 is a different symbol than one defined A == 1 and the local symbol can mask references to the global symbol - also some commands like READ and INQUIRE can create symbols, but I think they are always a local symbol - verify that since I'm working from memory.\
The command SET SYMBOL /SCOPE[={LOCAL|NOLOCAL},{GLOBAL|NOGLOBAL}) can also affect whether you can see certain types of symbols.
In general, stay with local symbols whenever you can - you usually only need a global symbol if a higher level (calling) command procedure needs access, or if you need the symbol still defined when you return to interactive DCL - the exception is any program that you run that specifically reads or write or creates a global symbol - rare, but I've run into a few.

Is p1 a parameter passed to the .com
file when it was executed? How many
parameter can it be supplied with?
You can pass up to 8 parameters. Each one are defined as P1, P2... P8
If you need more than 8 parameters, you can use trick like
#my_dcl "my_p1" "my_p2" "my_p3" "my_p4" "my_p5" "my_p6" "my_p7" "my_p8 my_p9 my_p10"
In my_dcl, P8 will contain value of "my_p8 my_p9 my_p10" in one single string.
$ wo "BILLING_RUN_NUMBER =
''billing_run_number'"
Is ''abc' substituded by the content
of the symbol abc? Why is it ''abc'
but not 'abc'? Can we use ""?
$ if ((status .nes. "P") .and. (status
.nes. "M")) .or. (ftp_status .nes.
"Y")
The single quote means translate the content of the string.
So,if you define wo = "write sys$output"
you can use
wo "Hello World!"
or
'wo "Hello World!"
But what if you want to show write sys$output Hello World
If you try,
wo "'wo Hello World!"
you'll get wo 'wo Hello World!
So, you have to surround it with single quote.
The first two are a escaped single quote, the last one means to stop translation.
wo "''wo' Hello World!"
Like other script language, you can have variable variable...
var_hidden = "Hello world!"
my_var = "var_hidden"
wo 'my_var'
will print Hello world!

Related

How to show an unknown list of variables and their values, if possible

As mentioned before in some questions with "Progress-4GL" and "OpenEdge" tags, I'm working with AppBuilder and Procedure editor. As a result, the debugging possibilities are extremely limited: for knowing the value of a variable, I need to do show them on screen, something like this:
MESSAGE "temp1=[" temp1 "], temp2=[" temp2 "]" VIEW-AS ALERT-BOX.
I can also put that information in a logfile, but that's not the main point here.
I would like to write a procedure, which can handle this, something like:
PROCEDURE SHOW_VARIABLES_AND_VALUES (INPUT I1, INPUT I2, ...):
1. <put parameter names and values together inside one string> => """I1="" I1"
2. <do this for all input parameters (the number is unknown)> => """I1="" I1, ""I2="" I2, ..."
3. <how to use this (MESSAGE VIEW-AS ALERT-BOX, LOG, ...) there I'll know what to do>
Does anybody know how to handle the fist two points (put variable name and value together and handle an unknown number of input parameters)?
Thanks in advance
You can use SUBSTITUTE function.
MESSAGE SUBSTITUTE ("temp1=&1 ~ntemp2=&2 ~n temp3=&3",
temp1,
temp2,
temp3) VIEW-AS ALERT-BOX.
Unfortunately there is no dynamic access to variables or parameters. So there's no way to automatically add all input parameters to a message string. Also there is no anytype parameter type in the ABL - for user defined functions or procedures. So you'd have to use the STRING() function a lot to convert your input parameters to string as the best fit parameter for everything.
The built in SUBSTITUTE function on the other hand can handle anytype of arguments. So temp1, temp2 and temp3 can actually be variables or parameters of any datatype.
As mentioned in one of my comments on one of your earlier questions: Give the OpenEdge debugger a chance. The debugger outside of Progress Developer studio looks historic. But it does it's job.
Meanwhile I've decided to use following system (as my request seems to be impossible):
MESSAGE "temp1=[" temp1 "]~n" ~
"temp2=[" temp2 "]~n" ~
"temp3=[" temp3 "]~n" ~
"temp4=[" temp4 "]" ~
VIEW-AS ALERT-BOX.
In order to make it easy to work with, I've found out the following keyboard "shortcut" for the tilde character: ALT+0126.
As indicated by Stefan, this is far better (no tilde and no shortcut needed):
MESSAGE "temp1=[" temp1 "]" SKIP
"temp2=[" temp2 "]" SKIP
"temp3=[" temp3 "]" SKIP
"temp4=[" temp4 "]" SKIP
VIEW-AS ALERT-BOX.

String interpolation in Perl6

I have difficulty figuring out why the statement
say "\c500";
produces the character 'Ǵ' on my screen as expected, while the following statements give me an error message at compile time ("Unrecognized \c character"):
my $i = 500;
say "\c$i";
even though
say "$i"; # or 'say $i.Str;' for that matter
produces "500" (with "$i".WHAT indicating type Str).
You'll have to use $i.chr, which is documented here. \c is handled specially within strings, and does not seem to admit anything that is not a literal.
The string literal parser in Perl 6 is a type of domain specific language.
Basically what you write gets compiled similarly to the rest of the language.
"abc$_"
&infix:«~»('abc',$_.Str)
In the case of \c500, you could view it as a compile-time constant.
"\c500"
(BEGIN 500.chr)
Actually it is more like:
(BEGIN 500.HOW.find_method_qualified(Int,500,'chr').(500))
Except that the compiler for string literals actually tries to compile it to an abstract syntax tree, but is unable to because there hasn't been code added to handle this case of \c.
Even if there was, \c is effectively compiled to run at BEGIN time, which is before $_ has a value.
Also \c is used for more than .chr
"\c9" eq "\c[TAB]" eq "\cI" eq "\t"
(Note that \cI represents the character you would get by typing Cntrl+Alt+i on a posix platform)
So which of these should \c$_ compile to?
$_.chr
$_.parse-names
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.index($_).succ.chr
If you want .chr you can write it as one of the following. (spaces added where they are allowed)
"abc$_.chr( )def"
"abc{ $_.chr }def"
"abc{ .chr }def"
'abc' ~ $_.chr ~ 'def'

Variable substitution within braces in Tcl

Correct me wherever I am wrong.
When we use the variables inside braces, the value won't be replaced during evaluation and simply passed on as an argument to the procedure/command. (Yes, some exception are there like expr {$x+$y}).
Consider the following scenarios,
Scenario 1
% set a 10
10
% if {$a==10} {puts "value is $a"}
value is 10
% if "$a==10" "puts \"value is $a\""
value is 10
Scenario 2
% proc x {} {
set c 10
uplevel {set val $c}
}
%
% proc y {} {
set c 10
uplevel "set val $c"
}
% x
can't read "c": no such variable
% y
10
% set val
10
%
In both of the scenarios, we can see that the variable substitution is performed on the body of the if loop (i.e. {puts "value is $a"}), whereas in the uplevel, it is not (i.e. {set val $c}), based on the current context.
I can see it as if like they might have access it via upvar kind of stuffs may be. But, why it has to be different among places ? Behind the scene, why it has to be designed in such this way ? Or is it just a conventional way how Tcl works?
Tcl always works exactly the same way with exactly one level of interpretation, though there are some cases where there is a second level because a command specifically requests it. The way it works is that stuff inside braces is never interpolated or checked for word boundaries (provided those braces start at the start of a “word”), stuff in double quotes is interpolated but not parsed for word boundaries (provided they start a word), and otherwise both interpolation and word boundary scanning are done (with the results of interpolation not scanned).
But some commands send the resulting word through again. For example:
eval {
puts "this is an example with your path: $env(PATH)"
}
The rule applies to the outer eval, but that concatenates its arguments and then sends the results into Tcl again. if does something similar with its body script except there's no concatenation, and instead there's conditional execution. proc also does the same, except it delays running the code until you call the procedure. The expr command is like eval, except that sends the script into the expression evaluation engine, which is really a separate little language. The if command also uses the expression engine (as do while and for). The expression language understands $var (and […]) as well.
So what happens if you do this?
set x [expr $x + $y]
Well, first we parse the first word out, set, then x, then with the third word we start a command substitution, which recursively enters the parser until the matching ] is found. With the inner expr, we first parse expr, then $x (reading the x variable), then +, then $y. Now the expr command is invoked with three arguments; it concatenates the values with spaces between them and sends the result of the concatenation into the expression engine. If you had x previously containing $ab and y containing [kaboom], the expression to evaluate will be actually:
$ab + [kaboom]
which will probably give you an error about a non-existing variable or command. On the other hand, if you did expr {$x + $y} with the braces, you'll get an addition applied to the contents of the two variables (still an error in this case, because neither looks like a number).
You're recommended to brace your expressions because then the expression that you write is the expression that will be evaluated. Otherwise, you can get all sorts of “unexpected” behaviours. Here's a mild example:
set x {12 + 34}
puts [expr $x]
set y {56 + 78}
puts [expr $y]
puts [expr $x * $y]
Remember, Tcl always works the same way. No special cases. Anything that looks like a special cases is just a command that implements a little language (often by calling recursively back into Tcl or the expression engine).
In addition to Donal Fellows's answer:
In scenario 2, in x the command uplevel {set val $c} is invoked, and fails because there is no such variable at the caller's level.
In y, the equivalent of uplevel {set val 10} is invoked (because the value of c is substituted when the command is interpreted). This script can be evaluated at the caller's level since it doesn't depend on any variables there. Instead, it creates the variable val at that level.
It has been designed this way because it gives the programmer more choices. If we want to avoid evaluation when a command is prepared for execution (knowing that the command we invoke may still evaluate our variables as it executes), we brace our arguments. If we want evaluation to happend during command preparation, we use double quotes (or no form of quoting).
Now try this:
% set c 30
30
% x
30
% y
10
If there is such a variable at the caller's level, x is a useful command for setting the variable val to the value of c, while y is a useful command for setting the variable val to the value encapsulated inside y.

Limitting character input to specific characters

I'm making a fully working add and subtract program as a nice little easy project. One thing I would love to know is if there is a way to restrict input to certain characters (such as 1 and 0 for the binary inputs and A and B for the add or subtract inputs). I could always replace all characters that aren't these with empty strings to get rid of them, but doing something like this is quite tedious.
Here is some simple code to filter out the specified characters from a user's input:
local filter = "10abAB"
local input = io.read()
input = input:gsub("[^" .. filter .. "]", "")
The filter variable is just set to whatever characters you want to be allowed in the user's input. As an example, if you want to allow c, add c: local filter = "10abcABC".
Although I assume that you get input from io.read(), it is possible that you get it from somewhere else, so you can just replace io.read() with whatever you need there.
The third line of code in my example is what actually filters out the text. It uses string:gsub to do this, meaning that it could also be written like this:
input = string.gsub(input, "[^" .. filter .. "]", "").
The benefit of writing it like this is that it's clear that input is meant to be a string.
The gsub pattern is [^10abAB], which means that any characters that aren't part of that pattern will be filtered out, due to the ^ before them and the replacement pattern, which is the empty string that is the last argument in the method call.
Bonus super-short one-liner that you probably shouldn't use:
local input = io.read():gsub("[^10abAB]", "")

SAS - how to mask double quotes (e.g. "")

I am running a VBA program from SAS. The SAS code for this basically looks like:
%let worksheet =&i; *worksheet number;
%let xlsfile = %STR(""C:\Data\Excel Workbook.xlsx"");
%let csvfile = %STR(""C:\Data\CSV File..csv"");
x 'cd "C:\Data\MN2013\Alignment\Data\SAS Programs"';
x "XlsWsToCsv.vbs &xlsfile &worksheet &csvfile";
I need to be able to include two double quotes (i.e. "") at the beginning and end of the file paths in the xlsfile and csvfile for the VBA program to recognize the spaces in the file paths and run correctly.
MY PROBLEM:
I run this in SAS Enterprise Guide using SAS 9.3. In my log, directly after the variable definition is read in, the double quotes are underlined in red (usually indicating an error) with the number 49 below. There is no error message, but instead, in green I get the following note:
NOTE 49-169: The meaning of an identifier after a quoted string might change in a future SAS
release. Inserting white space between a quoted string and the succeeding
identifier is recommended.
To me, this says SAS is reading these double quotes. They are somehow only partially being masked. My VBA program runs, so I could continue with this; but I like clean error logs. Does anyone have any recommendations for how to completely mask my xlsfile and csvfile variables? I've tried using %STR (as shown in my example above), %BQUOTE, %SUPERQ, and a few other things to make this work.
Those pesky error messages! You were very close, but try this syntax instead:
%let xlsfile = %STR("")C:\Data\Excel Workbook.xlsx%STR("");
%let csvfile = %STR("")C:\Data\CSV File..csv%STR("");
Double double quotes inside double quotes resolve to a single double quote character, ie...
x """c:\program files\office\excel.exe"" stuff stuff stuff ""stuff"" stuff";
should work just fine. Don't worry about the 'identifier' message, that's largely saying something like
"01JAN2013"d
could be possible with other things. You can add a space after the last " if it's a problem to have that in the log.