Thursday, July 20, 2017

[FreeDOS] The FAST and SOFA compilers

Two recent additions to the FreeDOS stable of development languages are FAST (Fast PC Compiler) and SOFA (Super Optimised Fast Assembler), inventions of one Peter Campbell of New Zealand. Peter developed FAST and SOFA in the mid 1980s and they were used as the main development languages for the Fastbase accounting system. In 2000, Peter rewrote the entire system in Tcl. All use and development of FAST and SOFA stopped at that point.

SOFA is an 8088 (and semi-80386) assembler. The syntax has some similarities to two other shareware assemblers,
A86 and CHASM. SOFA can assemble its own source-code. Its features include quick assembly time (thousands of lines per second), conditional compilation and include files.

FAST was written in SOFA. The language syntax was influenced by Basic, Pascal, C and Assembler. The binaries produced are small and fast
[1].

Peter Campbell died in tragic circumstances at Easter 2007, leaving the Fastbase business to his brother, Russell Bell.

The adding of FAST and SOFA to FreeDOS took a little too long: I had heard of the language during the 1990s and had rediscovered it in the
Vetusware abandonware collection. From there I found out about FastBase and made contact with Russell. This was 2009 and it was at that time that I suggested to Russell that SOFA/FAST could be re-released as Open Source software, thus preserving in some way Peter's legacy. Russell promptly gave permission and provided me with the sources. Embarassingly, it has taken me eight years to finalise the process.

SOFA and FAST will appear eventually on
HOPL (The Online Historical Encyclopaedia of Programming Languages). In the meantime, postings regarding the languages can be found on HOPL's Facebook front-end at SOFA and FAST respectively.

[1] Already some FreeDOS utilities have been rewritten in FAST.

© Copyright Bruce M. Axtens, 2017

Wednesday, December 02, 2015

[Browsers] Inconsistent HTML Entities

I fancy that this is old hat to most of you, but I had thought HTML entities cut and dried: An ampersand, a word and a semicolon (reminds me a little of dBase III, actually.) It seems, however, that certain browsers are still unsure as to what constitutes an HTML entity, and where it should end.

One of the languages I use for server-side management uses the ampersand + word + semicolon format to mark where variables may be interpolated into the output stream. So it was with some surprise when it was pointed out to me by a colleague that &currentValue; was being rendered as ¤tValue;.

Being the inquisitive sort, I went to
dev.w3.org and downloaded all the HTML entity names. After a bit of fiddling about in jQuery, I had the list. After a bit more fiddling, this time in JScript, I ended up with an HTML file showing the entity name, the entity as rendered, and then what happens when you leave off the trailing semicolon and append some other text (in this case 'es'). This is where things get ... well ... unusual. Some HTML entites follow the standard. Others like ¤ and £ don't. You enter &poundage, expecting the browser to give &poundage only to have it give you £age. Weirdness abounds. Take · which actually renders as ¢erdotes!

So as to give you an idea of how it looks in your browser, I've put the file up in a
jsfiddle . Let me know how you get on and whether your browser is as compliant is it makes out.

Enjoy!


© Copyright Bruce M. Axtens, 2015

Saturday, November 07, 2015

[COBOL] Sum multiples of 3 and 5

Last night I posted the following code on RosettaCode as another solution to the Sum multiples of 3 and 5 challenge. (Mine is the third one down after all the more-mathematical solutions.)

My solution is as brute-force as it gets, using only adds and comparisons.
       IDENTIFICATION DIVISION.
       PROGRAM-ID. SUM35.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  THREE-COUNTER   USAGE BINARY-CHAR value 1.
           88 IS-THREE VALUE 3.
       01  FIVE-COUNTER    USAGE BINARY-CHAR value 1.
           88 IS-FIVE VALUE 5.
       01  SUMMER          USAGE BINARY-DOUBLE value zero. 
       01  I               USAGE BINARY-LONG.
       01  N               USAGE BINARY-LONG.

       PROCEDURE DIVISION.
       10-MAIN-PROCEDURE.
           MOVE 1000000000 TO N.
           MOVE 1 TO I.
           PERFORM 20-INNER-LOOP WITH TEST AFTER UNTIL I >= N.
           DISPLAY SUMMER.
           STOP RUN.
       20-INNER-LOOP.
           IF IS-THREE OR IS-FIVE 
               ADD I TO SUMMER END-ADD
               IF IS-THREE
                   MOVE 1 TO THREE-COUNTER
               ELSE
                   ADD 1 TO THREE-COUNTER
               END-IF
               IF IS-FIVE
                   MOVE 1 TO FIVE-COUNTER
               ELSE    
                   ADD 1 TO FIVE-COUNTER
               END-IF
           ELSE
               ADD 1 TO FIVE-COUNTER END-ADD
               ADD 1 TO THREE-COUNTER END-ADD
           END-IF.
           ADD 1 TO I.
           EXIT.
       END PROGRAM SUM35.
