天天好味道

没钱没权没户口,靠走靠吼靠小狗
随笔 - 66, 文章 - 1, 评论 - 524, 引用 - 5

导航

<2006年7月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

留言簿(12)

随笔分类

随笔档案

文章档案

我的链接

搜索

最新评论

阅读排行榜

评论排行榜

再次崇拜C

今天down了wxBasic下来(被逼无奈,Basic的语法总是熟悉的人多多吧?),发现基本上也是纯C的。(Linkman的笔记里已经说过)
用VC6编译,哗一下就好了,只需要编译一个core.c,其他.c都被#include进去了,除了10多个warning,什么毛病没有。
改用EVC编译,出来几个链接错误,什么localtime呀的,这个我熟,所有想要往CE上移植的程序都缺这个,而Lua的CE版本
已经把这些函数都实现好了,复制过来,稍微改改(主要是把main入口改成WinMain),哗,又好了,哈哈!

运行测试程序的时候有点小问题,就是fputs的调用判断是错的,hack了一下,:P

接下来就要看看怎么让wxBasic支持调用动态链接库的函数了。语法我还没想好:
是像python那样,增加一个关键字:
import ModemLib
ModemLib.Dial(“01088881111“)
还是不改动Basic语法,用函数代替:
LoadLib ”ModemLib.dll”
ExtCall “ModemLib.dll”,”Dial”,”01088881111”

前者更方便些。

大伙给点建议

posted on 2006-07-18 17:59 jzhang 阅读(2492) 评论(16)  编辑 收藏

评论

# re: 再次崇拜C

看了一下它的文档有一个叫"The wxWindows Wrappers"的东西, 不需要自己去搞一套的吧.
按它的格式弄好后就可以直接在wxBasic里var=new ModemLib
2006-07-18 19:52 | hpho

# re: 再次崇拜C

没有搞过你说的这个。但是我使用过一种脚本NSIS是设个语法:
CallInstDLL $DIR\YourDll.dll YourFunc 呵呵,我觉得这样调用也挺好。很简练。不过参数和返回值有些麻烦。参数它使用堆栈,需要一个一个push,返回值是自定义的几个全局变量。
2006-07-19 08:23 | 晓寒

# btw

忘了问了。你的这种脚本如何传参数和得到返回值?
2006-07-19 08:24 | 晓寒

# 谢谢hpho

如果是这样那太好了,我去看看。 从他的代码里看,好像是要静态链接进去的。
2006-07-19 08:40 | jzhang

# to 晓寒

wxBasic我还没有看他的wrapper.不过Python,Lua,Tcl等语言都是类似的办法:
import ModemLib
ModemLib.dial("123456")

在Dll里的C代码可能是这样的(示例):

int dial(PyArg* arg)
{
if(arg->getNum() != 1)
{
PyError("Invalid arg number!");
return 0;
}
char* phonenum = arg->getString();
.....干活
PyIntResult(0);
return 1;
}

PyError,PyIntResult,PyArg都是 Python提供的函数和类.
Python引擎在调用dial前,会把PyArg准备好. TCL类似.
而Lua的实现比较野蛮,直接把脚本引擎用的堆栈当作参数
传给dial函数,自己pop和push,不过也有人做了封装.

2006-07-19 08:59 | jzhang

# re: 再次崇拜C

你看看这个文档吧
http://wxbasic.sourceforge.net/get_it.php里的
http://prdownloads.sourceforge.net/wxbasic/wxbasic.pdf
2006-07-19 09:35 | hpho

# to hpho

看了,我也看了wrap.c,这是wxbasic提供gtk扩展的办法。
但是这是静态的,需要实现链接到解释器里去。我需要可以动态扩充的办法,通过用脚本load一个dll来实现扩充功能。
2006-07-19 09:40 | jzhang

# re: 再次崇拜C

其实并不赞成增加语法, 但可以加在注释里
' import ModemLib
var = new ModemLib
这样即不会影响原有的解释器的解释也不用花多大力气去修改原代码.
2006-07-19 09:41 | hpho

# 加载注释里?

那一样要修改解释器去解释这种特别的注释阿。
2006-07-19 10:28 | jzhang

# re: 再次崇拜C

是呀, 但不会打扰原来的解释.
我想最简单的方法是用wrap.c作一个
class Method{ //类似于java.reflect里的那个东西
public:
     Method(string name);
    setArgv();
};

class Result{};

class DynamicInvoke{
public:
     attach(string dllname);
     detach();
     Result invoke(Method m);
};

相对的basic调用:
const metDial=new Method("dial")
const DI = new DynamicInvoke("ModemLib")
metDial.setArgv(....);
ret = new Result
ret=call DI.invoke( metDial)
利用静态搭桥到动态库上去.
2006-07-19 10:44 | hpho

# 这个用法太复杂了

呵呵,不过思路很好,谢谢!也许可以这样
extcall = new ExtCall("ModemLib")
extcall.Call("dial",argv...)
这个参数要用Variant数组来表示。
2006-07-19 11:42 | jzhang

# re: 再次崇拜C

