OpenVMS Pascal constant not constant when used as size initializer - pascal

I think the easiest way of demonstrating the problem is with an example. The code:
PROGRAM CONSTANTSTRING(OUTPUT);
CONST
C_MaxLength = 30;
VAR
small_string : VARYING[5] OF CHAR VALUE 'alpha';
PROCEDURE LocalProc(
localstring : VARYING[C_MaxLength] of CHAR
);
BEGIN
writeln('localstring length: ', localstring.LENGTH);
writeln('localstring size: ', SIZE(localstring.BODY));
writeln('C_MaxLength: ', C_MaxLength);
END;
BEGIN
writeln('small_string length: ', small_string.LENGTH);
writeln('small_string size: ', SIZE(small_string.BODY));
writeln('C_MaxLength: ', C_MaxLength);
LocalProc(small_string);
END.
Compiling:
>pascal /version
HP Pascal I64 V6.1-116 on OpenVMS I64 V8.4
>pascal constantstringinit
>link constantstringinit
>run constantstringinit
And the output:
small_string length: 5
small_string size: 5
C_MaxLength: 30
localstring length: 5
localstring size: 5
C_MaxLength: 5
As you can see the value of C_MaxLength has changed locally inside the LocalProc procedure. Which is odd, since it has been declared a constant.
The new value of the constant is only within the scope of the LocalProc procedure. Code running in main after the call to LocalProc will use the original value of the constant.
At first this looked like a compiler bug to me, but I reasoned that this compiler has been around long enough that something like this would have been detected and either fixed or documented. But, I can't find any documentation on the matter. It doesn't help that VARYING is an HP extension, which means I can't compare to other Pascal implementations.
Do any gurus know more about what's going on here?

It's been a very long time and I can't find documentation to support it, but I think this is a special case of using varying[] of char as the type for a parameter:
localstring : VARYING[C_MaxLength] of CHAR
This not only declares the parameter localstring but also a locally-scoped constant that receives the size of the actual string that's passed in. It's only because you named it the same as your global constant that causes the confusion. You haven't actually changed the value C_MaxLength. Instead you've got another C_MaxLength in the local scope.
Trying changing that line to something like:
localstring : VARYING[foo] of CHAR
and then examine foo as well as C_MaxLength. I expect you'll see foo is 5 and C_MaxLength remains 30.

Related

What can make the output of a simple ada program indent, when not told to?

I am currently doing the Ada tutorial from learn.adacore.com, and I am now at the second example: reading and outputting an integer. Since copy-pasting is for people who don't want to learn the syntax, I manually typed out most of the code (Some of it was generated by gnat-gps, but I'm now using vim).
I compiled and ran the program, and surprisingly, the second line of output is indented by roughly one tab. Why?
Here's the code:
With Ada.Text_IO;
Use Ada.Text_IO;
With Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
procedure Main is
N : Integer;
begin
-- Insert code here.
Put("Enter an integer value: ");
Get(N);
if N>0 then
Put (N);
Put_Line(" is a positive number");
end if;
end Main;
(how do I get syntax highlighting?)
Here is a sample of the output (the first 1 being input):
Enter an integer value: 1
1 is a positive number
The Put procedure from Ada.Integer_Text_IO uses a default field width padded with spaces.
The specification for that procedure is defined in the Ada Language Reference Manual as:
procedure Put(Item : in Num;
Width : in Field := Default_Width;
Base : in Number_Base := Default_Base);
The Width and Base parameters are given default values. Your call to Put only supplied a value for the formal parameter Item. To eliminate the left padding simply specify a desired width. I suggest you use Ada named notation for the call as in
Put(Item => N, Width => 1);

Convert from System.Address to Integer in Ada

