各程式語言 + aardio 相互呼叫範例

2022-09-24 18:00:15

程式碼都很簡單,複製可直接執行。aardio 快速呼叫 C,C++,C#,Java,R,V,Python,JavaScript,Node.js,Rust,PHP,Ruby,PowerShell,Fortran,Delphi,Julia,Nim,批次處理 …… 演示。

 

aardio 直接呼叫系統 API 函數( 支援回撥、相互呼叫 ):

var ret,point = ::User32.GetCursorPos({
  int x;
  int y;
})

aardio / JavaScript 相互呼叫( 支援 ES6 ):

import web.script;
var vm = web.script("ES6")

//匯出 aardio 函數到 JavaScript
vm.external = {
    log = function(...){  
        console.log(...)
    } 
}

vm.script = /*****
function TestFunction(a,b) { 
    return a + b;
}
*****/

var ret = vm.script.TestFunction(2,3);

在 aardio 中呼叫 Python( Python 呼叫 aardio 同樣簡單,參考 aardio 自帶範例),簡單得就像在 Python 中使用 Python:

import console;
import py3; 

//匯入 Python 模組。
var itertools = py3.import("itertools")

//呼叫 Python 函數,支援純 aardio 型別引數
var permutations = itertools.permutations({"a","b","c"});

//呼叫 Python 內建函數 list
var pyList = py3.list(permutations);

//遍歷 Python 物件成員
for( item in pyList.each() ){
  console.log(item); //像 aardio 物件一樣使用 Python 物件
}

console.pause();

aardio 直接呼叫 .Net / C# 元件( 支援相互呼叫,支援委託這些)

import win.ui; 
var winform = win.form(text="DataGridView")

import System.Data;
var tab = System.Data.DataTable("DT"); 
tab.Columns.Add("名稱"); 
tab.Rows.Add({"WangWu"}); 

import System.Windows.Forms;
var grid = System.Windows.Forms.CreateEmbed("DataGridView",winform); 
grid.ColumnHeadersHeightSizeMode = 2; 
grid.DataSource = System.Data.DataView(tab);
   
winform.show();
win.loopMessage();

aardio 直接呼叫 Java ( 也支援相互呼叫 )

import java; 
var jvm = java(); 

//載入Java類物件
HelloworldApp = jvm.import("aardio.sample.HelloworldApp");

//也可以如下自記憶體或檔案直接載入類,
HelloworldApp = jvm.import("aardio.sample.HelloworldApp",$"\java\aardio\sample\HelloworldApp.class");

//用 Java 類建立 Java 物件
var helloworld = HelloworldApp();

//直接呼叫 Java 物件的方法 
var result = helloworld.test(3); 

用 aardio 編譯 C 語言程式碼生成 DLL 執行檔案,再呼叫 DLL 中的 C 函數:

import tcc;  
//編譯 DLL
tcc.build( "/start.dll" ).code = /***
#include <windows.h> 
__declspec(dllexport) int Add( int a,int b ) 
{     
  return a + b;
} 
***/

//載入 DLL
var dll = raw.loadDll( "/start.dll",,"cdecl" );
//呼叫 C函數
var result = dll.Add(12,3);

在 aardio 中嵌入並呼叫批次處理:

import console
import process.batch;

//批次處理 for 遍歷並拆分字串
var bat = process.batch(`
@echo off 
for %%i in (abc,def,xyz) do echo %%i
`)
console.log(bat.read(-1))
 
console.pause()

aardio 呼叫 Rust 語言解析 TOML:

import console; 
import string.toml;

var str = string.toml.stringify({abc=123,d={1,2,3}});
console.log( str );

import process.code;
process.code("~\lib\string\toml\.res");
console.pause(true);

在 aardio 裡嵌入 PHP,以下短短几句程式碼,包含了 HTTP 伺服器,PHP伺服器端,嵌入的瀏覽器元件:

