一個簡明的編譯器

- 中國WEB開發者網絡 (http://www.webasp.net)
-- 技術教程 (http://www.webasp.net/article/)
--- 一個簡明的編譯器 (http://www.webasp.net/article/15/14651.htm)
-- 作者:未知
-- 發佈日期: 2004-11-08
一個簡明的編譯器



多次看到有人提起文本表達式的計算問題,就動手整理以前的代碼並加上註釋。

寫一個簡單的編譯器並不是很複雜的,當中要用到些反射的知識。自已覺得,反射的使用在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




webasp.net