In the example below, I am wondering, why line 17 does not work, but line 18? Can I not convert a System.Address directly to an Integer (see line 17)?
main.adb
with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with System.Storage_Elements;
procedure Main is
package SSE renames System.Storage_Elements;
type Integer_Access is access Integer;
I1_Access : Integer_Access := new Integer'(42);
I1_Address : System.Address := I1_Access.all'Address;
function Convert1 is new Ada.Unchecked_Conversion (System.Address, Integer);
function Convert2 is new Ada.Unchecked_Conversion (System.Address, Integer_Access);
begin
Ada.Text_IO.Put_Line (SSE.To_Integer (I1_Access'Address)'Img);
Ada.Text_IO.Put_Line (SSE.To_Integer (I1_Access.all'Address)'Img);
Ada.Text_IO.Put_Line (I1_Access.all'Img);
Ada.Text_IO.Put_Line (Convert1 (I1_Address)'Img); -- why does this NOT work?
Ada.Text_IO.Put_Line (Convert2 (I1_Address).all'Img); -- why does this work?
end Main;
Result
140734773254664
140243203260416
42
-363855872
42
If I compile your code on this Mac with -gnatwa (most warnings) and -gnatl (generate a listing) I get (excerpted)
12. function Convert1 is new Ada.Unchecked_Conversion (System.Address, Integer);
|
>>> warning: types for unchecked conversion have different sizes
because Integer is 32-bits while System.Address (and most access types) are 64-bits. Your machine is evidently similar.
So the reason you get a weird 5th output line (I got -490720512, by the way) is that it’s only looking at the bottom 32 bits of the actual address.
You might look at System.Address_To_Access_Conversions (ARM 13.7.2) for the supported way to do this.
It does work. Apparently it's doing something other than what you expected.
You can convert a System.Address to an Integer using Unchecked_Conversion, but the result isn't necessarily going to be meaningful. You'll get an integer representing the (probably virtual) address held in the System.Address value -- not the value of whatever object it points to. And if System.Address and Integer aren't the same size, the result will be even less meaningful.
Ada.Text_IO.Put_Line (Convert1 (I1_Address)'Img);
This prints an Integer representation of a memory address. It's not particularly meaningful. (Typically you'd want to see such an address in hexadecimal.)
Ada.Text_IO.Put_Line (Convert2 (I1_Address).all'Img);
This prints the Integer value, 42, of the object at the memory location indicated by the value of I1_Address. It's just a roundabout way of printing I1_Access.all.
If you only want to print the value of an image, as in your example, consider using the function System.Address_Image. This is not good for pointer arithmetic, but leads to better output (hexadecimal, for instance)

Crash when casting the result of arc4random() to Int

I've written a simple Bag class. A Bag is filled with a fixed ratio of Temperature enums. It allows you to grab one at random and automatically refills itself when empty. It looks like this:
class Bag {
var items = Temperature[]()
init () {
refill()
}
func grab()-> Temperature {
if items.isEmpty {
refill()
}
var i = Int(arc4random()) % items.count
return items.removeAtIndex(i)
}
func refill() {
items.append(.Normal)
items.append(.Hot)
items.append(.Hot)
items.append(.Cold)
items.append(.Cold)
}
}
The Temperature enum looks like this:
enum Temperature: Int {
case Normal, Hot, Cold
}
My GameScene:SKScene has a constant instance property bag:Bag. (I've tried with a variable as well.) When I need a new temperature I call bag.grab(), once in didMoveToView and when appropriate in touchesEnded.
Randomly this call crashes on the if items.isEmpty line in Bag.grab(). The error is EXC_BAD_INSTRUCTION. Checking the debugger shows items is size=1 and [0] = (AppName.Temperature) <invalid> (0x10).
Edit Looks like I don't understand the debugger info. Even valid arrays show size=1 and unrelated values for [0] =. So no help there.
I can't get it to crash isolated in a Playground. It's probably something obvious but I'm stumped.
Function arc4random returns an UInt32. If you get a value higher than Int.max, the Int(...) cast will crash.
Using
Int(arc4random_uniform(UInt32(items.count)))
should be a better solution.
(Blame the strange crash messages in the Alpha version...)
I found that the best way to solve this is by using rand() instead of arc4random()
the code, in your case, could be:
var i = Int(rand()) % items.count
This method will generate a random Int value between the given minimum and maximum
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
The crash that you were experiencing is due to the fact that Swift detected a type inconsistency at runtime.
Since Int != UInt32 you will have to first type cast the input argument of arc4random_uniform before you can compute the random number.
Swift doesn't allow to cast from one integer type to another if the result of the cast doesn't fit. E.g. the following code will work okay:
let x = 32
let y = UInt8(x)
Why? Because 32 is a possible value for an int of type UInt8. But the following code will fail:
let x = 332
let y = UInt8(x)
That's because you cannot assign 332 to an unsigned 8 bit int type, it can only take values 0 to 255 and nothing else.
When you do casts in C, the int is simply truncated, which may be unexpected or undesired, as the programmer may not be aware that truncation may take place. So Swift handles things a bit different here. It will allow such kind of casts as long as no truncation takes place but if there is truncation, you get a runtime exception. If you think truncation is okay, then you must do the truncation yourself to let Swift know that this is intended behavior, otherwise Swift must assume that is accidental behavior.
This is even documented (documentation of UnsignedInteger):
Convert from Swift's widest unsigned integer type,
trapping on overflow.
And what you see is the "overflow trapping", which is poorly done as, of course, one could have made that trap actually explain what's going on.
Assuming that items never has more than 2^32 elements (a bit more than 4 billion), the following code is safe:
var i = Int(arc4random() % UInt32(items.count))
If it can have more than 2^32 elements, you get another problem anyway as then you need a different random number function that produces random numbers beyond 2^32.
This crash is only possible on 32-bit systems. Int changes between 32-bits (Int32) and 64-bits (Int64) depending on the device architecture (see the docs).
UInt32's max is 2^32 βˆ’ 1. Int64's max is 2^63 βˆ’ 1, so Int64 can easily handle UInt32.max. However, Int32's max is 2^31 βˆ’ 1, which means UInt32 can handle numbers greater than Int32 can, and trying to create an Int32 from a number greater than 2^31-1 will create an overflow.
I confirmed this by trying to compile the line Int(UInt32.max). On the simulators and newer devices, this compiles just fine. But I connected my old iPod Touch (32-bit device) and got this compiler error:
Integer overflows when converted from UInt32 to Int
Xcode won't even compile this line for 32-bit devices, which is likely the crash that is happening at runtime. Many of the other answers in this post are good solutions, so I won't add or copy those. I just felt that this question was missing a detailed explanation of what was going on.
This will automatically create a random Int for you:
var i = random() % items.count
i is of Int type, so no conversion necessary!
You can use
Int(rand())
To prevent same random numbers when the app starts, you can call srand()
srand(UInt32(NSDate().timeIntervalSinceReferenceDate))
let randomNumber: Int = Int(rand()) % items.count

Very strange behaviour in the increment of a FOR

I'm having a problem when I use these 2 FOR to initialize a two dimensional vector/array:
I have these types defined:
type
Range9 = 0..8;
Digit = '0'..'9';
Board = array [Range9,Range9] of Digit;
and then the part of the code where there are problems with the FOR's is the following:
var
i : Range9;
j : Range9;
table : Board;
BEGIN
for i:=0 to 8 do begin
for j:=0 to 8 do begin
table[i,j] := '0'
end
end;
END.
Now the problem is that, when I debug this portion of code, for some reason, my i variable is modified when it's not supposed to.
For example, I have a watch on i and j and if I put a breakpoint in the line table[i,j] := 0
I see with the watches these values:
i j
0 0
256 1
512 2
768 3
1024 4
1280 5
1536 6
1792 7
2048 8
2049 8
1 0
257 1
513 2
769 3
and so on...
So, when the program enters in the second for (the one that increases the j) my i increases in intervals of 256... I really don't know why is this happening.
And another thing I discovered is that, the problem solves if I change the TYPE of the i variable.
If in the VAR section I put i : integer instead of i : Range9, i doesn't get modified when isn't supposed to.
I would really appreciate if someone explains me why is happening this.
I've found the answer to my own question... well, I didn't exactly found the answer, I've asked this same question in the forum board of the programming course I'm attending and one of the professors gave me this link:
(it's in spanish btw)
http://www.fing.edu.uy/inco/cursos/prog1/pm/field.php/FAQ/Laboratorio#toc17
A quick translation:
This happens with variables defined as subranges. The reason isn't sure; but without doubt is an implementation error of the debugger. There is a 'trick' that can work to solve this (although not always), to be able to see the correct values on the debugger:
Suppose that you have the following variable in your program:
var anything: 1 .. 10;
Add in your program a integer variable which won't be used in any part of the program:
var anything: 1..10;
aux: integer; { only for the debugger }
Then when you define the debugger watch, instead of adding the anything variable, you should add the following expression:
aux:= anything
The aux variable can be used to view different variables, so you only need to declare one aux variable.
In some cases, the previous may not work. Another solution is to change the type of all the variables defined with subranges to integer, char, string, etc (depending the case) only for debug and the change it back again.
end of the translation.
Hope this will be useful for someone else facing the same error.
BTW, this happens with the debugger of free pascal IDE 2.2.2 , maybe in another IDE/compiler/debugger of pascal it doesn't happen.
I haven't done Pascal in a while, so I might be a bit rusty. The only thing I can think of that is creating your problem is that you created a character range that was interpreted as a byte array, which was then converted to a Digits and then multiplied, which gave you those weird values. But, I could be wrong. I am unfamiliar with FreePascal.
Type
Range9 = 0..8
Board = Array[Range9,Range9] of Integer;
var
A : Board;
I,J : Integer;
begin
For I:=0 to 8 do
For J:=0 to 8 do
A[I,J]:=I*J;
end.
Reference: ftp://ftp.freepascal.org/pub/fpc/docs-pdf/ref.pdf

How to read byte headers of untyped files and then use and display that data when they are file streams in Free Pascal and Lazarus

I am trying to learn Free Pascal using Lazarus and one of my pet projects involves reading the 64 byte headers of a particular set of untyped files that cannot be read and displayed using text or ASCII related procedures (so cannot be outputted directly to Memo boxes etc).
So far, I have devised the following code which does, I think, read in the 64 bytes of the header and I am using TStreams and a "Select Directory" dialog box to do this, based on advice received via the Lazarus IRC. My question though is how to actually USE the data that is read into the buffer from the header? For example, in the headers, there are sequences of 8 bytes, then 16 bytes, then 2 bytes and so on that I want to "work on" to generate other output that will eventually be converted to a string to go into my string grid.
Some of what I have so far is based on what I found here written by Mason Wheeler near the end (http://stackoverflow.com/questions/455790/fast-read-write-from-file-in-delphi) but it only shows how to read it in, not how to use it. I also read this (http://stackoverflow.com/questions/4309739/best-way-to-read-parse-a-untyped-binary-file-in-delphi) but again, it shows you how to READ the data too, but not subsequently USE the data. Any guidance wamrly received! So far, the code below just outputs single value integer numbers to the edit box, as opposed to, say, a range of 8 hexadecimal values.
PS - I am new to programming so please be gentle! Nothing too complex.
procedure TForm1.ProbeFile(FileIterator: TFileIterator);
type
TMyHeader = Array[1..64] of packed record
First8Bytes,
Next16Bytes,
Next2Bytes: byte;
end;
var
FI : TFileIterator; //File Iterator class
SG : TStringGrid;
NumRead : SmallInt;
FileToProbe: TStream;
header: TMyHeader;
begin
FI := TFileIterator.Create;
SG := TStringGrid.Create(self);
// Open the file and read the header
FileToProbe := TFileStream.Create(FileIterator.FileName, fmOpenRead);
try
FileToProbe.seek(0, soFromBeginning);
FileToProbe.ReadBuffer(header, SizeOf(header));
edit1.text := IntToStr(header[0].First8Bytes); // Just outputs '0' to the field? If I try '10' it ooutputs '29' and so on
finally
FileToProbe.Free;
end;
Please forgive me if I misunderstood your question.
As I understand it there is a header of 64 bytes. The first 8 bytes belong together, then the next 16 bytes and finally another 2 bytes.
To me it seems the declaration for this header should be:
TMyHeader = packed record
First8Bytes: array[0..7] of byte;
Next16Bytes: array [0..15] of byte;
Next2Bytes: array [0..1] of byte;
// add more if you like
end;
This recordtype has a size of 8+16+2 = 26 bytes.
Your code that reads the header looks ok to me, So I won't repeat that.
The next16bytes in your header can be retrieved, for example, like this:
edit1.text:= '';
// needs a declaration of a variable "i" as integer
for i:= 0 to 15 do
edit1.text:= edit1.text + IntToStr(header.next16bytes[i]) + '-';
Change the value of the first byte in the next2bytes part of your header as follows (again as an example):
header.next2bytes[0]:= 123;
Finally, you could write your changes back to the header of the file with help of the filetoprobe.writebuffer method.

Resources