There are some really helpful people in the Perl community. I advertised the original posting on comp.lang.perl.misc and received some very useful responses from John W. Krahn and Michele Dondi, as below.
With respect to the rules, John wrote:
John> Why [were] you converting the '|' character and the 'e' character to 'e'?
John> The . character class matches a lot more than just letters, or did you really mean "replace any first character except newline with 'n'".
John> The . character class matches a lot more than just letters.
Then, with respect to the string eval() of each rule, John said, "Ouch! Use a dispatch table instead of string eval()."
At this point Michele Dondi chipped in with, ">my %rule = (
> 1 => sub {
> ( my $arg = shift ) =~ tr/s//d;
> return $arg;
> },
> 2 => sub {
> return join '', sort split //, shift;
> },
Since the keys are numbers, an array may be appropriate."
So there you have it, better rules, anonymous subroutines, and hashes. Powerful stuff.
I wonder how Tcl and the other languages would have done it. Or Protium, for that matter. Any takers?
© Copyright Bruce M. Axtens, 2008
Past (20 years or so) and present code. A variety of languages and platforms. Some gems. More gravel. Some useful stuff and some examples of how not to do it.
Tuesday, March 11, 2008
Sunday, March 02, 2008
[Perl] How not to do it?
The header for this website says, "Some useful stuff and some examples of how not to do it." This may fall into the latter category.
One of my kids had been playing a computer-based game which used a variety of word puzzles. The question he posed to me was to take a word, apply a small set of rules to it, as many times as necessary, and come up with another word. He would supply the rules and both words, and I would supply the sequence of rule applications which would effect the conversion.
(I'm no guru when it comes to Perl, so if you see something that could be expressed in a more efficient manner, please let me know.)
These are the rules:
1. Remove all 's'
2. Sort the characters of the word into alphabetic order
3. Convert all vowels to 'e'
4. Replace the first letter with 'n'
5. Drop the last letter
6. Replace letter pairs with 'ow'
My son then said that the start word was 'first' (or 'ant') and the stop word was 'now'. After some fiddling, resulting in the code below, I said, "With these rules you can't get from 'first' to 'now'. Not even from 'ant' to 'now'. But from 'gnat', yes."
So here's the code, for what it's worth. It was assumed that this would be run as a command line tool, so I load up the start word (stored in $root) and a recursion management flag (stored in $managed). Recursion management is defaulted to true. The recursion level is marked with $level and there's a hash, called %deadends, to keep track of "solutions" that shouldn't be pushed any further as they have already been proved not to get any closer to the solution.
Everything else happens in the apply function which looks at every possible combination of rules in pursuit of the target word. After getting the word to check from @_ with shift, a couple of variables are declared and a for loop initiated, stepping through the rules.
Each rule is evaluated against the passed in value in $arg, and stored in $res. $reason is cleared and each test applied to $res.
If $res is the same as $arg, $reason is "equal". If the length of $res is less than 3, $reason is set to "too short". If $managed is 1, and $res is already in the %deadends hash, $reason is set to "deadend", and if $res is equal to "now" (the goal, as it happens) then $reason is set to "found".
If $reason is not empty and not "equal" then print a newline, as many spaces as there are levels of recursion, the rule that got us here, the incoming word and the result of the rule application. If $reason is "deadend" then print an exclamation mark to show that a deadend has been reached, otherwise print a full stop.
If we've actually reached "now", indicate that with an asterisk. (We could exit the script at this point, but I left it to show all the possible paths to the stop word.)
If managing recursion, store the value of $res in the deadends hash.
Now, if $reason is, for some reason, empty, print a newline, as many spaces as there are recursion levels, the rule, and the $arg and the $res. Then increase the value of $level and recursively call apply with the contents of $res. When it returns, decrease the value of $level.
Here's the first call to apply, with a newline displayed once processing returns from the call.
Keeping track of dead-ends proved useful. Without it, the 'first' to 'now' attempt generated at 406K file (redirecting the output). With it, I got a 5K file. Similarly, 'ant' to 'now' was 1.8K without, and 457 bytes with. When it came to starting with 'gnat', a managed conversion generated an 8K file. Without management the laptop slowed to a crawl. After about five minutes I got an "Out of memory!" on stderr, so I killed the perl processing resulting in a 902 Megabyte file.
This is the result of a managed attempt with 'ant' as the start word:
Sadly, no asterisks. Next, the log of a managed run starting with 'gnat'. Success came quite quickly: start with 'gnat' and apply rules 2, 3, 4, 2, 4, 5, 6 and 2. An even shorter path appears further down: 3, 4, 2, 5, 6, and 4. The shortest appears to be 4, 2, 5, 6, 4 -- nnat, annt, ann, aow, now.
Writing this makes me wonder if I should have found some way to jump from a successful traversal back to 'gnat' rather than applying the rules to instances of 'now' in search of an extended path to 'now'. I leave that as an exercise to the reader, and if you work out how to do it, please let me know.
© Copyright Bruce M. Axtens, 2008
One of my kids had been playing a computer-based game which used a variety of word puzzles. The question he posed to me was to take a word, apply a small set of rules to it, as many times as necessary, and come up with another word. He would supply the rules and both words, and I would supply the sequence of rule applications which would effect the conversion.
(I'm no guru when it comes to Perl, so if you see something that could be expressed in a more efficient manner, please let me know.)
These are the rules:
1. Remove all 's'
2. Sort the characters of the word into alphabetic order
3. Convert all vowels to 'e'
4. Replace the first letter with 'n'
5. Drop the last letter
6. Replace letter pairs with 'ow'
My son then said that the start word was 'first' (or 'ant') and the stop word was 'now'. After some fiddling, resulting in the code below, I said, "With these rules you can't get from 'first' to 'now'. Not even from 'ant' to 'now'. But from 'gnat', yes."
So here's the code, for what it's worth. It was assumed that this would be run as a command line tool, so I load up the start word (stored in $root) and a recursion management flag (stored in $managed). Recursion management is defaulted to true. The recursion level is marked with $level and there's a hash, called %deadends, to keep track of "solutions" that shouldn't be pushed any further as they have already been proved not to get any closer to the solution.
Everything else happens in the apply function which looks at every possible combination of rules in pursuit of the target word. After getting the word to check from @_ with shift, a couple of variables are declared and a for loop initiated, stepping through the rules.
Each rule is evaluated against the passed in value in $arg, and stored in $res. $reason is cleared and each test applied to $res.
If $res is the same as $arg, $reason is "equal". If the length of $res is less than 3, $reason is set to "too short". If $managed is 1, and $res is already in the %deadends hash, $reason is set to "deadend", and if $res is equal to "now" (the goal, as it happens) then $reason is set to "found".
If $reason is not empty and not "equal" then print a newline, as many spaces as there are levels of recursion, the rule that got us here, the incoming word and the result of the rule application. If $reason is "deadend" then print an exclamation mark to show that a deadend has been reached, otherwise print a full stop.
If we've actually reached "now", indicate that with an asterisk. (We could exit the script at this point, but I left it to show all the possible paths to the stop word.)
If managing recursion, store the value of $res in the deadends hash.
Now, if $reason is, for some reason, empty, print a newline, as many spaces as there are recursion levels, the rule, and the $arg and the $res. Then increase the value of $level and recursively call apply with the contents of $res. When it returns, decrease the value of $level.
Here's the first call to apply, with a newline displayed once processing returns from the call.
Keeping track of dead-ends proved useful. Without it, the 'first' to 'now' attempt generated at 406K file (redirecting the output). With it, I got a 5K file. Similarly, 'ant' to 'now' was 1.8K without, and 457 bytes with. When it came to starting with 'gnat', a managed conversion generated an 8K file. Without management the laptop slowed to a crawl. After about five minutes I got an "Out of memory!" on stderr, so I killed the perl processing resulting in a 902 Megabyte file.
This is the result of a managed attempt with 'ant' as the start word:
Sadly, no asterisks. Next, the log of a managed run starting with 'gnat'. Success came quite quickly: start with 'gnat' and apply rules 2, 3, 4, 2, 4, 5, 6 and 2. An even shorter path appears further down: 3, 4, 2, 5, 6, and 4. The shortest appears to be 4, 2, 5, 6, 4 -- nnat, annt, ann, aow, now.
Writing this makes me wonder if I should have found some way to jump from a successful traversal back to 'gnat' rather than applying the rules to instances of 'now' in search of an extended path to 'now'. I leave that as an exercise to the reader, and if you work out how to do it, please let me know.
© Copyright Bruce M. Axtens, 2008
Labels:
Eval,
Perl,
recursion,
regular expressions,
text transformations
Thursday, February 21, 2008
[Perl/PDK/PerlCtrl] Returning an array of arrays for VB6/VBScript
For the last few weeks I've been trying to get used to working with the Perl Development Kit (PDK 7.1) from ActiveState, particularly PerlCtrl.
One of the challenges I've been facing lately is how to return an array of arrays. That is, returning something that in VB6 or VBScript might be represented as Array( Item, Array( Item, Item ), Item ).
Kudos to Perl Monks, who have provided a function which converts a Perl array to a VB array. Interestingly, it also copes with said converted array being embedded in another array and with that array being converted as well. (Bodes well for other projects.)
So what we have below is some Perl code, a PerlCtrl wrapper around a WMI call to list System Services. The wrapper returns a two element array, the first element being an array of all the services which are running, and the second an array of all the services which have been stopped.
Note that without 'use Win32::OLE qw(in);' you can't access the elements of the $colItems collection, as it enables the use of 'in' in the foreach. Similarly, you need Win32::OLE::Variant for all the Variant() calls.
Please don't ask me to explain what's going on in the convertArrayToVBArray sub, because I have no idea. Anyone know?
The second bit of code is a VBScript testing the COM DLL. It's a assumed that you've run the above code through PerlCtrl, generated a DLL and registered it with RegSvr32.
I'm having a lot of fun with Perl. I've been able to take a lot of Perl functionality (Tree::Nary, Data::Trie, Lingua::EN::Inflect, Algorithm::LCSS, Algorithm::Knapsack, Algorithm::BinPack, Algorithm::Bucketizer, Algorithm::Permute, Algorithm::SetCovering, String::LCSS and Statistics::Benford) and turn it into something that VB6 and VBScript can use. I'm impressed. Would that every scripting language had this kind of power.
© Copyright Bruce M. Axtens, 2008
One of the challenges I've been facing lately is how to return an array of arrays. That is, returning something that in VB6 or VBScript might be represented as Array( Item, Array( Item, Item ), Item ).
Kudos to Perl Monks, who have provided a function which converts a Perl array to a VB array. Interestingly, it also copes with said converted array being embedded in another array and with that array being converted as well. (Bodes well for other projects.)
So what we have below is some Perl code, a PerlCtrl wrapper around a WMI call to list System Services. The wrapper returns a two element array, the first element being an array of all the services which are running, and the second an array of all the services which have been stopped.
Note that without 'use Win32::OLE qw(in);' you can't access the elements of the $colItems collection, as it enables the use of 'in' in the foreach. Similarly, you need Win32::OLE::Variant for all the Variant() calls.
Please don't ask me to explain what's going on in the convertArrayToVBArray sub, because I have no idea. Anyone know?
The second bit of code is a VBScript testing the COM DLL. It's a assumed that you've run the above code through PerlCtrl, generated a DLL and registered it with RegSvr32.
I'm having a lot of fun with Perl. I've been able to take a lot of Perl functionality (Tree::Nary, Data::Trie, Lingua::EN::Inflect, Algorithm::LCSS, Algorithm::Knapsack, Algorithm::BinPack, Algorithm::Bucketizer, Algorithm::Permute, Algorithm::SetCovering, String::LCSS and Statistics::Benford) and turn it into something that VB6 and VBScript can use. I'm impressed. Would that every scripting language had this kind of power.
© Copyright Bruce M. Axtens, 2008
Labels:
ActiveState,
Perl,
PerlCtrl,
VB6,
VBScript,
VT_ARRAY|VT_VARIANT,
Win32::OLE,
Win32::OLE::Variant,
WMI
Tuesday, January 08, 2008
[Protium] Master Class #1 (Part 2)
Demonstration - Oblique Strategies
"The Oblique Strategies are a deck of cards. Up until 1996, they were quite easy to describe. They measured about 2-3/4" x 3-3/4". They came in a small black box which said "OBLIQUE STRATEGIES" on one of the top's long sides and "BRIAN ENO/PETER SCHMIDT" on the other side. The cards were solid black on one side, and had the aphorisms printed in a 10-point sans serif face on the other.Oblique StrategiesA web-based installation of Oblique Strategies is supplied in the standard Protium install. Using the "Internet Information Services" snap-in, create a virtual directory pointing to C:\protium\Code\prx\os. Specify its default as index.prx, call it 'os', and share the C:\protium\Code\prx\os directory (Microsoft Access is picky about being able to write to databases even when only reading.)
...
The deck itself had its origins in the discovery by Brian Eno that both he and his friend Peter Schmidt ... tended to keep a set of basic working principles which guided them through the kinds of moments of pressure — either working through a heavy painting session or watching the clock tick while you're running up a big buck studio bill. Both Schmidt and Eno realized that the pressures of time tended to steer them away from the ways of thinking they found most productive when the pressure was off. The Strategies were, then, a way to remind themselves of those habits of thinking — to jog the mind."
--
Further information on setting up IIS can be found in the Protium document "C:\protium\bin\plugins\Configuring Windows IIS 5 to use with Protium and Isis.html"
The Protium code in the index.prx file is as follows: The above code (indented only to more clearly demonstrate document structure) outputs a different strategy each time the page is refreshed. The syllables translate as follows:
| <@ DEFKEYLIT>__Transput|__HTMLTransput</@> | define the Transput as HTML |
| <@ SAYPRO>@> | output a document prologue. (What is output by the PRO syllable depends on the Transput defined, as is the case for all document related syllables.) |
| <@ SAYDOC>@> | Enclose a document. |
| <@ SAYDHD>@> | Enclose a document header, in the context of an existing document. |
| <@ SAYDTT>Oblique strategies</@> | set the document title, in the context of an existing document body and header. |
| <@ SAYDBD></@> | enclose a document body, in the context of an existing document. |
| <@ DEFDBFLIT>os</@> | opens this data source, by default a JET/MSAccess database. |
| <@ DEFRSTCAP>select * from os where cardid = <@ SAYRANLIT>100</@></@> | Open record source defined by captured output. |
| <@ SAYRANLIT>100</@> | return a random number between 1 and 100. |
| <@ SAYDPR></@> | enclose a document paragraph, in the context of the existing document. |
| <@ SAYFLD>message</@> | print the contents of the field called 'message' which, in this context, is part of the 'os' recordset. |
The changed syllables translate as follows:
| <@ DEFRSTLIT>os</@> | Select every record in the database. |
| <@ LETVARSZERST>upper|___</@> | assign to the variable 'upper' the size of the default (marked by three underscores) recordset. |
| <@ RSTCAP>select * from os where cardid = <@ SAYRANVAR>upper</@></@> | Select a new recordset, capturing the output from RAN but using the variable 'upper' to specify the limit. |
| <@ ACTMOVRANRST></@> | move the record pointer to a random place in the recordset. |
Questions and Answers
I can't see a main() anywhere. Where's the start?Protium implements a single-pass code evaluation model. This is quite unlike most other languages which implement double-pass code evaluation: first pass to discover and make a map; second pass to evaluate.
Protium starts at the beginning of a named file, and builds its awareness of context from there. Because of this approach, we may never examine code we aren't interested in.
I keep using the same construction over and over again. Is there a way of abbreviating Protium words?
What you can do is create a compound operator (a concept borrowed from OPS-3). These are defined using the KOP syllable. If you wanted to define LVK as a shorthand for LETVARKEY, for example, then you would code
Thereafter you would be able to use the abbreviated form, replacing code like with
There are other ways of doing this, including UDS (user-defined syllable) and BLK (code block), but this is by far the simplest.
© Copyright Bruce M. Axtens, 2008
Monday, January 07, 2008
[Protium] Master Class #1 (Part 1)
Protium is ...
Protium is essentially, and at heart, a way of finding data in a myriad of locations in a homologous manner, manipulating it in a myriad of ways, and then delivering it to a myriad of displays, in a myriad of [human] languages. A concomitant of that is that data be current, so this also involves updating old data, which means that a similar potential is there for making changes to data.But the overriding maxim is for a direct equivalence of task to operator at the conceptual level. This means that high-level / low-level language distinctions don't enter in: branching is a single operator, but so is "work out the utility matrix for this complex set of possibilities"; the iteration syllable is at the same level as that of add and of unique-sort.
Influences
"I had as a goal the economy and power ofAPL (minus the cryptic nature) while having the manipulation power of SNOBOL, the one-for-one power of multimedia-instruction-set assembler (e.g. QUEXAL), the task oriented nature of NODAL, the reality representation characteristics of ATLAS, and the data ordering capabilities of the 'lost languages' (Simscript, Dectab, dBase etc.), [such that] the data is available in a way that ordinary programming languages don't permit.
"The other thing I took from NODAL was JOSS's implicit mixing of code and data, which is much saner than LISP's, and also the way that there was an implicit hierachy of data.
"I also borrowed some control structures from RPG, temporary constructions from Miranda, generators from Icon, multiple outcomes from COMIT and compound operators from OPS-3.
"I rejected the naive form of IO (which bedevilled Algol and Fortran). I think the ability to display is the least-used functionality of programming, only touched on in screenwiping and SOAP.
"The idea of assignment [in most programming languages] is generally also naive - for instance the fact that it is a function has been overblown in functional languages, but ignored elsewhere. So [I implemented] scaling and multiple returns (present in LYaPAS in 1964), multiple assigns (common in FORTRAN, APL and the original BASIC), multiple functional transforms (common in any DSP language, e.g. Faust) as well as the closer knitting of data and code (from FORTRAN and dBase).
"The final point to note is that the entirety of Protium works with a SNOBOL-/COMIT-style success/failure flag, converted to a success / failure / don't-know ternary system. Any operation will set this, but it can be qualified (success, found, quantity more than zero, test type, pattern match, etc.)
"Essentially, the idea was to revisit the nature of the programming task, and learn from where the bounds had been stretched; to try to get a thought-to-line-of-code transference capability, rather than the reductive, ALGOL-ish, 'see-how-small-I-can-get-the-core' approach."
-- Diarmuid Pigott, personal correspondence, 9 January 2007.
How to Program in Protium
The overall Protium approach to programming is to work out what you want to do, then work out what it is that that would be in Protium terms. Sometimes this is very similar to the conventional way (most of the code examples are conventional in that way to secure familiarity). Sometimes it involves a simple task that is unfamiliar in those terms.Unlike most other programming languages, Protium does not force the programmer to work in either a top-down / decompositional or bottom-up / compositional way. Either approach can be taken. The art of Protium is in the discovery of the idiomatic form which best describes the solution to the problem.
Workflow in Protium programming is simply this: work out what data you need to solve a problem, then extract it and present it. When there is need to extract data according to a criterion, it is specified in two stages, first the data source, then the record source, in the following hierarchy: Data Source -> Record Source -> Record -> Field -> Subfield. Any number of Data Sources can be open at one time, and any number of Record Sources, but a Record Source will always be subordinate to its Data Source.
Protium supports a number of ways of addressing and participating in memory. Memory is untyped. It is a simple tuple of name => value (read name implies value) which can be late-bind typed using 'typifiers', but is inherently capable of any interpretation. There is one global memory space, but it is lexically divided (using namespaces) to make the equivalents of local, static etc. Memory is synthetic, in that it will create a variable (or a structure) on being first called — variables do not need to be declared nor the type of their content specified.
Protium relies heavily on the concept of aliases: when any complex computational task is invoked the operation is aliased, so that you can switch back to it at any time. Anything can be aliased: branching, looping, switching, creation and use of structures, and interaction with data. These aliases, also known as workareas, have two components: a type and a name.
Alias names can be invoked internally, so a field called 'ChinaCollection => DateOfPurchase' will automatically invoke the aliased workspace called ChinaCollection to fulfil the data request. This means that some very powerful manipulations are possible - such as the much-used scatter/gather (borrowed from FoxPro) to swap data between aliased workspaces.
Implicit in all of this is the idea that, where possible, everything should be invokable with one operator, so DCT (dictionary) without a name is the dictionary currently in scope, and of course implicit in the entire thing is a hierarchy of context - the field is always (unless specified) invoked in the context of the current record or the current recordset (or query or view) of the current database. It is this dependence on context - Protium's context-aware, rather than context-free, grammar - which trips up the beginning Protium programmer. That and the lack of appropriate casting of numerics - all comparisons in Protium are lexical (i.e. 'a' is less than 'c'; 5 is greater than 45000), so comparison syllables must bear an explicit number-cast syllable when a numeric comparison is required.
Conventions
Protium respects the following naming conventions with respect to the file type:| Extension | Meaning |
| .p | a Protium script in plain text format |
| .pr | a Protium script in rich text format (RTF) |
| .pre | a ProtiumWeb script (plain text, HTML or rich text) |
| .prx | a Protium executable project (plain text, HTML or rich text) |
| .plb | a Protium library (plain text, HTML or rich text) |
Generally we make two files per website, a default.prx and an index.prx, but of course it could be set up to look for an index.pre, if necessary.
© Copyright Bruce M. Axtens, 2008
Labels:
Diarmuid J Pigott,
Protium
Monday, December 24, 2007
[FORTRAN] Making a DLL for VB6
As a break from trying to figure out how to get a pattern matching library DLLified (and UTF-32 sensitive), my boss gave me something else to do, something on the one hand similar (wrap something in a DLL) and on the other quite different (wrap two knapsack algorithms done in Fortran 90).
The knapsack code is derived from
The files used from the COMBO library were knapsack_01.f90, knapsack_rational.f90, knapsack_reorder.f90, rvec_backtrack.f90 and r_swap.f90, and were compiled using Compaq Visual Fortran.
Seeing as CVF is no longer available (except perhaps on eBay) you might get away with the Intel® Fortran Compiler for Windows, SilverFrost's FTN95 or g95.
Don't try it with Watcom Fortran, however, because that only does Fortran 77. Mind you, if the algorithms you're wrapping are F77 ones from NIST's 'Dictionary of Algorithms and Data Structures' or Steven Skiena's The Stony Brook Algorithm Repository you may do okay with the Watcom offering or even GNU's g77.
I really like Fortran. I remember writing some VAX-11 FORTRAN back in 1983 (along with a bit more VAX-11 COBOL) and found it quite powerful and expressive. I know that there's been a fairly widespread pooh-pooh-ing of Fortran in recent years, but I try not to be influenced by peer-group pressure, preferring to actually see for myself whether a language is any good or not.
Okay, off the soap box and down to the code. I'm not going to bother posting all the COMBO code, just what I wrote to wrap it.
These are the three exposed routines and have essentially the same framework: a subroutine header, two declarations controlling symbol names and method of access, the call to the COMBO routine, the return and the end of subroutine. Really, really, simple. Gobsmackingly simple, compared to some other languages I could mention. Rather embarrasing really ... so little to show.
If you're wondering why I explicitly state that the parameters are being passed by reference (!dec$ attributes reference ...), it's because a side effect of specifying "stdcall" is that it forces the passing mechanism into pass-by-value, and in this case, quite a few of the parameters are being modified by the subroutine.
The other routines are linked in during the compile and I end up with a nice little .DLL.
Okay, so maybe there's something more to show on the VB6 side.
First off, the declares. VB6 defaults to pass-by-reference, thus the missing "ByRef" ahead of each parameter.
Next the test code which is derived from TEST094 and TEST095 found inside COMBO's combo_prb.f90.
Do note that the floating point variables are defined here as Single. This seems to be the best match to an undecorated Fortran REAL. Maybe if I'd tweaked the COMBO code to REAL*8, I could have used Double on the VB6 side.
Hmm ... nothing much amazing there either. But that's how it was with wrapping the Fortran. Simple and powerful at the same time. So far as I'm concerned, if we ever have to rewrite Protium, Fortran's going to be very near the top of the list.
© Copyright Bruce M. Axtens., 2007
The knapsack code is derived from
Algorithm 4.9, Donald Kreher and Douglas Stinson, Combinatorial Algorithms, CRC Press, 1999, page 125.and was downloaded from FORTRAN Source Codes, specifically, the COMBO library. Many thanks to John Burkardt for making this resources available.
The files used from the COMBO library were knapsack_01.f90, knapsack_rational.f90, knapsack_reorder.f90, rvec_backtrack.f90 and r_swap.f90, and were compiled using Compaq Visual Fortran.
Seeing as CVF is no longer available (except perhaps on eBay) you might get away with the Intel® Fortran Compiler for Windows, SilverFrost's FTN95 or g95.
Don't try it with Watcom Fortran, however, because that only does Fortran 77. Mind you, if the algorithms you're wrapping are F77 ones from NIST's 'Dictionary of Algorithms and Data Structures' or Steven Skiena's The Stony Brook Algorithm Repository you may do okay with the Watcom offering or even GNU's g77.
I really like Fortran. I remember writing some VAX-11 FORTRAN back in 1983 (along with a bit more VAX-11 COBOL) and found it quite powerful and expressive. I know that there's been a fairly widespread pooh-pooh-ing of Fortran in recent years, but I try not to be influenced by peer-group pressure, preferring to actually see for myself whether a language is any good or not.
Okay, off the soap box and down to the code. I'm not going to bother posting all the COMBO code, just what I wrote to wrap it.
These are the three exposed routines and have essentially the same framework: a subroutine header, two declarations controlling symbol names and method of access, the call to the COMBO routine, the return and the end of subroutine. Really, really, simple. Gobsmackingly simple, compared to some other languages I could mention. Rather embarrasing really ... so little to show.
If you're wondering why I explicitly state that the parameters are being passed by reference (!dec$ attributes reference ...), it's because a side effect of specifying "stdcall" is that it forces the passing mechanism into pass-by-value, and in this case, quite a few of the parameters are being modified by the subroutine.
The other routines are linked in during the compile and I end up with a nice little .DLL.
Okay, so maybe there's something more to show on the VB6 side.
First off, the declares. VB6 defaults to pass-by-reference, thus the missing "ByRef" ahead of each parameter.
Next the test code which is derived from TEST094 and TEST095 found inside COMBO's combo_prb.f90.
Do note that the floating point variables are defined here as Single. This seems to be the best match to an undecorated Fortran REAL. Maybe if I'd tweaked the COMBO code to REAL*8, I could have used Double on the VB6 side.
Hmm ... nothing much amazing there either. But that's how it was with wrapping the Fortran. Simple and powerful at the same time. So far as I'm concerned, if we ever have to rewrite Protium, Fortran's going to be very near the top of the list.
© Copyright Bruce M. Axtens., 2007
Labels:
algorithms,
Compaq Visual Fortran,
DLL,
Fortran,
John Burkardt,
Knapsack,
stdcall,
VB6
Thursday, December 20, 2007
[Visual BASIC 6] BOOST (v2)
I revisited BOOST this week. A friend had pointed out to me some time back that /P: and /L: were a bit of a pain when one wanted to speed something up quickly, or kill it quickly. So this version changes the command line handling.
This version also demonstrates how to kill a process. In fact it kills every process of the same name. I wish I'd had this a couple of years ago when I was doing stuff with Excel -- I'd end up with multiple Excel sessions which I would then have to kill off one-by-one through the Task Manager. (I eventually ended up installing Cygwin so that I could script the process.)
If you remember the original VBScript of BOOST and the first VB6 version, you'll see some similarities.
I've given up on using setsubsys in favour of using VBAdvance (which is now freeware) to handle the compile-to-console-app process. I'm still compressing the app using UPX.
First a few constants and the declarations, found on vb.mvps.org, for doing console I/O.
Next, a few supporting functions. First off the rank, is an invocation syntax display. Notice that priority can now be specified either by a number (0 to kill, 1-6 for low to realtime) or by name (kill, low, belownormal, normal, abovenormal, high, realtime).
Second, the command line parser. This splits on space and tab and is based on code found at microsoft.public.vb.winapi
Third, the two routines which wrap the WMI functions to set priority and to kill process. The latter was found at tek-tips. If I remember correctly, the places in each routine where it says
Finally, the Main subroutine. Notice the additions to the priorities collection. Note also the change in the way the command line is handled.
Because there's no .Exists method on Collections, the .Item call is wrapped in an 'On Error' so that a non-existent item situation is caught appropriately.
What's next with this thing? I suppose one could explore the possibility of influencing the lives of applications on other computers:
Finally, here's a short batch file (killall.bat) I use for killing applications en masse. It was originally designed to get around the 'typing the /P and /L' issue, but I'm so used to using it now ...
An example invocation:
© Copyright Bruce M. Axtens, 2007
This version also demonstrates how to kill a process. In fact it kills every process of the same name. I wish I'd had this a couple of years ago when I was doing stuff with Excel -- I'd end up with multiple Excel sessions which I would then have to kill off one-by-one through the Task Manager. (I eventually ended up installing Cygwin so that I could script the process.)
If you remember the original VBScript of BOOST and the first VB6 version, you'll see some similarities.
I've given up on using setsubsys in favour of using VBAdvance (which is now freeware) to handle the compile-to-console-app process. I'm still compressing the app using UPX.
First a few constants and the declarations, found on vb.mvps.org, for doing console I/O.
Next, a few supporting functions. First off the rank, is an invocation syntax display. Notice that priority can now be specified either by a number (0 to kill, 1-6 for low to realtime) or by name (kill, low, belownormal, normal, abovenormal, high, realtime).
Second, the command line parser. This splits on space and tab and is based on code found at microsoft.public.vb.winapi
Third, the two routines which wrap the WMI functions to set priority and to kill process. The latter was found at tek-tips. If I remember correctly, the places in each routine where it says
sComputer = "."could, in fact, have some other computer's name. If you had sufficient privilege you could be tweaking or killing a process on someone else's machine.
Finally, the Main subroutine. Notice the additions to the priorities collection. Note also the change in the way the command line is handled.
Because there's no .Exists method on Collections, the .Item call is wrapped in an 'On Error' so that a non-existent item situation is caught appropriately.
What's next with this thing? I suppose one could explore the possibility of influencing the lives of applications on other computers:
BOOST Skype.exe kill /C:GUEST_01There's also the fact that the commandline parser doesn't properly handle double-quoted names. If an .exe had a space it its name, you wouldn't be able use BOOST on it.
Finally, here's a short batch file (killall.bat) I use for killing applications en masse. It was originally designed to get around the 'typing the /P and /L' issue, but I'm so used to using it now ...
An example invocation:
killall excel.exe outlook.exe
© Copyright Bruce M. Axtens, 2007
Monday, December 17, 2007
[Programming] Okay, Ignorance is Bliss ...
If by Ignorance you mean an implementation of the Bliss programming language, then yes, Ignorance is Bliss.
After two and half weeks of Ada, I had to have a break. I wandered over to the Retrocomputing Museum and downloaded the Ignorance source code. While I was at it I also downloaded the source for CUPL, the Cornell University Programming Language. Interestingly, the same interpreter also works for CORC, the CORnell Compiler. The man behind the website, Eric Raymond, has the following, less than flattering, words to describe CUPL:
I've been able to get CUPL to work, but haven't had any success with Ignorance. Or, for that matter, with JOSS.
To find out more about these languages, HOPL is your friend: Bliss, CUPL and JOSS.
as is Wikipedia: Bliss, CORC (CUPL precursor) and JOSS.
© Copyright Bruce M. Axtens., 2007
After two and half weeks of Ada, I had to have a break. I wandered over to the Retrocomputing Museum and downloaded the Ignorance source code. While I was at it I also downloaded the source for CUPL, the Cornell University Programming Language. Interestingly, the same interpreter also works for CORC, the CORnell Compiler. The man behind the website, Eric Raymond, has the following, less than flattering, words to describe CUPL:
"Another hideous old design, CUPL — Cornell University Programming Language. It looks something like a really archaic BASIC with linear-algebra builtins."And what's wrong with BASIC and linear algebra? (That was a rhetorical question.)
I've been able to get CUPL to work, but haven't had any success with Ignorance. Or, for that matter, with JOSS.
To find out more about these languages, HOPL is your friend: Bliss, CUPL and JOSS.
as is Wikipedia: Bliss, CORC (CUPL precursor) and JOSS.
© Copyright Bruce M. Axtens., 2007
Labels:
Bliss,
Cornell,
CUPL,
Eric Raymond,
Ignorance,
JOSS,
Retrocomputing Museum
Thursday, December 13, 2007
[Ada (GNAT)] Who said, "Ignorance is bliss"?
Ignorance isn't bliss. For the past two days I've been trying to do what I expect an Ada freak could do in 2 minutes. It's been a very frustrating, sleep-depriving process.
After trying to do amazing things with GNAT.Spitbol, I'm now trying to do the same to Dr. Dmitry A. Kazakov's match library. And, as usual, it has not been an easy journey.
UTF-32 aside for now, I've been trying this week simply to make the Matching and Pattern packages available to the Windows programmer via the DLL mechanism.
At this point, all I have is a wrapper for the match() function.
I really hope there is an easier way of doing this. As I've said a few times before, I'm a newbie when it comes to Ada but have been forced to jump into the deep end of the pool. Please, if you know a better way, tell me!!
Oh, and I should point out that, though it seems I'm passing the right stuff to match(), in its current form the VB6 code keeps receiving an error code from the Matching package. It may be because I'm not declaring the pattern properly.
Okay, to the code. First up, the BOS.ADS spec file:
Next, the body (BOS.ADB). There are lots of Put calls in here for debugging. These are what appear if you run the VB6 code as a console app.
For the sake of completeness, BOS.DEF, BUILD.GPR and BUILD.BAT. Note that in BUILD.BAT, I specify match\match.o as an object file for gnatlink.
Finally, the VB6 code being used to test it all. Please note that I'm using SetSubSys to turn the resulting EXE into a Console application. If you don't do that, the output from Ada's Put() statements will not be made visible.
It's not pretty code, but it does the job. By the way, if you don't do the StrConv, you'll be passing UTF-16 strings.
© Copyright Bruce M. Axtens., 2007
After trying to do amazing things with GNAT.Spitbol, I'm now trying to do the same to Dr. Dmitry A. Kazakov's match library. And, as usual, it has not been an easy journey.
UTF-32 aside for now, I've been trying this week simply to make the Matching and Pattern packages available to the Windows programmer via the DLL mechanism.
At this point, all I have is a wrapper for the match() function.
I really hope there is an easier way of doing this. As I've said a few times before, I'm a newbie when it comes to Ada but have been forced to jump into the deep end of the pool. Please, if you know a better way, tell me!!
Oh, and I should point out that, though it seems I'm passing the right stuff to match(), in its current form the VB6 code keeps receiving an error code from the Matching package. It may be because I'm not declaring the pattern properly.
Okay, to the code. First up, the BOS.ADS spec file:
Next, the body (BOS.ADB). There are lots of Put calls in here for debugging. These are what appear if you run the VB6 code as a console app.
For the sake of completeness, BOS.DEF, BUILD.GPR and BUILD.BAT. Note that in BUILD.BAT, I specify match\match.o as an object file for gnatlink.
Finally, the VB6 code being used to test it all. Please note that I'm using SetSubSys to turn the resulting EXE into a Console application. If you don't do that, the output from Ada's Put() statements will not be made visible.
It's not pretty code, but it does the job. By the way, if you don't do the StrConv, you'll be passing UTF-16 strings.
© Copyright Bruce M. Axtens., 2007
Labels:
DLL,
Dr. Dmitry A. Kazakov,
GNAT,
GNAT/GCC,
Matching,
Pattern,
pattern matching,
Spitbol
Tuesday, December 11, 2007
[Google] AdSense and Analytics
On the suggestion of my eldest, I've added Google Analytics. In the next couple of days I should hear back from Google AdSense and will trial having Google Ads on the page. If it earns, fine. If not, I think I'll give it a miss. "Suck it and see," as my late father would have said.
Monday, December 10, 2007
[Ada (GNAT)] A 32bit GNAT.Spitbol.*
It is said that, "a wise man knows his own turnips." In my case, I've run into some difficulty converting GNAT.Spitbol to UTF-32. The compiler error messages are marvellously helpful, but I'm a newbie to Ada and ... yeah, well, you get the idea.
The source code is up on Box.net, if you feel so inclined as to download it and tell me where I'm going wrong.
The source code is up on Box.net, if you feel so inclined as to download it and tell me where I'm going wrong.
Monday, December 03, 2007
[Ada (GNAT/GCC)] VARPTR, GNAT and data "transput"
Okay, have got it figured (soli Deo gloria!)
The following listings demonstrate the passing of a two part Type containing two Singles from VB6 into a GNAT/GCC DLL. Once inside, the elements of the Type are swapped and control returns to VB6. VB6 then displays the results.
As before, the code has been worked out in the context of the afore-posted
First up, the additions to api.ads:
Next, the additions to api.adb:
API.DEF must also be modified, adding
Next, some VB code to demonstrate ...
... and part of a sample output.
The project that all this was part of was done in both GNAT/GCC and ObjectAda 7.2.2. Interestingly, ObjectAda's Float is equivalent to VB6's Double, whereas GNAT/GCC's Float is equivalent to VB6's Single. Does anyone know why?
By the way, regarding "transput", Wikipedia's article on Algol states:
© Copyright Bruce M. Axtens, 2007
The following listings demonstrate the passing of a two part Type containing two Singles from VB6 into a GNAT/GCC DLL. Once inside, the elements of the Type are swapped and control returns to VB6. VB6 then displays the results.
As before, the code has been worked out in the context of the afore-posted
api project. If you want to play with this, just take the earlier work and tweak it appropriately.First up, the additions to api.ads:
Next, the additions to api.adb:
API.DEF must also be modified, adding
swapsingles to the list of EXPORTS.Next, some VB code to demonstrate ...
... and part of a sample output.
The project that all this was part of was done in both GNAT/GCC and ObjectAda 7.2.2. Interestingly, ObjectAda's Float is equivalent to VB6's Double, whereas GNAT/GCC's Float is equivalent to VB6's Single. Does anyone know why?
By the way, regarding "transput", Wikipedia's article on Algol states:
ALGOL 60 as officially defined had no I/O facilities; implementations defined their own in ways that were rarely compatible with each other. In contrast, ALGOL 68 offered an extensive library of transput (ALGOL 68 parlance for Input/Output) facilities.A extensive library of transput facilities is something you'll find in Protium, too.
© Copyright Bruce M. Axtens, 2007
Wednesday, November 28, 2007
[Ada (GNAT/GCC)] Passing data with VB6's VARPTR to Ada
In the current project, I've been trying to figure out how to pass data to Ada. Because I'll be passing Long, Double (Float), and two Types to emulate a LongLong and a DoubleDouble, I decided I'd use VB's VarPtr and have Ada sort the mess out on its side.
After the loss of a little hair, the reading of bits of Michael B. Feldman's Software Construction and Data Structures with Ada 95, and with help from Martin Krischik and Simon Wright, I came up with the following.
The code was worked out in the context of the afore-posted
First up, the additions to api.ads:
Next, the additions to api.adb:
Next, some VB code to demonstrate ...
... and a sample output.
I'm in the process now of working out how to access the LongLong and DoubleDouble Types. Shall keep you posted.
By the way, the return value will eventually be a pointer as well. That'll keep the VB programmer on his toes.
© Copyright Bruce M. Axtens, 2007
After the loss of a little hair, the reading of bits of Michael B. Feldman's Software Construction and Data Structures with Ada 95, and with help from Martin Krischik and Simon Wright, I came up with the following.
The code was worked out in the context of the afore-posted
api project. If you want to play with this, just take the earlier work and stuff this into it.First up, the additions to api.ads:
Next, the additions to api.adb:
Next, some VB code to demonstrate ...
... and a sample output.
I'm in the process now of working out how to access the LongLong and DoubleDouble Types. Shall keep you posted.
By the way, the return value will eventually be a pointer as well. That'll keep the VB programmer on his toes.
© Copyright Bruce M. Axtens, 2007
Monday, November 26, 2007
[Ada (GNAT/GCC)] Better build automation
Just after I notified comp.lang.ada of my posting re GNAT/GPL, Martin Krischik wrote suggesting I use a .gpr file and
After reading a friendly manual, I came up with the following .grp file and a batch file to control it.
I have stayed with the batch file, simply because I can't yet see how to have a multiple-program linkage (
First,
And then,
Trust that's helpful.
© Copyright Bruce M. Axtens, 2007
gnat make rather than a direct reference to gcc. After reading a friendly manual, I came up with the following .grp file and a batch file to control it.
I have stayed with the batch file, simply because I can't yet see how to have a multiple-program linkage (
gnatlink, dlltool and gnatlink a second time).First,
build.gprAnd then,
build.bat. Note the call to UPX, which compresses the release version of the .DLL. Note also that the debug and release subdirectories must already exist (though it wouldn't have been difficult to check-for-and-create-if-missing.)Trust that's helpful.
© Copyright Bruce M. Axtens, 2007
Friday, November 23, 2007
[Ada (GNAT/GCC)] Creating DLLs for VB6
And finally, after much prayer and perspiration, the GNAT/GCC version. Again, thanks to Roger Pearse for The Noddy Guide to using ADA code with Visual Basic. I'm sure there was someone else [This space set aside for an attribution.]
First up, api.ads, which declares the functions and exports them.
Next, api.adb, where the declarations are explained.
I'm still working through the issue of what to do with Mangle. Perhaps two routines are needed. The first to ask the DLL how much space is needed, with VB the allocating it, and the second then actually poking the data into the allocated space.
Pattern returns a pointer to the string data. I rediscovered the technique for reading it out and that's in the VB code below. It also seems that, for as long as the DLL is loaded, the data stored in
api.def lists the symbols exported, and is needed by gnatdll.
Now, pulling it all together requires some handwaving on the commandline. From one of the demos I modified the make.bat to look like this:
The output from a run of these is as follows:
Now the VB code. Take note of a few things:
Finally, a sample run under VB6
Next? Well, there's the issue of how to export overloaded functions and procedures. And there's the question of how to pass in complex data types, like records and the like. I'll keep you posted.
(By the way, I'm not doing this merely for the fun of it, but also in pursuit of the fulfillment of my employer's expectations. So more will follow.)
© Copyright Bruce M. Axtens, 2007
First up, api.ads, which declares the functions and exports them.
- function Factorial, which accepts a Win32.LONG and returns a Win32.LONG (I gave up on C.int as it was too small for a Factorial of 16)
- function GetCount, which returns the value of the global
Count(another Win32.LONG) - procedure Mangle, which accepts a Win32.LPWSTR and attempts the same dangerous stuff that Bob6 does in the ObjectAda version.
- function Pattern, which returns a long pointer to some string data
- procedures Initialize_API, and Finalize_API, which I understand are mandatory. (If someone knows better, please tell me.)
Next, api.adb, where the declarations are explained.
I'm still working through the issue of what to do with Mangle. Perhaps two routines are needed. The first to ask the DLL how much space is needed, with VB the allocating it, and the second then actually poking the data into the allocated space.
Pattern returns a pointer to the string data. I rediscovered the technique for reading it out and that's in the VB code below. It also seems that, for as long as the DLL is loaded, the data stored in
foo is available for transfer.api.def lists the symbols exported, and is needed by gnatdll.
Now, pulling it all together requires some handwaving on the commandline. From one of the demos I modified the make.bat to look like this:
The output from a run of these is as follows:
Now the VB code. Take note of a few things:
- Exporting as
DLLgenerates decorated names, but dlltool was able to resolve the decorated ones with the undecorated ones specified in api.def - For reasons I remember reading somewhere, initialize_api and finalize_api are mandatory, and must be the first and last calls to the DLL
- Seeing as woof woof woof woof is longer than foo man chew,
sends up containing foo man chew f woof - As mentioned before, I rediscovered some code for getting data from a pointer and put that in. It's the
PointerToStringfunction at the bottom.
Finally, a sample run under VB6
Next? Well, there's the issue of how to export overloaded functions and procedures. And there's the question of how to pass in complex data types, like records and the like. I'll keep you posted.
(By the way, I'm not doing this merely for the fun of it, but also in pursuit of the fulfillment of my employer's expectations. So more will follow.)
© Copyright Bruce M. Axtens, 2007
Subscribe to:
Posts (Atom)