import win.ui;
/*DSG{{*/
var winform = win.form(text="Hello World / PHP_CGI 伺服器")
/*}}*/

var code = /*
<html>
<head> 
<meta charset="utf-8">
<title>PHP 測試</title>
</head>
<body>
<?php echo '<p>Hello World / PHP_CGI 伺服器</p>'; ?>
</body>
</html>
*/
string.save("/test.php",code);

import php.simpleHttpServer;
var url = php.simpleHttpServer.startUrl("/test.php");

import web.form;
var wb = web.form(winform);
wb.go(url);

winform.show();
win.loopMessage();

aardio 呼叫 PowerShell,並且在 PowerShell 中呼叫 aardio。這甚至都不用帶上體積較大的
System.Management.Automation.dll,一個輕巧的 EXE 就可以搞定一切,向下相容到 PowerShell 2.0 :

import dotNet.ps;
 
var pScript = /*

# 宣告 PowerShell 引數
param($win)  

# 修改 aardio 物件屬性
$win.title = "PowerShell + aardio";

# 呼叫 aardio 物件函數
$win.msgbox("這是 PowerShell 呼叫 aardio 開啟的對話方塊。")  
*/

import win;
dotNet.ps(pScript,{ 
  win = win; //# 將 aardio 物件作為引數傳給 PowerShell
});

aardio 執行 Ruby 語言程式碼 :

import win.ui;
/*DSG{{*/
var winform = win.form(text="執行Ruby程式碼")
winform.add(
edit={cls="edit";left=26;top=16;right=737;bottom=435;multiline=1;z=1}
)
/*}}*/

import process.ruby;
var out = process.ruby.exec("puts '測試UTF-8'")
winform.edit.print(out);

var out = process.ruby.eval(`[1, 2, { name: "tanaka", age: 19 }]`)
winform.edit.print(out);

winform.show();
win.loopMessage();

aardio 呼叫 Node.js :

import console;  
import nodeJs;

var js = /******

console.log(process.argv);

var startEnviron = require('startEnviron');
console.log(startEnviron.dest);

******/

//自動分析 JS 程式碼中的 require 語句並安裝依賴模組
nodeJs.requireByJs(js);

//把物件傳給 node.js,在 JS 程式碼中用 require('startEnviron') 獲取。
nodeJs.startEnviron({
    src:"傳個字串",dest:{test:"巢狀的物件表,傳給node.js都沒問題",number:123, arr:{1,2,3} }
})

//執行JS,這裡指定的啟動引數在 JS 程式碼中可用 process.argv 獲取。
var prcs = nodeJs.exec(js,"--args1=1","--args2=1");
prcs.logResponse();

console.pause(true);

aardio 呼叫 Fortran ( DLL 原始碼在 aardio 範例裡有 ) :

import console

//載入 DLL , DLL 路徑前加 $ 實現記憶體載入 DLL(釋出後不需要外部 DLL 檔案)
var dll = raw.loadDll($"/fortran.dll",,"cdecl");

//不宣告直接呼叫,結構體預設傳址,這不用改什麼。
var c = dll.__test_MOD_addbypoint({
    int x = 22;
    int y = 3;
})
console.log(c);


//可以先宣告一下,引數型別加上&宣告為按參照傳址(指標)
var add = dll.api("__test_MOD_add","int(int &a,int &b)")
var c = add(33,2); //Fortran 的數值引數預設都是傳址(傳指標)

//不宣告直接呼叫可以用結構體取代指標
var c = dll.__test_MOD_add({int a=33},{int b=2});

//用 raw.int 建立傳址數值也可以
var c = dll.__test_MOD_add(raw.int(33,true),raw.int(2,true));

//引數宣告為傳值時呼叫更簡單,不宣告呼叫時數值預設為 int 型別
var c = dll.__test_MOD_addbyval(33,2,raw.double(123));
console.log(c);