The above code compiles on GnuCOBOL 2.0 and MicroFocus Visual COBOL 2.3. In the latter environment, I was able to get a run time of 7.3 seconds for the 1,000,000,000 iterations (AMD A6-5200 APU running at 2.00 GHz.)

I ended up in COBOL via the usual circuitous route through other programming languages after figuring out a solution using
mLite. The next postings will demonstrate the mLite and perhaps others.

© Copyright Bruce M. Axtens., 2015

Sunday, October 11, 2015

[GnuCOBOL 2.0] Beginning COM

In my ample (NOT!) spare time, I'm exploring making COM available to the Windows implementation of GnuCOBOL. You can do GnuCOBOL on Linux and Mac as well.

Many thanks to
Brian Tiffin over on GnuCOBOL's Sourceforge Forum for cheering me on.
       IDENTIFICATION DIVISION.
       PROGRAM-ID. OLE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  HRESULT     USAGE BINARY-LONG.
           88 S_OK     VALUE 0.
           88 S_FALSE  VALUE 1.
       01  NUL         USAGE BINARY-LONG VALUE 0.
       
       PROCEDURE DIVISION.
       MAIN-PROCEDURE.
        CALL STDCALL "OleInitialize"
            USING BY VALUE NUL
            RETURNING HRESULT.
            DISPLAY HRESULT.

        CALL STDCALL "OleUninitialize" USING
            BY REFERENCE OMITTED
            GIVING NULL.

            STOP RUN.
       END PROGRAM OLE.
The are some runtime settings (Windows 10 Home, 64bit) required to get that working, namely
set COB_PRE_LOAD=ole32
set COB_LIBRARY_PATH=C:\windows\syswow64
I should sometime create a Github project for this, in case others want to run with it.

© Copyright Bruce M. Axtens, 2015

[CSNOBOL4] Decimal to Binary #2

Many thanks to Gordon Peterson who took my non-idiomatic code and improved it both in size and speed, viz
         DEFINE("BIN(N)")                            :(BIN_END)
BIN      BIN = (GT(N,1) BIN(N / 2), ) REMDR(N,2)     :(RETURN)
BIN_END
This is guru level stuff.

I'm also grateful to
Fred Weigel who first responded to my posting in the Snobol Yahoo! Group and made some very useful suggestions. He points out that Gordon's solution is a prime candidate for DEXP, viz:
dexp('bin(n) = (gt(n, 1) bin(n / 2), ) remdr(n, 2)')
That's enough CSNOBOL4 for now, though if you do want to spend some time with it (Windows or Linux), I'd suggest using Rafal M. Sulejman's TkSLIDE.

© Copyright Bruce M. Axtens, 2015

Wednesday, October 07, 2015

[CSNOBOL4] Decimal to Binary

Okay, I think this is the last of the solutions to Binary Digits. Thie one's in CSNOBOL4. Kudos to Phil Budne for maintaining and expanding this amazing programming language.

The code below, like all the ones before it, is recursive.
        DEFINE('BIN(N,STR)')              :(BIN_END)
BIN     EQ(N,0)                           :S(NZERO)
        BIN = BIN(N / 2, REMDR(N,2) STR)  :S(RETURN)
NZERO   BIN = EQ(SIZE(STR),0) '0'         :S(RETURN)
        BIN = STR                         :(RETURN)
BIN_END

        OUTPUT = BIN(0)
        OUTPUT = BIN(5)
        OUTPUT = BIN(50)
        OUTPUT = BIN(9000)
END


© Copyright Bruce M. Axtens, 2015

