Making an OxygenBasic compiler with PureBasic

Just starting out? Need help? Post your questions and find answers here.
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Making an OxygenBasic compiler with PureBasic

Post by JoeC4281 »

I would like to use the Oxygen64.dll with PureBasic 6.02 LTS (Windows - x64)

A search of this forum for OxygenBasic did return some hits, but nothing that would help me.

This thread from 2012 says it has been done;
Ref: viewtopic.php?p=389922#p389922

The OxygenBasic Github site;
https://github.com/Charles-Pegge/OxygenBasic

Oxygen64.dll is a 64-bit .dll;

Code: Select all

E:\...\Oxygen>file oxygen64.dll
oxygen64.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows
I have placed the Oxygen64.dll into a folder with my test code;

Code: Select all

Volume in drive E is New Volume   Serial number is 2c1e:6e61
 Directory of  E:\Documents\PureBasic\Oxygen\*

2023-11-14   9:50         <DIR>    .
2023-11-14   9:50         <DIR>    ..
2023-11-13  22:39         141,312  oxygen64.dll
2023-11-14   9:55          13,824  test.exe
2023-11-14   9:55             740  test.pb
             155,876 bytes in 3 files and 2 dirs    163,840 bytes allocated
My test code;

Code: Select all

theString.s

If OpenLibrary(0, "oxygen64.dll")
  thestring = "result=10+20"
  oAddr = CallCFunction(0, "o2_basic", @"thestring")
  ; and/or instead of thestring, an .o2bas file
  ; Use o2_exec to execute thestring, and return the result
  oExec = CallCFunction(0, "o2_exec", oAddr)
  ; Get the result, which should be 30, and store it in oString
  ; Not sure how this would be accomplished.
  If OpenConsole()
    ; PrintN(oString)
    PrintN("Result to be printed.")
  EndIf
Else
  PrintN("Error opening oxygen.dll")
EndIf
The library gets opened.

I then step through this code, and the WatchList displays both oAddr and oExec returning 0.

Not sure if, in my test code, this is the proper way to call the functions from the dll.

I have also managed to create a Oxygen64.lib file;

I have created oxygen64.def from the oxygen64.dll;

Code: Select all

E:\...\Oxygen>dumpbin /exports oxygen64.dll > oxygen64.def

Microsoft (R) COFF/PE Dumper Version 14.37.32825.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file oxygen64.dll

File Type: DLL

  Section contains the following exports for oxygen64.dll

    00000000 characteristics
           0 time date stamp
        0.00 version
           1 ordinal base
          17 number of functions
          17 number of names

    ordinal hint RVA      name

          1    0 000A57F0 o2_abst
          2    1 000A55B0 o2_basic
          3    2 000A38D0 o2_buf
          4    3 000A3E30 o2_compile
          5    4 000A5880 o2_errno
          6    5 000A58E0 o2_error
          7    6 000A3C50 o2_exec
          8    7 000A3860 o2_len
          9    8 000A3A10 o2_lib
         10    9 000A3D30 o2_link
         11    A 000A3800 o2_mode
         12    B 000A37A0 o2_pathcall
         13    C 000A5770 o2_prep
         14    D 000A4CD0 o2_stats
         15    E 000A3740 o2_varcall
         16    F 000A5980 o2_version
         17   10 000A56F0 o2_view

  Summary

      303000 UPX0
       22000 UPX1
        1000 UPX2
I modified the oxygen64.def file;

Code: Select all

EXPORTS
o2_abst
o2_basic
o2_buf
o2_compile
o2_errno
o2_error
o2_exec
o2_len
o2_lib
o2_link
o2_mode
o2_pathcall
o2_prep
o2_stats
o2_varcall
o2_version
o2_view
I create the oxygen64.lib file;

Code: Select all

E:\...\Oxygen>lib /def:oxygen64.def /out:oxygen64.lib /machine:x64
Microsoft (R) Library Manager Version 14.37.32825.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library oxygen64.lib and object oxygen64.exp
Which gives me;