//字串
var str = "hello"; //唯讀字串,改用 raw.buffer 建立可讀寫位元組陣列
dll.__test_MOD_hello(str,#str); //注意到字串長度傳過去

console.pause(true);

aardio 呼叫 C++ :

import vc6;
import console; 
console.open();

var vc = vc6( "/" )  

//輸入C++原始碼
vc.cpp = /****** 
    #include <windows.h> 
      
    struct TestInfo{
        int x;
        int y;
        BYTE name[256];
    };
    
    class CTestObject
    {
    public: 
        //注意函數宣告前加上 virtual 以支援 aardio 中的 raw.interface
        virtual    void getName(char *buffer,int len); 
        virtual    void getInfo(TestInfo *pInfo);
    };
    
    void CTestObject::getName(char *buffer,int len){
        strcpy(buffer,"測試");
    }
    
    void CTestObject::getInfo(TestInfo *pInfo){
        pInfo->x = 1;
        pInfo->y = 2;
        strcpy((char *)pInfo->name,"測試");
    }
    
    extern "C" __declspec(dllexport) CTestObject* __cdecl CreateTestObject() { 
        return new CTestObject();
    }
    
    extern "C" __declspec(dllexport) void __cdecl DeleteTestObject( CTestObject* pTest) {
        delete pTest;
    }
******/

//編譯生成DLL 
vc.exec(
    'cl *.cpp'
    ,'/W3' /*警告等級*/
    ,'/MD' /*使用多執行緒動態執行庫*/
    ,'/O2 /Ot /EHsc' /*程式碼優化選項*/
    ,'/D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL"' /*定義常數和宏*/
    ,'/I"./INCLUDE"'/*指定標頭檔案目錄*/
    ,'kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib' /*匯入庫*/
    ,'/link /SUBSYSTEM:WINDOWS /MACHINE:X86' /*後面是連結引數 */
    ,'/out:test.dll'/*輸出檔名*/
    ,'/dll' /*輸出DLL*/ 
    ,'/LIBPATH:".\LIB" /LIBPATH:".\LIB2"' /*指定庫目錄*/
)

var dll = raw.loadDll("/test.dll",,"cdecl");

import raw.interface;
class testObject{
    ctor(){
        //建立 C++ 物件,並獲取指標,注意這裡使用了 P 尾標獲取指標。
        var pTest = dll.CreateTestObjectP();
        
        //C++ 物件指標轉換為 aardio 物件。
        this = ..raw.interface( pTest,"
            void getName(string &buffer,int len);
            void getInfo(struct &pInfo); 
            ","thiscall" //注意呼叫約定為thiscall
        )
        
        //新增解構函式    
        ..table.gc(this,"delete")    
    };
    delete = function(){
        if(!owner.deleted){
            dll.DeleteTestObject( owner );
            owner.deleted = true;
        } 
    };
}

//建立物件
var obj = testObject();

//呼叫 C++ 函數
var name = obj.getName(25,25);
console.log(name);

//呼叫 C++ 函數
var info = obj.getInfo({ int x;int y;BYTE name[256]})
console.log( info.name  );

console.pause();

aardio 呼叫 FreeBASIC ( DLL 原始碼在 aardio 範例裡有 ) :

//載入DLL,DLL路徑前面加上$表示把DLL嵌入到程式中並通過記憶體載入
var dll = raw.loadDll( 
    $"\basic.dll",,"cdecl" //注意引數裡指定使用 cdecl 呼叫約定。
);

//定義結構體,當然也可以先宣告一個 class 來建立範例。
var info = {
    int x;
    INT y;
}

// 然後直接呼叫 API
var ret = dll.msgboxW(123,456,"測試一下好用不好用",info);


//最後列印結構體看一下值
import console;
console.log(ret);

console.dumpJson(info);
console.pause();
aardio 呼叫 Delphi ( DLL 原始碼在 aardio 範例裡有) :

import win.ui;

//記憶體載入 DLL
var delphiDll = raw.loadDll($"\Project1.dll");
class win.ui.ctrl.delphiForm{
    ctor(parent,tParam){
        this.hwnd = delphiDll.CreateForm(parent.hwnd);
    };
    @..win.ui.ctrl.metaProperty()
}
/*DSG{{*/
var winform = win.form(text="用 Delphi 語言為 aardio 編寫控制元件";right=507;bottom=423;bgcolor=11842740)
winform.add(
custom={cls="delphiForm";text="嵌入 Delphi 控制元件";left=17;top=28;right=490;bottom=211;db=1;dl=1;dr=1;dt=1;z=1};
edit={cls="edit";text="請先用 Delphi 開啟此目錄下的 DLL 原始碼工程編譯生成 \Project1.dll";left=16;top=228;right=489;bottom=398;edge=1;multiline=1;z=2}
)
/*}}*/

import web.json;
winform.onTest = function(delphiStructParam){ 
    winform.edit.print("Delphi 呼叫了aardio 函數,引數如下:");
    winform.edit.print(delphiStructParam);
    delphiStructParam.x = 90;

    //可選返回修改後的結構體
    return delphiStructParam;
}
winform.edit.text = "";

winform.show();
win.loopMessage();

aardio 呼叫 R 語言:

import console; 
import process.r;

//執行 R 程式碼,支援 aardio 模板語法
process.r.code = /*
write("<?="這是 aardio 程式碼"?>",file=".data.txt");
*/

//執行 R 程式碼,支援 aardio 模板語法
var out = process.r.loadcode(`write("<?="這是 aardio 程式碼"?>",file=".data.txt");`)
 
//執行純 R 程式碼,引數 @1 可以指定 R程式碼或 R 檔案。
var out = process.r.exec(`
args=commandArgs(T);
write(args[1],file=".data.txt");

# list 有點像 aardio 中的表(table),可以包含各種資料型別, 
a <- list(hello = 1, world = "字串" ) # <- 相當於 aardio 中的等號,  R的等號一般用於分隔鍵值對
print ( a[["world"]] ); # aardio 裡的直接下標也是這麼寫
print ( a$world ); # 相當於 aardio 裡的  a.world
print ( a[1] ); # 這個返回的是鍵值對 hello = 1,不像 aadio 中 a[1] 與 a.hello 是指向不同的元素。
print ( mode(a[1]) ); # 資料型別還是顯示為 list

b <- TRUE #布林值必須全大寫
print( b ) 

# 向量
a = c(10, 20, 30, 40, 50)
print( a[1] ) #起始下標為 1 ,這跟 aardio 一樣
print( a[1:4] ) # 取出第 1 項到第 4 項

# 定義函數,與 aardio 語法類似
new.function <- function(a,b,c) {
   result <- a * b + c # 類似 aardio 中的 return a * b + c #
   print(result) # 指定返回值以後,還能繼續執行後面的程式碼,不像 aardio 函數 return 後面的程式碼被忽略。
}

print( new.function(2,3,1) )
`,"測試一下"); //可以新增不定個數的啟動引數
console.log( out );
console.more(,true);

console.showLoading(" 正在安裝 rjson 包");
process.r.require("rjson","https://mirrors.ustc.edu.cn/CRAN/");//不會重複安裝

var out  = process.r.exec( `
library("rjson") # 載入 rjson 包

args <- commandArgs(T);
tab <- fromJSON(args[1], simplify=FALSE);

#不要用 print ,cat 不會加一堆不必要的東西
cat( toJSON(tab) )
`, {
  name1 = "測試一下,傳物件給 R 語言";
  name2 = "這是一個 aardio 物件"
})

console.dump(out);

var rCode = /*
testabc <- function(a,b,c) {
   result <- a * b + c # 類似 aardio 中的 return a * b + c #
   print(result) # 指定返回值以後,還能繼續執行後面的程式碼,不像 aardio 函數 return 後面的程式碼被忽略。
}
*/

//啟動 R 
var r = process.r.startRpc(rCode);
 
//呼叫 R 函數
var ret  = r.testabc(2,3,1)

//列印 R 函數返回值
if(ret[["result"]]){
    console.log("R 函數返回值",ret[["result"]])
} 
console.pause(true);

aardio 呼叫 Julia :

import console;
import julia;

//呼叫 Julia 函數
var ret = julia.sqrt(2);
console.log(ret);

//匯入 Julia 模組
julia.using("Base64");
var data = julia.Base64.base64encode("測試一下");
console.log( data );

//轉換 Julia 資料型別
var buf = julia.value.build(raw.buffer("abc"));
console.log(julia.typeof(buf));

//執行 Julia 程式碼並獲取返回值
var refs = julia.eval("refs = IdDict(");

//檢視 Julia 程式碼錯誤
console.log(julia.lasterr());

console.pause();

aardio 呼叫 Nim 語言:

import console;
 
var nimCode = /*
 {.pragma: rtl, exportc, dynlib, cdecl.}
 
import md5
 
# Nim 雙引號中的字串,相當於 aardio 中用單引號包含的跳脫字串
# aardio 中雙引號包含的字串,相當於 Nim 中的原始字串: r"原始字串" 
# Nim 與 aardio 都是 UTF-8 編碼,aardio 的文字字串在 Nim 中的型別為 cstring
# Nim 中 string 可以隱式轉換為 cstring, cstring 加上 $ 轉為 string 型別
proc build*(str: cstring, num: ptr[cint]): cstring {.rtl.} =
    num[] =  num[] * 2
    result =  md5.getMD5($str)
*/
string.save("/test.nim",nimCode )

import process.nim; 
process.nim("c --app:lib -d:release -r test.nim")

//支援改為 $"/test.dll" 記憶體載入 DLL,
//但這時候 test.dll 還未生成,所以範例裡沒有加 $
//用cdecl 呼叫約定的好處是:匯出函數名直接可用,不會被加上修飾名
var test = raw.loadDll("/test.dll",,"cdecl")

//nim 與 aardio 的字串都是 UTF-8 編碼,UTF-8 真是到處通行,非常方便省了很多事
var build  = test.api("build","str(str,int& num)" )

//一般C語言不能這麼直接返回字串(要考慮誰釋放記憶體)。
//但是 nim 可以投機取巧一下,nim 會自動回收記憶體,而這時候還來不及回收。
var str,num = build("測試abc",9)
console.log(str,num)

//用 aardio 算出 MD5 對比一下,結果一模一樣
import crypt;
console.log(crypt.md5("測試abc",false))
console.pause();

aardio 呼叫 V 語言:

import console; 
import process.v;

console.open();

//V語言不支援中文路徑,所以工程目錄路徑不要包含任何中文
string.save("/hello.v","
struct Point {
pub mut: //宣告下面的欄位公開、可變
    x int
    y int 
} 

[export: 'add'] //一定要用這句指定DLL匯出函數名
pub fn add(a int,b int,mut pt &Point) int {
   pt.x = a+b
   return a+b
}" )

/*
V語言是翻譯成C語言然後生成DLL,生成的DLL依賴 VC 執行庫,
試了換成呼叫TCC編譯,10KB的DLL增大到 400KB,並且執行崩潰。
*/
process.v.shared("hello.v").waitOne();

//V生成的DLL建議至少在 WIN10 上用,需要VC++2017執行庫 
import sys.vc14;
sys.vc14.require(); //檢測並自動安裝 VC++ 執行庫

//匯入DLL,注意要指定 cdecl 呼叫約定
var dll = raw.loadDll("/hello.dll",,"cdecl")

//呼叫 V 函數( V是翻譯為C語言,所以參考C語言的規則)
var n,pt = dll.add(12,3,{int x=1;int y =2});

//輸出結果
console.log(n);
console.dumpJson(pt);

console.pause(true);