找回密码
 注册
搜索
热搜: 回贴

ASP.net:详解如何编写一个简明编译器

2009-12-13 13:08| 发布者: admin| 查看: 40| 评论: 0|原作者: 天仙子

〖多次看到有人提起文本表达式的计算问题,就......


  多次看到有人提起文本表达式的计算问题,就动手整理以前的代码并加上注释。
  写一个简单的编译器并不是很复杂的,当中要用到些反射的知识。自已觉得,反射的使用在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

最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-9-30 07:32 , Processed in 0.247178 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

返回顶部