Code: Select all

E:\...\Oxygen>dir

 Volume in drive E is New Volume   Serial number is 2c1e:6e61
 Directory of  E:\Documents\PureBasic\Oxygen\*

2023-11-14  10:24         <DIR>    .
2023-11-14  10:24         <DIR>    ..
2023-11-14  10:24             178  oxygen64.def
2023-11-14  10:24             169  oxygen64.def.bak
2023-11-13  22:39         141,312  oxygen64.dll
2023-11-14  10:24           2,275  oxygen64.exp
2023-11-14  10:24           4,362  oxygen64.lib
2023-11-14   9:55          13,824  test.exe
2023-11-14  10:08             783  test.pb
             162,903 bytes in 7 files and 2 dirs    184,320 bytes allocated
Verify that oxygen64.lib has correct exports;

Code: Select all

dumpbin /exports oxygen64.lib

Microsoft (R) COFF/PE Dumper Version 14.37.32825.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file oxygen64.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  o2_abst
                  o2_basic
                  o2_buf
                  o2_compile
                  o2_errno
                  o2_error
                  o2_exec
                  o2_len
                  o2_lib
                  o2_link
                  o2_mode
                  o2_pathcall
                  o2_prep
                  o2_stats
                  o2_varcall
                  o2_version
                  o2_view

  Summary

          C6 .debug$S
          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
           E .idata$6
If I understand correctly, I can then create an;

Code: Select all

ImportC "Oxygen64.lib"
  o2_basic
  o2_exec
EndImport
Charles Pegge has made an OxygenBasic compiler with PowerBasic.
Ref: http://forum.it-berater.org/index.php?topic=4638.0

From his PowerBasic code, it shows that o2_basic accepts a byval string, and returns a string.
Also, o2_exec accepts an optional byval long, and returns a long.

I note that this is 32-bit, since PowerBasic is only 32-bit.

Not sure how to duplicate this in PureBasic.

I was also wondering if I should use Import instead of ImportC, but the help says;
On x64, there is only one calling convention, so ImportC will behave the sames as Import.
Constructive assistance would be appreciated on how to get this working,
and how to return results from the call back to PureBasic.

Joe
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by Olli »

Could you communicate what result does it display , please ?

Code: Select all

theString.s

If OpenLibrary(0, "oxygen64.dll")
  thestring = "result=10+20"
  Debug CallCFunction(0, "o2_basic", @thestring)
EndIf
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Making an OxygenBasic compiler with PureBasic

Post by infratec »

I also don't know how to get the outputs.

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf

#o2_mode_asci_0 = 0
#o2_mode_asci_1 = 1
#o2_mode_unicode = 2
#o2_mode_ole_bstring_ascii_1 = 8
#o2_mode_ole_bstring_ascii_2 = 9
#o2_mode_ole_bstring_unicode = 10

Macro o2_version()
  PeekS(o2_version_())
EndMacro

Macro o2_error()
  PeekS(o2_error_())
EndMacro