[Euphoria] Decimal to Binary

Having solved the Binary Digits task in mLite and FBSL, I thought I'd have a go in OpenEuphoria. Kudos to David Craig of Rapid Deployment Software for the original implementation and to the subsequent team of heroes for a very powerful Windows/Linux/Mac programming (interpreted and/or compiled) language.

The code below, expressed in v4.1.0 form, follows the FBSL fairly closely.
include std/math.e 
include std/convert.e

function Bin(integer n, sequence s = "")
  if n > 0 then
   return Bin(floor(n/2),(mod(n,2) + #30) & s)
  end if
  if length(s) = 0 then
   return to_integer("0")
  end if
  return to_integer(s)
end function

printf(1, "%d\n", Bin(0))
printf(1, "%d\n", Bin(5))
printf(1, "%d\n", Bin(50))
printf(1, "%d\n", Bin(9000))
There's already an Euphoria solution to the task. Some enterprising soul might want to check which of the two solutions is the faster.

© Copyright Bruce M. Axtens, 2015

[FBSL] Decimal to Binary

Having solved the Binary Digits task in mLite, I thought I'd have a go in FBSL. Kudos to the FBSL team for a very useful and powerful Windows scripting language. Even ABAP users can leverage it!

The code below, expressed in v3.5 RC form, follows the mLite fairly closely.

#AppType Console
function Bin(byval n as integer, byval s as string = "") as string
 if n > 0 then return Bin(n \ 2, (n mod 2) & s)
 if s = "" then return "0"
 return s
end function

print Bin(5)
print Bin(50)
print Bin(9000)

pause
It was during the testing of this code that it dawned on me that the mLite wasn't handling the binary 0; appropriately. Thus those who notice such things will discover a subtle change in the mLite posting.

© Copyright Bruce M. Axtens, 2015

Tuesday, October 06, 2015

[mLite] Convert to Binary Notation

A short diversion: the Standard ML dialect called mLite is where I go for the intellectual challenge of thinking in a functional programming way. Kudos to Nils M. Holm for developing it. mLite is one of the languages I use to solve RosettaCode tasks, in this case, Binary Digits.

This turned out to be a fairly simple task and only took me a few minutes to solve. Granted, I reused some code from the logarithm calculator of a few postings ago, but that was just for the conversion of a list of numbers to their printable form.
fun binary
   (0, []) = "0"
 | (0, x) = implode ` map (fn x = if int x then chr (x + 48) else x) x
 | (n, b) = binary (n div 2, n mod 2 :: b)
 | n = binary (n, [])
; 
The gist of that is
  • pass in a number and recurse with the number and an empty list.
  • With a number that isn't zero recurse with the number divved by 2 and the list prepended with the number modded by 2.
  • Keep going until the number is zero. At that point, convert the answer's digits to string representation and implode them into a single string.
  • If the number is zero and there are no digits, return "0"
Might get back to some VB6 next. Or javascript.
© Copyright Bruce M. Axtens, 2015

Sunday, October 04, 2015

[VB6] Map and Reduce

Following on from a previous posting about Fluent VB6 we look now at two routines which pop up a lot these days: Map and Reduce. On offer here is one way of implementing these two functional programming stalwarts. They are presented here as part of a FluentVB6 object, but could just as easily be declared and used separately.

I'm implementing these using the
MSScript object which also appeared in the aforementioned posting. This makes available to the programmer, out of the box, the functionality of two scripting languages, VBScript and JScript. Other languages are available within the Windows Scripting Host environment and these could also be used (e.g. PerlScript as per the link.)

The code below tests the Map and Reduce functions. After this the implementation will be discussed.
Dim words As New Collection
    words.Add "cat"
    words.Add "dog"
    words.Add "cow"
    words.Add "wolf"
    words.Add "rat"
    
    Dim F As New FunctionalObject
    
    Dim upperwords As New Collection
    Set upperwords = F.WorkingWith(words).Map("UCase(Value)").AsCollection()
    
    Dim concatenated As Variant
    F.Reset
    concatenated = F.WorkingWith(upperwords).Reduce(vbNullString, "InitValue = InitValue & Value").asValue()
    
    Dim counted As Variant
    counted = F.Reset().WorkingWith(upperwords).Reduce(0, "initValue = initValue + Len(Value)").asValue()
For the sake of simplicity, I'm limiting things to Collections in, Collections and Variants out. The code could be readily changed to handle Arrays, Dictionaries and other data structures.

The class is called FunctionalObject and it begins with
Option Explicit

Private workingCollection As Collection
Private incomingCollection As Collection
Private workingValue As Variant
Private SC As ScriptControl

Private Sub Class_Initialize()
    Set workingCollection = New Collection
    Set incomingCollection = New Collection
    workingValue = vbEmpty
    Set SC = New ScriptControl
End Sub
No surprises there. Again, the ScriptControl object is the OCX added via the Project menu as a Reference rather than as a Component. One could late-bind with CreateObject but there's not much point in this case.

Next the public function to receive the incoming collection
Public Function WorkingWith(inCol As Collection) As FunctionalObject
    Set incomingCollection = inCol
    Set WorkingWith = Me
End Function
Reset clears the workingCollection (in the event that you reuse the currently instantiation of the FunctionalObject rather than instantiate another one.)
Public Function Reset() As FunctionalObject
    Dim i As Integer
    For i = 1 To workingCollection.Count
        workingCollection.Remove 1
    Next
    workingValue = vbEmpty
    Set Reset = Me
End Function
wrapText you'll seen before from the previous posting. It just makes the incoming collection's value palatable to VBScript.
Private Function wrapItem(v As Variant) As String
    If VarType(v) = vbString Then
        wrapItem = Chr$(34) & v & Chr$(34)
    Else
        wrapItem = CStr(v)
    End If
End Function
Next the Map function. The ScriptControl language is set to VBScript and the "safe subset" of script language functions is selected. Then the code iterates through each element of the incoming collection, and sets a VBScript place-holder variable called Value to that element. Next the map script is evaluated in the context of Value, the result being added to the working collection.

The example at the top of page has the map script as "UCase(Value)", so the value stored in the working collection is the uppercase of the value in the incoming collection.
Public Function Map(Optional script As String = "Value") As FunctionalObject
    SC.Language = "VBScript"
    SC.UseSafeSubset = True
    Dim i As Integer
    For i = 1 To incomingCollection.Count
        SC.ExecuteStatement "Value = " & wrapItem(incomingCollection.Item(i))
        workingCollection.Add SC.Eval(script)
    Next
    Set Map = Me
End Function
Reduce works in a similar manner except the result is a variant. There is the expectation that the reduce script will somehow work toward deriving a single value from the incoming collection, thus the use of an second place-holder called InitValue. The first parameter of the Reduce call is stored in InitValue with the expectation that the reduce script will refer to it and to the Value place-holder.

For example, one of the examples from the first code block reads, in part,
Reduce(0, "initValue = initValue + Len(Value)").
This reduces the collection to a value accruing the lengths of the strings assumed to be in the incoming collection.

Both parameters to Reduce are marked as optional. If neither is specified, the Reduce does nothing except set Value to InitValue, effectively filling the working collection with as many zeroes as there are items in the incoming collection.
Public Function Reduce(Optional initval As Variant = 0, Optional script As String = "Value = InitValue") As Variant
    SC.Language = "VBScript"
    SC.UseSafeSubset = True
    Dim vAnswer As Variant
    Dim vItem As Variant
    Dim vResult As Variant
    Dim i As Integer
    SC.ExecuteStatement "InitValue = " & wrapItem(initval)
    For i = 1 To incomingCollection.Count
        vItem = incomingCollection.Item(i)
        SC.ExecuteStatement "Value = " & wrapItem(vItem)
        SC.ExecuteStatement script
    Next
    workingValue = SC.Eval("InitValue")
    Set Reduce = Me
End Function
Finally the two output functions, asCollection and asValue. The former copies the working collection to an answer collection and returns that to the caller. asValue returns the working value from the Reduce.
Public Function AsCollection() As Collection
    Dim answerCollection As New Collection
    Dim i As Integer
    For i = 1 To workingCollection.Count
        answerCollection.Add workingCollection.Item(i)
    Next
    Set AsCollection = answerCollection
End Function

Public Function asValue() As Variant
    asValue = workingValue
End Function
I will make the sources available on Github in the near future.

© Copyright Bruce M. Axtens, 2015