一个简明的编译器
多次看到有人提起文本表达式的计算问题,就动手整理以前的代码并加上注释。
写一个简单的编译器并不是很复杂的,当中要用到些反射的知识。自已觉得,反射的使用在net中真是无处不在,使用反射没什么效率不效率的问题,毕竟现在的电脑配置并不是很低。适当使用反射,或者通过使用反射本身,会使自己加深对net的理解。以后会写些运用反射增加代码灵活性的小”文章”供初学者参考。
如果只是计算表达式的值的,当然用不了那么多的代码.这样写法,只是使它通用性强些.
以下的我直接贴代码了,不再说些什么(可以说如何如何臭,只是不许骂人)。
imports system.reflection
imports system.codedom
imports system.codedom.compiler
public class sourcecomp
//编译器接口
private m_compiler as icodecompiler
//编译器参数
private m_compilerparameters as compilerparameters
//引用的程序集
private m_refassemblies as string() = {"system.dll", "system.data.dll"}
//源代码
private m_source as string = ""
//记录是否是默认的源代码
private m_is_default as boolean = true
//记录编译状态
private m_compiled as boolean = false
//编译生成的程序集
private m_assembly as system.reflection.assembly
//默认源代码生成的实例
private m_tmpclass as object
//默认源代码生成的实例函数
private m_methodinfo as system.reflection.methodinfo
//默认源代码函数的表达式参数
private m_expression as string
//返回程序集
public readonly property cpassembly() as system.reflection.assembly
get
return me.m_assembly
end get
end property
sub new()
//获取vb编译器实例
me.m_compiler = new vbcodeprovider().createcompiler
//初始编译器参数
me.m_compilerparameters = new compilerparameters
with me.m_compilerparameters
.generateexecutable = false //false值指定编译为类集,true编译为可执行程序
.generateinmemory = false //只在内存中生成程序集,不输出到磁盘
//添加默认的程序集
me.add_compilerparameters()
end with
end sub
//添加要引用的程序集
private sub add_compilerparameters()
me.m_compilerparameters.referencedassemblies.addrange(me.m_refassemblies)
end sub
//添加指定的引用程序集
public sub add_compilerparameters(byval refassemblies as string())
me.m_refassemblies = refassemblies
me.m_compilerparameters.referencedassemblies.clear() //清除原有的程序集,重复引用编译会产生异常
me.add_compilerparameters()
end sub
//生成默认的源代码
//类名:tmpclass
//函数名:getexpressionvalue ,参数:expression ,参数类型:字符串
//主要功能:返回表达式expression的值 ,返回值类型:object
private sub builddefaultsource()
dim mcodebuilder as codebuilder = new codebuilder
with mcodebuilder
.appendcode("imports system")
.appendcode("imports system.data")
.appendcode("imports system.math")
.appendcode("imports microsoft.visualbasic")
.appendcode()
.appendcode("public class tmpclass")
.appendcode(" public function getexpressionvalue() as object")
.appendcode(" dim result as object")
.appendcode(" result={0}") 这里传入表达式
.appendcode(" return result")
.appendcode(" end function")
.appendcode("end class")
end with
me.m_source = mcodebuilder.tostring
end sub
//指定源代码
public sub setsource(byval source as string)
me.m_source = source
me.m_compiled = false
me.m_is_default = false
end sub
//从指定文件中读取源代码
public sub getsourceformfile(byval sourcefilename as string)
dim mcodebuilder as codebuilder = new codebuilder
mcodebuilder.appendfromfile(sourcefilename)
me.m_source = mcodebuilder.tostring
me.m_compiled = false
me.m_is_default = false
end sub
//编译
public sub complile()
if me.m_source = "" then
me.builddefaultsource()
end if
if me.m_is_default then
传入参数
me.m_source = string.format(me.m_source, me.m_expression)
end if
dim mcompresult as compilerresults = me.m_compiler.compileassemblyfromsource(me.m_compilerparameters, me.m_source)
//错误提示
if (mcompresult.errors.haserrors) then
dim errormessage as string
errormessage = "编译错误:" & vbcrlf
dim err as compilererror
for each err in mcompresult.errors
errormessage = errormessage & err.errortext & vbcrlf
next
throw new exception("编译错误: " + errormessage)
end if
me.m_assembly = mcompresult.compiledassembly
me.m_compiled = true
end sub
//如果是默认源代码,此函数取表达式的值;
//如果自定义源代码,请参考本函数视实际自己写
public function getexpressionvalue(byval expression as string) as object
if not me.m_is_default then
msgbox("所用的代码不是默认代码,此函数无效")
return nothing
end if
如果还没有编译则编译
if not me.m_compiled then
//传入参数表达式作为代码
me.m_expression = expression
complile()
//生成实例,注意类名区分大小写
me.m_tmpclass = me.m_assembly.createinstance("tmpclass")
//取函数,注意大小写
m_methodinfo = me.m_tmpclass.gettype().getmethod("getexpressionvalue")
end if
//计算结果
dim result as object = m_methodinfo.invoke(m_tmpclass, nothing)
表达式不同时要重新编译
me.m_compiled = false
return result
end function
end class
//格式生成或读取代码的类
public class codebuilder
private _stringbuilder as system.text.stringbuilder
//格式,{0}为空格数,{1}代码字串,最后加回车换行
private const codeformat as string = "{0}{1}" & controlchars.crlf
sub new()
_stringbuilder = new system.text.stringbuilder
end sub
public overloads sub appendcode()
_stringbuilder.appendformat(codeformat, space(0), space(0))
end sub
public overloads sub appendcode(byval codestring as string)
_stringbuilder.appendformat(codeformat, space(0), codestring)
end sub
public overloads sub appendcode(byval codefloor as integer, byval codestring as string)
_stringbuilder.appendformat(codeformat, space(codefloor * 4), codestring)
end sub
//直接从已有vb文件中读取代码
public sub appendfromfile(byval filename as string)
if not system.io.file.exists(filename) then
msgbox(filename & "不存在.")
exit sub
end if
dim tmpstr as string
dim fs as system.io.filestream
fs = new system.io.filestream(filename, io.filemode.open, io.fileaccess.read, io.fileshare.read)
dim reader as new system.io.streamreader(fs, system.text.encoding.default)
tmpstr = reader.readtoend
reader.close()
fs.close()
_stringbuilder.append(tmpstr)
end sub
//返回代码串
public overrides function tostring() as string
return _stringbuilder.tostring
end function
//清除原有代码
public sub clear()
if _stringbuilder.length > 0 then _stringbuilder.remove(0, _stringbuilder.length – 1)
end sub
end class codebuilder
测试
dim mycomp as sourcecomp
private sub button1_click(byval sender as system.object, byval e as system.eventargs) handles button1.click
如果不重新生成实例,则因重新编译时输出同名的临时程序集,会出错
mycomp = new sourcecomp
console.writeline(mycomp.getexpressionvalue("math.round(math.sqrt(123 * 456), 2) ").tostring)
结果236.83
mycomp = new sourcecomp
console.writeline(mycomp.getexpressionvalue("123 * 456 > 12 * 6987").tostring)
结果false
end sub