Macro o2_stats()
  PeekS(o2_stats_(), -1, #PB_Ascii)
EndMacro

Macro o2_view(a)
  PeekS(o2_view_(a))
EndMacro

Prototype.i Prototype_o2_abst(s.p-bstr)
Prototype.i Prototype_o2_asmo(*isrc)
Prototype.i Prototype_o2_basic(s.p-bstr)
Prototype.i Prototype_o2_buf(n.i)
Prototype.i Prototype_o2_errno()
Prototype.i Prototype_o2_error()
Prototype.i Prototype_o2_exec(p.i=0)
Prototype.i Prototype_o2_len()
Prototype.i Prototype_o2_lib()
Prototype.i Prototype_o2_link(s.p-bstr)
Prototype.i Prototype_o2_mode(m.i)
Prototype.i Prototype_o2_pathcall(m.i)
Prototype.i Prototype_o2_prep(s.p-bstr)
Prototype.i Prototype_o2_stats()
Prototype.i Prototype_o2_varcall(m.i)
Prototype.i Prototype_o2_version()
Prototype.i Prototype_o2_view(s.p-bstr)


Global o2_abst.Prototype_o2_abst
Global o2_asmo.Prototype_o2_asmo
Global o2_basic.Prototype_o2_basic
Global o2_buf.Prototype_o2_buf
Global o2_errno.Prototype_o2_errno
Global o2_error_.Prototype_o2_error
Global o2_exec.Prototype_o2_exec
Global o2_len.Prototype_o2_len
Global o2_lib.Prototype_o2_lib
Global o2_link.Prototype_o2_link
Global o2_mode.Prototype_o2_mode
Global o2_pathcall.Prototype_o2_pathcall
Global o2_prep.Prototype_o2_prep
Global o2_stats_.Prototype_o2_stats
Global o2_varcall.Prototype_o2_varcall
Global o2_version_.Prototype_o2_version
Global o2_view_.Prototype_o2_view

Global LibOxygen.i

Procedure.i Oxygen_Init()
  
  If Not IsLibrary(LibOxygen)
    CompilerIf #PB_Compiler_32Bit
      LibOxygen = OpenLibrary(#PB_Any, "oxygen.dll")
    CompilerElse
      LibOxygen = OpenLibrary(#PB_Any, "oxygen64.dll")
    CompilerEndIf
    If IsLibrary(LibOxygen)
      o2_abst = GetFunction(LibOxygen, "o2_abst")
      o2_asmo = GetFunction(LibOxygen, "o2_asmo")
      o2_basic = GetFunction(LibOxygen, "o2_basic")
      o2_buf = GetFunction(LibOxygen, "o2_buf")
      o2_errno = GetFunction(LibOxygen, "o2_errno")
      o2_error_ = GetFunction(LibOxygen, "o2_error")
      o2_exec = GetFunction(LibOxygen, "o2_exec")
      o2_len = GetFunction(LibOxygen, "o2_len")
      o2_lib = GetFunction(LibOxygen, "o2_lib")
      o2_link = GetFunction(LibOxygen, "o2_link")
      o2_mode = GetFunction(LibOxygen, "o2_mode")
      o2_pathcall = GetFunction(LibOxygen, "o2_pathcall")
      o2_prep = GetFunction(LibOxygen, "o2_prep")
      o2_stats_ = GetFunction(LibOxygen, "o2_stats")
      o2_varcall = GetFunction(LibOxygen, "o2_varcall")
      o2_version_ = GetFunction(LibOxygen, "o2_version")
      o2_view_ = GetFunction(LibOxygen, "o2_view")
      
      o2_mode(#o2_mode_ole_bstring_unicode)
    EndIf
  EndIf
  
  ProcedureReturn LibOxygen
  
EndProcedure


CompilerIf #PB_Compiler_IsMainFile
  
  Define o2.i
  
  If Oxygen_Init()
    
    Debug o2_version()
    
    Debug o2_view("print 10+20")
    
    o2 = o2_basic("print 10+20")
    If o2_errno()
      Debug o2_error()
    Else
      Debug o2_stats()
      o2_exec(o2)
      If o2_errno()
        Debug o2_error()
      Else
      
      EndIf
    EndIf
    
  EndIf
  
CompilerEndIf
Last edited by infratec on Thu Nov 16, 2023 10:27 pm, edited 1 time in total.
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by JoeC4281 »

Olli,
it returns an error;

Code: Select all

[17:20:37] Waiting for executable to start...
[17:20:37] Executable type: Windows - x64  (64bit, Unicode)
[17:20:37] Executable started.
[17:20:37] [ERROR] test.pb (Line: 5)
[17:20:37] [ERROR] Invalid memory access. (read error at address 39723008)
Joe
PeDe
Enthusiast
Enthusiast
Posts: 278
Joined: Sun Nov 26, 2017 3:13 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by PeDe »

I was just playing around, and with these changes the code and result is output.

Peter

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf


Prototype.i Prototype_o2_abst(*isrc)
Prototype.i Prototype_o2_asmo(*isrc)
Prototype.i Prototype_o2_basic(src.p-bstr)
Prototype.i Prototype_o2_buf(p.i)
Prototype.i Prototype_o2_errno()
Prototype.i Prototype_o2_error()
Prototype.i Prototype_o2_exec(p.i)
Prototype.i Prototype_o2_len()
Prototype.i Prototype_o2_lib()
Prototype.i Prototype_o2_link(*isrc)
Prototype.i Prototype_o2_mode(m.i)
Prototype.i Prototype_o2_pathcall(p.i)
Prototype.i Prototype_o2_prep(*isrc)
Prototype.i Prototype_o2_varcall(p.i)
Prototype.i Prototype_o2_version()
Prototype.i Prototype_o2_view(src.p-bstr)


Global o2_abst.Prototype_o2_abst
Global o2_asmo.Prototype_o2_asmo
Global o2_basic.Prototype_o2_basic
Global o2_buf.Prototype_o2_buf
Global o2_errno.Prototype_o2_errno
Global o2_error.Prototype_o2_error
Global o2_exec.Prototype_o2_exec
Global o2_len.Prototype_o2_len
Global o2_lib.Prototype_o2_lib
Global o2_link.Prototype_o2_link
Global o2_mode.Prototype_o2_mode
Global o2_pathcall.Prototype_o2_pathcall
Global o2_prep.Prototype_o2_prep
Global o2_varcall.Prototype_o2_varcall
Global o2_version.Prototype_o2_version
Global o2_view.Prototype_o2_view

Global LibOxygen.i

Procedure.i Oxygen_Init()
  
  If Not IsLibrary(LibOxygen)
    CompilerIf #PB_Compiler_32Bit
      LibOxygen = OpenLibrary(#PB_Any, "oxygen.dll")
    CompilerElse
      LibOxygen = OpenLibrary(#PB_Any, "oxygen64.dll")
    CompilerEndIf
    If IsLibrary(LibOxygen)
      o2_basic = GetFunction(LibOxygen, "o2_basic")
      o2_errno = GetFunction(LibOxygen, "o2_errno")
      o2_error = GetFunction(LibOxygen, "o2_error")
      o2_exec = GetFunction(LibOxygen, "o2_exec")
      o2_mode = GetFunction(LibOxygen, "o2_mode")
      o2_version = GetFunction(LibOxygen, "o2_version")
      o2_view = GetFunction(LibOxygen, "o2_view")
    EndIf
  EndIf
  
  ProcedureReturn LibOxygen
  
EndProcedure


CompilerIf #PB_Compiler_IsMainFile
  
  Define sCode.s, sResult.s
  
  If Oxygen_Init()
    
    o2_mode(10)
    
    Debug PeekS(o2_version(), -1, #PB_Unicode)
     
    sCode = "print 10+20"
    sResult = PeekS(o2_view(sCode), -1, #PB_Unicode)
    MessageRequester("", sResult)
    
    o2_basic(sCode)
    If o2_errno()
      Debug PeekS(o2_error(), -1, #PB_Unicode)
    Else
      o2_exec(0)
      If o2_errno()
        Debug PeekS(o2_error(), -1, #PB_Unicode)
      EndIf
    EndIf
    
  EndIf
  
CompilerEndIf
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by Olli »

When you don't just drink water, the "global" ones hurt...

(french translation : << Mutin... Les global, ça fait mal... >>
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by Olli »

)
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Making an OxygenBasic compiler with PureBasic

Post by infratec »

Optimized and bug fixed (thanks to PeDe) my listing above.
No longer PeekS() required :wink:
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by Mijikai »

Very interesting, thanks for sharing.

Lazy loading:

Code: Select all

EnableExplicit

Global o2_dll.i = OpenLibrary(#PB_Any,"oxygen64.dll")

Macro o2_Function0(_name_,_entry_)
  Prototype.i _name_():Global.i _name_._name_ = GetFunctionEntry(o2_dll,_entry_)
EndMacro

Macro o2_Function1(_name_,_param_,_entry_)
  Prototype.i _name_(_param_):Global.i _name_._name_ = GetFunctionEntry(o2_dll,_entry_)
EndMacro

Macro o2_String(_ptr_)
  PeekS(_ptr_)
EndMacro

o2_Function1(o2_abst,Input.p-bstr,1)
o2_Function1(o2_basic,Input.p-bstr,2)
o2_Function1(o2_buf,Input.i,3)
o2_Function0(o2_compile,4)
o2_Function0(o2_errno,5)
o2_Function0(o2_error,6)
o2_Function1(o2_exec,Input.i,7)
o2_Function0(o2_len,8)
o2_Function0(o2_lib,9)
o2_Function1(o2_link,Input.p-bstr,10)
o2_Function1(o2_mode,Input.i,11)
o2_Function1(o2_pathcall,Input.i,12)
o2_Function1(o2_prep,Input.p-bstr,13)
o2_Function0(o2_stats,14)
o2_Function1(o2_varcall,Input.i,15)
o2_Function0(o2_version,16)
o2_Function1(o2_view,Input.p-bstr,17)

#o2_mode_asci_0 = 0
#o2_mode_asci_1 = 1
#o2_mode_unicode = 2
#o2_mode_ole_bstring_ascii_1 = 8
#o2_mode_ole_bstring_ascii_2 = 9
#o2_mode_ole_bstring_unicode = 10

o2_mode(#o2_mode_ole_bstring_unicode)

Debug o2_String(o2_version())
Debug o2_String(o2_view("print 10 + 20"))
Debug o2_errno()
Debug o2_String(o2_error())

End
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by JoeC4281 »

Thankyou all for your code examples on how to use OxygenBasic with PureBasic.

I'm still learning about how it works.

In reference to http://forum.it-berater.org/index.php/t ... l#msg17963 it explains about sharing variables directly between a PowerBasic host program and OxygenBasic at run time, in memory, not using a DLL.

In reference to https://help.thinbasic.com/oxygen.htm it tells of interacting directly with ThinBasic variables, including User-Defined Types, or it can return values using Subs and Functions like an external DLL.

Here is a simple example of how I can run Oxygen code from ThinBasic,

Code: Select all

#BUNDLE Type Console

#Region "Modules"
Uses "Console"
Uses "Oxygen"
#EndRegion

dim bday as Long

bday = 1964

o2_run "
overlay long bday
@bday=#bday
bday = bday - 2
"

' Displays 1962
Printl bday
Note that I create the bday ThinBasic variable,
pass it as part of the Oxygen script,
process the variable,
which then can be used by ThinBasic.

The author talks of the o2_run command, which I cannot find reference to, other than;
https://www.thinbasic.com/community/sho ... #post96311

As the author of OxygenBasic has stated;
Ref: http://forum.it-berater.org/index.php/t ... l#msg26664
OxygenBasic has no licensing restrictions, but also no liability.

You are welcome to use it in a commercial application with closed source as long as your software is not harmful to its users.
I was hoping that there is a way to do the same thing with PureBasic, that is, delcare variables in PureBasic, so that they can be processed by OxygenBasic, and then returned to PureBasic for further use.

Many thanks,

Joe
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by Mijikai »

This works:

Code: Select all

Macro o2_Run(_input_)
  o2_exec(o2_basic(_input_))
EndMacro

Procedure.i Main()
  Protected bday.l
  bday = 1964 
  o2_Run("overlay long bday" + #CR$ + "@bday =" + Str(@bday) + #CR$ + "bday = bday - 2")
  Debug bday
  ProcedureReturn #Null
EndProcedure 
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: Making an OxygenBasic compiler with PureBasic

Post by JoeC4281 »

Mijikai, it does indeed work.

Many Thanks!

Joe
Post Reply