我有一个想法 不用增加新的函数 修改一下解释器的框架 让它在初始化的时候去扫描一个目录 运行里边的所有dll/so文件的init函数(如果有的话) 同时开放两个函数register_func和unregister_func 每个dll可以利用这两个函数动态的把自己实现的函数注册到全局符号表里
例如有这样一个dll文件 c源文件是
int dial(PyArg* arg) 
{
    ....
    return 0;
}

int init(struct env *env)
{
    env->register_func("dial", dial);
    return 0;
}

void dinit(struct env *env)
{
    env->unregister_func("dial");
    return 0;
}
大概就是这么个意思 
最好把func定义成结构体 
struct func {
    char *name;
    union {
         int(func0)(PyArg* ret);
         int(func1)(PyArg* ret, PyArg* arg0);
         int(func1)(PyArg* ret, PyArg* arg0, PyArg *arg1);
         ...
    } func;
}
这样register_func/unregister_func就只需要一个参数了
2006-07-23 22:00 | TripleX

# re: 再次崇拜C

其实做成这样就和提供一个LoadDll函数差不多了 LoadDll只不过是在运行期去dlopen和运行dll里的init函数而已 
2006-07-23 22:26 | TripleX

# To TripleX

Python,Tcl,Lua基本也是这么个设计,只不过可以动态的装载。 如果要在启动前就搜索dll的话,用户用起来不是特别方便。 不过我在想我可以把脚本执行两遍,第一遍执行import lib这样的指令,获得所有的函数信息,第二遍重新初始化脚本引擎,加入这些函数信息。:)
2006-07-24 12:35 | jzhang

# wxBasic的作者给我回复了他的方案

Hi, Jason.

> One problem is that I don't find the way to change the above syntax to:
>
> import ModemLib
> ModemLib.Dial("1076767677")
>
> I think it's better and clearer. Could you give me some hint?

I'd set up a LIBRARY object as a new type of object, creating it in the lookup
table in SYMBOL.H as W_SYM_LIBRARY, and in TOKEN.H as W_TOKEN_LIBRARY_NAME.

In the preparsing stage, tokens like "ModemLib" would be added to the symbol
table as W_SYMBOL_LIBRARY symbols. You probably should have a different sort
of keyword than IMPORT. I think LIBRARY or LINK would probably be a better
name.

In LEX.C, change identifyWord() to return a W_TOKEN_LIBRARY_NAME when it sees
a symbol of W_SYM_LIBRARY.

The way wxBasic handles "dotted" expressions is a bit of a hack. In LEX.C,
check out wLexToken. If the token begins with a non-numeric character and
contains a '.', it then checks to see if there's a open parenthesis. If there
is, the lexer reports it as a W_TOKEN_METHOD_NAME. It's too complex for the
lexer to make a determination past that point, so it passes it back to the
parser to figure things out. In PARSE.C, check out the routine
wParseDotMethod, which handles the parsing of a token identified as a
W_TOKEN_METHOD_NAME.

Since the lexer will report the symbol as being of type W_TOKEN_LIBRARY_NAME,
add some logic to check for that:

if (wParseTokenIs(W_TOKEN_LIBRARY_NAME)
// YOUR CODE HERE

/* known variable typecast to a class? */
else if (wParseTokenIs(W_TOKEN_VARIABLE_NAME)
&& wTheToken.symbol->typeCast < 0) {
/* code holder */
code = wCodeNew();

You've got two options for generating code: either create a hook via a builtin
routine such as:

callLibrary( "ModemLib", "Dial", "1076767677")

or create a new CallLibrary token for the virtual machine. Let's assume you
create a builtin routine, since it's easier.

I assume you can figure out how to add your own builtin routines; the code in
BUILTIN.H and BUILTIN.C is pretty self-explanatory. Now you need to generate
the appropriate code so the builtin routine gets called properly. Take a look
at wParseStatementRoutineName() to see how that's done. Basically, the core
routine is:

wCodeAppend( code, wParseExpression( 0 ) );

which parses the arguments. You know the name of the library from the symbol
names, and you can use wCodeEmit() to emit their names as arguments. Then
generate the call with:

wCodeEmitCall( code, s, argCount, returnCount );

which generates the call to the builtin routine. You can find your CallLibrary
routine in the symbol table by using the routine:

wSymbol *wSymbolFind( char *name, int scope )

Since the builtin routine is global in scope, it would look something like:

wSymbol s = wSymbolFind( "calllibrary", wTheGlobalScope );

The end result should be to generate code that's equivalent to:

callLibrary( "ModemLib", "Dial", "1076767677")

which would be something along the lines of:

pushString "modemlib"
pushString "dial"
pushString "1076767677"
pushNumber 3
call "callLibrary"

Did that make sense?

> I have ported your engine to WinCE, a popular embeded OS. I think wxBasic
> is very useful for this OS, since there is no good script engine on it.

Cool. Isn't Lua available for CE?

Did that answer your questions?
2006-07-25 16:06 | jzhang

# 老板同意用Lua了

他说他在加拿大参加一个展览,发现Lua在通信行业很流行了。太棒了。不过我还是会找时间把wxBasic 改成支持调用dll里的函数的。
2006-07-31 10:39 | jzhang
标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]