Git Product home page Git Product logo

sancarn / extended_mapbasic Goto Github PK

View Code? Open in Web Editor NEW
1.0 1.0 0.0 5.52 MB

This is an Open Source project. We are developing a powerful extension for the MapBasic Window in Pitney Bowes' MapInfo. The extension will give users the ability to compile on demand from the MapBasic Window. Further extensions may include custom MapBasic functions to accomplish common tasks. This project is still very much WIP

License: MIT License

AutoHotkey 39.06% Visual Basic 8.09% HTML 23.96% JavaScript 28.89%

extended_mapbasic's People

Contributors

sancarn avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar

extended_mapbasic's Issues

Detecting when MapBasic window is active and a word on the Message window

I was hoping we would be able to grab the hWND from MapInfo using the WindowInfo() function.
The MapInfo window ID for the MapBasic window is 1002.

If I do the following:
print WindowInfo(1002,12) '[or WindowInfo(WIN_MAPBASIC,WIN_INFO_WND) ]

I get 0 in the Message Window, regardless of whether the window is bound to MapInfo or not.

Unfortunately also there is no specific info that we can target from AHK that would indicate the current window being the MapBasic Window. However there is the active window's text which we can get with WinGetText statement. That contains this:

<<
>>
 List 

If the MapBasic window is not docked to the main application however the title of the window is indicated as "MapBasic". Therefore we can say:

WinGetTitle, TITLE, A
MapBasicWindowActive(){
    If winexist("MapBasic"){
        return TITLE = "MapBasic"
    } else {
        ;...
    }
}

The question really comes down to what we do in that ...


On a more positive note the Message window is much easier to access:
When docked:

ClassNN: RICHEDIT1
WindowInfo(1003,12) returns 1117068 (at least it returns something!)
again if not docked wintitle = "Message"

Auto-Declare System

Finding Sub-Routines without declares:

One of the features I wanted to implement was the Auto-Declare feature. It always annoyed me that you had to declare all functions and sub routines when creating MapBasic application. I still don't know why this is important to be honest... Many other programming languages I have worked with have been fine without function declares, so why should MapBasic be any different? Regardless, this feature will put that right.

The plan is to remove all existing declares (for compatibility), and then to recreate them. We will also allow this feature will be disable-able.

In this issue, I will make a few suggestions as to how we may go about implementing Auto-Declare.

Original idea:

Selects "Sub ... End Sub" blocks:
(?<!Declare )sub\s+(?!\bsub\()\w+\(\)(.|\s)*?End\s+Sub <-- Selects Sub ... End Sub blocks
[(?<!Declare\s+) doesn't work which implies this will not work


Subs extracted into capturing groups (see http://www.rexegg.com/regex-best-trick.html):

Declare\s+(Sub).*|(\b(Sub)\b.*\(\))

Functions into extracted sub-patterns:
Declare\s+\bFunction\b.*|(\bFunction\b.*\(\).*)

Problem occurs with arguments in the functions...


Final patterns?

These are the final patterns I came up with tonight. They could probably be extended further (they might have to be) but for the purpose of Auto-Declare this system is probably fine

Functions

\bDeclare\s+Function\s+\w+\($args$\)\s+as\s+\w+|(\bFunction\s+\w+\($args$\)\s+as\s+\w+)

Sub-routines

\bDeclare\s+Sub\s+\w+\($args$\)|(\bSub\s+\w+\($args$\))

Important:

Replace $args$ with (?:\s*\w+\s+as\s+\w+\s*){0,1}(?:,\s*\w+\s+as\s+\w+\s*)*


Final words

Now that we have a pattern we need to simply test the program. Also things will likely change in the future with MBP compatibility. That's for another day...

The repository is already a mess...

Something that should be thought about is how the source code and the repository is organised.

Note - It may be useful to combine all AHK files in the repository into one. For this we can use this

For a start we may want a folder: "src". In this folder we will keep:
The main script, the script to compile, should probably be Main.AHK.
All inclusion statements should be contained in a separate Headers.AHK.
A folder lib_core (storing all .AHK files for the core features compiler.)
A folder lib_emb (storing folders of .AHK files built to extend MapBasic)

Here's a picture of how I envision it:

extended mapbasic organisation

It is likely to end up different to this and many files which should be separate will likely be combined together for splitting later on. I also still don't know whether the header file will work as I think it will. It's all currently untested but this will be much better than its current state.

[Idea] Table.ColumnName in SQL Select

Something that annoys me a fair amount is when I am trying to loop over one table and select records in the other. For instance:

For i =1 to tableinfo(MyLookup,8)
  fetch rec n from MyLookupTable
  select * from MyTable where Lookup = MyLookupTable.Lookup into Q
  '... Do something with Q ...
  close Q
next

In my mind this should work in exactly the same way as the following:

Dim s as string
For i =1 to tableinfo(MyLookup,8)
  fetch rec n from MyLookupTable
  s = MyLookupTable.Lookup
  select * from MyTable where Lookup = s into Q
  '... Do something with Q ...
  close Q
next

However instead it simply errors out. I believe that this should be fixed in the MapBasic language and Should be, therefore, fixed in EMB.

[IDEA] clsProgressBar

EMB - Progress Bar:

Option 1 - Transpile to Native MapBasic

    Sub doTheStuff()
        Dim pb as New clsProgressBar    'This is a Extended MapBasic call to create a new progress bar
        pb.Title "Doing the stuff!"     'Set the Progress bar text.
        pb.Range = 5                    'Set the number of stages of the progress bar.
        pb.Show
            'do stuff
            pb.progress = pb.progress + 1
            'do more stuff
            pb.progress = pb.progress + 1
            'do more stuff
            pb.progress = pb.progress + 1
            'do more stuff
            pb.progress = pb.progress + 1
            'do more stuff
            pb.progress = pb.progress + 1
            'do more stuff
        pb.Hide
    End Sub

Transpiles to:

    Dim mnProgress1 as integer

    Sub doTheStuff()
        ProgressBar "Doing the stuff!" Calling doProgressBar(1) Range 5
    End Sub

    sub doProgressBar(i)
        Do Case i
            Case 1
                mnProgress1 = mnProgress1 + 1
                Do case mnProgress
                    Case 1
                        'doStuff
                    Case 2
                        'do more stuff
                    Case 3
                        'do more stuff
                    Case 4
                        'do more stuff
                    Case 5
                        'do more stuff
                End Case

                If mnProgress = 5
                    ProgressBar = -1
                Else
                    ProgressBar = mnProgress1
                End If

            'More cases...
        End Case
    End Sub

Option 2 - Custom dialog

No example yet...

Option 3 - External .NET Dialog

No example yet...

Ideas for the addition of custom MapBasic functions

It may be desired for these custom MapBasic functions to reside separate from the standard application (perhaps in a sub-folder, A_ScriptDir/Libraries/...)

Required information:

  • code = {...} block, indicating what code should be inserted into the .MB file.

Each of these statements should be surrounded in a special string to provide easy extraction with RegEx. For example:

{
Sub say(someArg as string)
    print someArg
End Sub
}

Declares will be automatically added into the header of the file and should not be included in the code block. All Declare blocks will be removed with RegexReplace(str,"Declare.*","") [unless anyone can offer a reason as to why this is a bad idea!]

Each sub/function should be kept separate. If one requires the existence of another a comment should be added 'Requires "Name of Function or Sub". - Why? Mainly for future features. It may be nice for users to select which functions they want to use from which libraries.

It may also be nice to add functionality for custom statements. Classes and COMObjects would also be a nice feature if possible to implement in MB.


Custom Statement Idea:
Set up a Regex replace to find statements and replace them with sub-routine calls:

{
{
statement: place\s+"(.*)"\s+(on|in)\s+"(.*)"
}
{
replace: call place(match1,"match2",match3)
}
}

Then define the sub-routine calls separately:

{
sub place(object as string, on_in as string, furniture as string)
    if object = "" then
        Note "An error occurred in statement place - Cannot place nothing"
    end if
    if on_in <> any("on","with") AND furniture <> "" then
        Note "An error occurred in statement place - Cannot place item"
    end if
    print object & " is now " & on_in & " " & furniture
end sub
}

This setup would convert something like this:
place "this" on "that"

Into this:
call place("this","on","that")

Which would call the base sub-routine function. In the end these are simply sub-routines, but they may be easier to understand in this context than as actual sub-routine.

Determining position of cursor in MapBasic Window

While the MapBasic window doesn't have a hWND, here is a simple key-stroke to determine the position of the cursor, execute all commands, and return to the original position.

Suspend, On
old_clipboard := ClipboardAll
Send, ^+{Home}
Send, ^c
pos := StrLen(clipboard)
Send, ^a
Suspend,Off
Send. {Enter}
;... do stuff
Send,^{Home}
Loop, pos
send, {Right}
clipboard := old_clipboard

Find "If ... End If" blocks avoiding "Catastrophic Backtracking"

Algorithm:

Use "\bIf\s+.+\s+Then" to find "If ... then"
Use "\bEnd\s+If\b" to find End If.

E.G.

Positions of END: 170, 234, 746, 754
Positions of IFS: 115, 181, 383, 633

Concatenate e or i depending on which list they're in. Then merge lists:
170E, 234E, 746E, 754E, 115I, 181I, 383I, 633I

Sort list:
115I, 170E, 181I, 234E, 383I, 633I, 746E, 754E

This is our nested if ... end if structure.

Including files other than DEF/STR files

Including a real file - Syntax:

Include "C:\myfile.bas", filePath as string

From this point filePath will represent a real file path for "myfile.bas"

When we transpile to .MB we will transpile it to code like this:

If fileexist("C:\myfile.bas" then
   filePath = "C:\myfile.bas"
else 
    filePath = makeFile(<GUID>)
End If

Dim <GUID> as string
<GUID> = hex(<fileContents>)

Sub makeFile(fileContents as string)
...
End Sub

Pre-Compiler

Interlude

To make this project easy to manage and easy to extend, a pre-compiler may need to be created. In this issue I will explain why this is necessary and what the issues with the idea are, as well as some potential solutions to said problems.

Difficulty to Extend

What are the current steps required when extending (and compiling) the Extended MapBasic Application?

Create transpiling library

Of course the first step is to make the code which takes some source code and converts that into the new transpiled source code. Currently there is no convention to how the transpiling function should be named. Let's assume, for simplicity, it's defined as f().

Ultimately the transpiling library acts a bit like this:

mbsource = f(mbsource)

Editing EMB_Core/transpile.ahk

Writing the functions isn't the only thing that needs to be done before compiling. You also need to link these new transpiling functions to the rest of the application. You do this by modifying the "LIB_Core/transpile.ahk" file with the code above:

mbsource = EMB_Lib_BlockComment(mbsource)
mbsource = EMB_Lib_BlockQuote(mbsource)
mbsource = EMB_Lib_AutoDeclare(mbsource)
mbsource = EMB_Lib_OptionalArgs(mbsource)
mbsource = f(mbsource)

Including the transpiling library in header.ahk

In order to try and simplify this process I decided to use a header file. This file declares all .AHK modules/libraries which are used in the final project. Of course our file needs to be included here also:

#Include "EMB_Lib/BlockComment.ahk"
#Include "EMB_Lib/BlockQuote.ahk"
#Include "EMB_Lib/AutoDeclare.ahk"
#Include "EMB_Lib/myFunctions.ahk"

RegEx for Transpile Requirement Detection

Before Extended MapBasic transpiles the MapBasic application it first detects whether it needs to transpile at all. To do this RegEx is added to the needsTranspiling() function of the file "EMB_Core/transpiler.ahk".

example needed

Other future features which might be required

The Problem

Modifying EMB is not easy!

Having to modify 4+ lines of code is definitely not a difficult task, however it does involve editing files within the "vital organs" of Extended MapBasic. An accidental error could cause serious damage to the compiled exe / cause damage to compiled MBX files which is what we want to avoid most, but it could also cause a lot of false bug-reports with working versions of Extended MapBasic.

The solution

Simple extensibility

The simplest extensibility is a simple "drag and drop" extensibility. E.G. If you want to add a library "MyFunctions.ahk" to Extended MapBasic, you simply drop the file into the "EMB_Lib" folder, make sure the transpilation regex is declared (as in the example below) and run Compile.EXE.

Compile.EXE will run:

  • Pre-Compile.EXE - Used to create all the requirements declared above from the contents and the file name of the file.
  • Ahk2Exe.EXE - Used to compile the Pre-Compile AHK files into a full .exe file.

Example extension "MyFunctions.AHK"

;RegEx = {i)=\s*myfunction\(\),i)=\s*myNextFunction\(\w+\s+as\s+\w+\)}

EMB_Lib_MyFunctions(mbsource) {
    ;Your AHK library code here
}

Note:

  • The RegEx specifies multiple different Regular Expressions, all of which would indicate that the source needs transpiling.
  • The name of the main function which transpiles the MapBasic code is called "EMB_Lib_MyFunctions". That is "EMB_Lib_" + .

More Problems?

Problem 1: Transpilation Order

Since now the user doesn't specify the specific transpilation order a problem might occur where transpilation of certain code might be required to run BEFORE other transpilation modules.

E.G.

A EMB module might add a new function TempTable with the definition TempTable(numCol as integer, optional colDefs as string, optional colDelimeter as string)

In this case the EMB module should be transpiled BEFORE EMB_Lib_OptionalArguments() and not after.

Potential Solutions:

One option is to add a Transpile_Before option:

;T_BEFORE = "EMB_Lib_OptionalArguments()"
;RegEx = {TempTable\(}

EMB_Lib_MyFunctions(mbsource) {
    ;Your AHK library code here
}

A standard to be kept

Anyone who is used to standards know that it is innately difficult for people to keep said standards.

A potential solution

A simple AHK-IDE for creating transpilation libraries might be useful. The IDE would enforce users to specify whether their function should be transpiled before certain modules as well as the transpilation regex.

It is also important to note that Error checking should be implemented into the pre-compiler to check validity of modules before implementing changes to the source

Compile to .EXE

Recently I changed the front page adding an intended feature, that I had never dreamt of before:

  • Compile to .exe

But... How? In this issue I am going to brainstorm my ideas on this.

How it works

The actual idea is pretty simple. Our Transpiled MapBasic source code will be placed in a AHK file - mbSrc.ahk like:

MapBasicSrc = ("
<MapBasic code goes here>
")

We also create a MapBasicExecuter.ahk.

The first line of this code will Include mbSrc.ahk.

The rest of the application will parse be set up to parse the MapBasicSrc variable. It will identify For loops, if statements, dialogs statements and parse all necessary information to MapInfo using OLE Automation.

This will truly be revolutionary to MapBasic developers wanting to create simple but secure MapBasic applications - including the ability to even license MapBasic applications. The possibilities with this technique are potentially endless.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.