Created
October 24, 2013 10:53
-
-
Save luelista/7135048 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Imports System.Text.RegularExpressions | |
Imports System.Reflection | |
Public Class BatchProcessor | |
Event TraceEvent(ByVal varName As String, ByVal varData As String, ByVal codeLine As Integer) | |
Event PrintEvent(ByVal text As String, ByVal codeLine As Integer, ByVal lineBreak As Boolean) | |
Private Shared rJumpTarget As New Regex("^\s*([a-zA-Z0-9_]+):\s*") | |
Private Shared rCall As New Regex("^\s*[a-zA-Z]+\s+(([a-zA-Z0-9_]+(\[.*\])?)\s*=\s*)?(([a-zA-Z0-9_.]+::)|([a-zA-Z0-9_]+\.))([a-zA-Z0-9_]+)\s*(.*)") | |
Private Shared rCallSet As New Regex("^\s*SET\s+(([a-zA-Z0-9_.]+::)|([a-zA-Z0-9_]+\.))([a-zA-Z0-9_]+)\s*(.*)\s*=\s*(.*)", RegexOptions.IgnoreCase) | |
Private Shared rSimple As New Regex("^\s*([a-zA-Z]+)\s+([a-zA-Z0-9_]+)\s*(.*)") | |
Private Shared rVarDecl As New Regex("^\s*DIM\s+([a-zA-Z0-9_,\s]+)", RegexOptions.IgnoreCase) | |
Private Shared rVarDeclSet As New Regex("^\s*VAR\s+([a-zA-Z0-9_]+)(\[([0-9]*)\])?\s*=\s*(.+)", RegexOptions.IgnoreCase) | |
Private Shared rVarSet As New Regex("^\s*([a-zA-Z0-9_]+(\[.*\])?)\s*=\s*(.+)") | |
Dim globVars As New Dictionary(Of String, Object) | |
Dim linesC() As lineC | |
Dim loadedAssemblies As New List(Of String) | |
Dim usedAssemblies As New Dictionary(Of String, Assembly) | |
Dim usedTypeCache As New Dictionary(Of String, RuntimeTypeHandle) | |
Dim usedMethodCache As New Dictionary(Of String, RuntimeMethodHandle) | |
Dim currentLine As Integer | |
Dim hasScript As Boolean | |
Dim goSubStack As New Stack(Of BatchStackFrame) | |
Dim ifCondStack As New Stack(Of Boolean) | |
Dim runningMode As String = "" | |
Dim scriptFilespec As String | |
Public isDebugMode As Boolean | |
Sub LoadAssembly(ByVal name As String) | |
Try | |
If loadedAssemblies.Contains(LCase(name)) Then Return | |
Dim a = Assembly.LoadWithPartialName(name) | |
usedAssemblies(a.FullName) = a | |
loadedAssemblies.Add(LCase(name)) | |
Catch ex As Exception | |
Throw New Exception("Couldnt load assembly " + name) | |
End Try | |
End Sub | |
Property ExecutingLine() As Integer | |
Get | |
If hasScript = False Then Return -1 | |
Return currentLine | |
End Get | |
Set(ByVal value As Integer) | |
If hasScript = False Then Throw New InvalidOperationException("No script code loaded") | |
currentLine = value | |
End Set | |
End Property | |
ReadOnly Property GlobalVars() As Dictionary(Of String, Object) | |
Get | |
Return globVars | |
End Get | |
End Property | |
ReadOnly Property CallStack() As BatchStackFrame() | |
Get | |
Return goSubStack.ToArray | |
End Get | |
End Property | |
ReadOnly Property IsScriptLoaded() As Boolean | |
Get | |
Return hasScript | |
End Get | |
End Property | |
Structure BatchStackFrame | |
Dim lineNr As Integer | |
Dim txt As String | |
Dim para As Object | |
End Structure | |
Structure lineC | |
Dim typ As LineTypes | |
Dim jumpTarget As String | |
Dim expression As String | |
Dim expression2 As String | |
Dim params() As String | |
Dim params2 As String | |
Dim fullText As String | |
' Dim nettoLine As String | |
Dim ifIndent As Integer | |
Dim varName As String | |
Dim isStatic As Boolean | |
End Structure | |
Sub ParseLine(ByRef line As lineC, ByVal txt As String, ByRef curIfIndent As Integer, ByRef blockComment As Boolean, ByRef inProg As Integer) | |
With line | |
.fullText = txt | |
Dim nettoLine = Trim(.fullText) | |
.ifIndent = curIfIndent | |
If nettoLine = "" Or nettoLine.StartsWith("#") Then .typ = LineTypes.Comment : Exit Sub | |
Dim m = rJumpTarget.Match(nettoLine) | |
If m.Success Then | |
.jumpTarget = m.Groups(1).Value | |
nettoLine = nettoLine.Substring(.jumpTarget.Length + 1).Trim | |
End If | |
If nettoLine = "" Or nettoLine.StartsWith("#") Then .typ = LineTypes.Comment : Exit Sub | |
Dim words() As String = nettoLine.Split(" "c, "("c, ","c) | |
Dim firstWord As String = UCase(words(0)) | |
If (inProg = 2) Then .typ = LineTypes.Comment : Exit Sub | |
If (inProg = 0) And (firstWord <> "SCRIPT" And firstWord <> "REFERENCE" And firstWord <> "REF") Then .typ = LineTypes.Comment : Exit Sub | |
If (blockComment) And (firstWord <> "ENDKOM") Then .typ = LineTypes.Comment : Exit Sub | |
Select Case firstWord | |
Case "KOM" | |
blockComment = True | |
Case "ENDKOM" | |
blockComment = False | |
Case "SCRIPT" | |
inProg = 1 | |
Case "ENDSCRIPT" | |
inProg = 2 | |
Case "REFERENCE", "REF" : .typ = LineTypes.ReferenceStatement | |
' m = rSimple.Match(nettoLine) | |
' If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige REFERENCE-Syntax") | |
.expression = nettoLine.Substring(words(0).Length).Trim | |
LoadAssembly(.expression) | |
Case "IF" : .typ = LineTypes.IfStatement | |
.expression = nettoLine.Substring(2) | |
curIfIndent += 1 | |
Case "ELSE" : .typ = LineTypes.ElseStatement | |
.ifIndent = curIfIndent - 1 | |
Case "ENDIF" : .typ = LineTypes.EndIfStatement | |
.ifIndent = curIfIndent - 1 | |
curIfIndent -= 1 | |
Case "TRACE" : .typ = LineTypes.TraceCmd | |
.expression = nettoLine.Substring(5).Trim | |
Case "PRINT" : .typ = LineTypes.PrintCmd | |
.expression = nettoLine.Substring(5).Trim | |
If .expression.EndsWith(";") Then .expression = .expression.Substring(0, .expression.Length - 1) : .expression2 = "nobreak" | |
Case "PAUSE" : .typ = LineTypes.PauseCmd | |
Case "GOTO" : .typ = LineTypes.GotoCmd | |
m = rSimple.Match(nettoLine) | |
If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige GOTO-Syntax") | |
.expression = m.Groups(2).Value | |
Case "GOSUB" : .typ = LineTypes.GosubCmd | |
m = rSimple.Match(nettoLine) | |
If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige GOSUB-Syntax") | |
.expression = m.Groups(2).Value | |
.params2 = m.Groups(3).Value | |
Case "RETURN", "RET" : .typ = LineTypes.ReturnCmd | |
Case "STOP" : .typ = LineTypes.StopCmd | |
Case "EXIT" : .typ = LineTypes.ExitCmd | |
Case "CALL", "GET", "SET", "GETF", "SETF", "FGET", "FSET" | |
If firstWord = "CALL" Then : .typ = LineTypes.CallMethod | |
ElseIf firstWord = "GET" Then : .typ = LineTypes.CallGet | |
ElseIf firstWord = "SET" Then : .typ = LineTypes.CallSet | |
ElseIf firstWord = "GETF" Or firstWord = "FGET" Then : .typ = LineTypes.FieldGet | |
ElseIf firstWord = "SETF" Or firstWord = "FSET" Then : .typ = LineTypes.FieldSet : End If | |
m = rCall.Match(nettoLine) | |
If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige GET/SET/CALL-Syntax") | |
If m.Groups(5).Success Then | |
.isStatic = True | |
.expression = m.Groups(5).Value | |
.expression = .expression.Substring(0, .expression.Length - 2) | |
Else | |
.expression = m.Groups(6).Value | |
.expression = .expression.Substring(0, .expression.Length - 1) | |
End If | |
.expression2 = m.Groups(7).Value | |
.params2 = m.Groups(8).Value | |
If m.Groups(1).Success Then .varName = m.Groups(2).Value | |
'Case "SET" | |
' .typ = LineTypes.CallSet | |
' m = rCallSet.Match(nettoLine) | |
' If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige SET-Syntax") | |
' If m.Groups(2).Success Then | |
' .isStatic = True | |
' .expression = m.Groups(2).Value | |
' .expression = .expression.Substring(0, .expression.Length - 2) | |
' Else | |
' .expression = m.Groups(3).Value | |
' .expression = .expression.Substring(0, .expression.Length - 1) | |
' End If | |
' .params2 = m.Groups(4).Value | |
' .expression2 = m.Groups(5).Value | |
Case "VAR" : .typ = LineTypes.VarDeclSet | |
m = rVarDeclSet.Match(nettoLine) | |
If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige VAR-Syntax") | |
.varName = m.Groups(1).Value | |
If m.Groups(2).Success Then | |
.expression = If(m.Groups(3).Value <> "", m.Groups(3).Value, "") | |
Else | |
.expression = Nothing | |
End If | |
.expression2 = m.Groups(4).Value | |
Case "DIM" : .typ = LineTypes.VarDecl | |
m = rVarDecl.Match(nettoLine) | |
If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige DIM-Syntax") | |
.params = Split(m.Groups(1).Value, ",") | |
Case Else | |
m = rVarSet.Match(nettoLine) | |
If m.Success = False Then Throw New SyntaxErrorException(0, 1000, "Ungültige Zeile") | |
.typ = LineTypes.VarSet | |
.varName = m.Groups(1).Value | |
'If m.Groups(2).Success Then | |
' .expression = If(m.Groups(3).Value <> "", m.Groups(3).Value, "") | |
'Else | |
' .expression = Nothing | |
'End If | |
.expression2 = m.Groups(3).Value | |
End Select | |
End With | |
End Sub | |
Enum LineTypes | |
Comment = 0 | |
VarDecl | |
VarDeclSet | |
VarSet | |
JumpTarget | |
GotoCmd | |
GosubCmd | |
ReturnCmd | |
TraceCmd | |
PrintCmd | |
PauseCmd | |
InkeyCmd | |
CallMethod | |
CallNew | |
CallGet | |
CallSet | |
FieldGet | |
FieldSet | |
IfStatement | |
ElseStatement | |
EndIfStatement | |
ReferenceStatement | |
StopCmd | |
ExitCmd | |
End Enum | |
Sub setScriptFile(ByVal fileSpec As String) | |
setScriptCode(IO.File.ReadAllText(fileSpec)) | |
scriptFilespec = fileSpec | |
End Sub | |
Sub setScriptCode(ByVal code As String) | |
'reset | |
scriptFilespec = Nothing | |
globVars.Clear() | |
goSubStack.Clear() | |
ifCondStack.Clear() | |
loadedAssemblies.Clear() | |
usedAssemblies.Clear() | |
'load | |
Dim ifIndent As Integer, blockComment As Boolean, inProg As Integer | |
Dim lines() = Split(code, vbNewLine) | |
ReDim linesC(lines.Length - 1) | |
hasScript = True | |
For i = 0 To lines.Length - 1 | |
Try | |
ParseLine(linesC(i), lines(i), ifIndent, blockComment, inProg) | |
Catch ex As Exception | |
RaiseEvent TraceEvent("Compile Error", ex.ToString, i) | |
hasScript = False | |
End Try | |
Next | |
If hasScript Then | |
currentLine = 0 | |
Else | |
currentLine = -1 | |
End If | |
End Sub | |
Sub RunScript() | |
If hasScript = False Then Throw New InvalidOperationException("No script code loaded") | |
runningMode = "RUN" | |
Do | |
ExecStepInternal() | |
If runningMode <> "RUN" Then | |
Exit Do | |
End If | |
Loop | |
End Sub | |
Sub SingleStep() | |
If hasScript = False Then Throw New InvalidOperationException("No script code loaded") | |
runningMode = "STEP" | |
ExecStepInternal() | |
runningMode = "BREAK" | |
End Sub | |
Private Sub ExecStepInternal() | |
Dim myLine As Integer = currentLine | |
Try | |
ExecuteLine(currentLine) | |
Catch ex As SyntaxErrorException | |
RaiseEvent TraceEvent("Runtime Error", ex.ToString, myLine) | |
runningMode = "BREAK" | |
Catch ex As Exception | |
RaiseEvent TraceEvent("Runtime Error", ex.ToString, myLine) | |
runningMode = "BREAK" | |
End Try | |
End Sub | |
Private Sub ExecuteLine(ByVal idx As Integer) | |
If idx > linesC.Length - 1 Then runningMode = "" : currentLine = 0 : Exit Sub | |
Dim line = linesC(idx) | |
Select Case line.typ | |
Case LineTypes.VarDecl | |
For Each varName In line.params | |
globVars(LCase(Trim(varName))) = "" | |
Next | |
Case LineTypes.GotoCmd | |
Dim target = findJumpTarget(line.expression) | |
If target = -1 Then Throw New SyntaxErrorException(0, 1006, "Jump target " + line.expression + " not found") | |
currentLine = target | |
Exit Sub | |
Case LineTypes.GosubCmd | |
Dim target = findJumpTarget(line.expression) | |
If target = -1 Then Throw New SyntaxErrorException(0, 1006, "Jump target " + line.expression + " not found") | |
goSubStack.Push(New BatchStackFrame() With {.lineNr = currentLine, .para = ParseBoolExpression(line.params2), .txt = linesC(target).fullText}) | |
currentLine = target | |
Exit Sub | |
Case LineTypes.ReturnCmd | |
Dim stackFrame = goSubStack.Pop | |
currentLine = stackFrame.lineNr + 1 | |
Exit Sub | |
Case LineTypes.IfStatement | |
Dim val As Boolean = ParseExpression(line.expression) | |
ifCondStack.Push(val) | |
If val = False Then | |
currentLine = findNextIfChange(idx) | |
Exit Sub | |
End If | |
Case LineTypes.ElseStatement | |
Dim val As Boolean = Not ifCondStack.Pop() | |
ifCondStack.Push(val) | |
If val = False Then | |
currentLine = findNextIfChange(idx) | |
Exit Sub | |
End If | |
Case LineTypes.StopCmd | |
If isDebugMode Then | |
If runningMode = "RUN" Then runningMode = "BREAK" : Exit Sub | |
Else | |
RaiseEvent PrintEvent("STOP-Anweisung aufgetreten. Weiter mit beliebiger Taste ...", currentLine, True) | |
End If | |
Case LineTypes.ExitCmd | |
runningMode = "" : currentLine = 0 : Exit Sub | |
Case LineTypes.EndIfStatement | |
ifCondStack.Pop() | |
Case LineTypes.VarSet, LineTypes.VarDeclSet | |
'If line.typ <> LineTypes.VarDeclSet AndAlso globVars.ContainsKey(LCase(line.varName)) = False Then | |
' Throw New SyntaxErrorException(0, 1002, "Var " + line.varName + " not defined") | |
'End If | |
'If line.expression IsNot Nothing Then | |
' If line.expression = "" Then | |
' globVars(LCase(line.varName)).add(ParseExpression(line.expression2)) | |
' Else | |
' globVars(LCase(line.varName))(line.expression) = ParseExpression(line.expression2) | |
' End If | |
'Else | |
' globVars(LCase(line.varName)) = ParseExpression(line.expression2) | |
'End If | |
setVarByName(line.varName, ParseExpression(line.expression2), line.typ = LineTypes.VarDeclSet) | |
Case LineTypes.TraceCmd | |
If line.expression.StartsWith("""") Then | |
RaiseEvent TraceEvent("", ParseBoolExpression(line.expression), idx) | |
Else | |
Dim varCont As Object | |
If Not globVars.TryGetValue(LCase(line.expression), varCont) Then | |
varCont = "(VAR NOT DEFINED)" | |
Else | |
varCont = varDump(varCont) | |
End If | |
RaiseEvent TraceEvent(line.expression, varCont, idx) | |
End If | |
Case LineTypes.PrintCmd | |
Dim obj = ParseBoolExpression(line.expression) | |
If TypeOf obj Is List(Of Object) Then | |
RaiseEvent PrintEvent(Join(obj.ToArray(), vbTab), idx, line.expression2 <> "nobreak") | |
ElseIf TypeOf obj Is String Then | |
RaiseEvent PrintEvent(obj, idx, line.expression2 <> "nobreak") | |
Else | |
RaiseEvent PrintEvent(obj.ToString, idx, line.expression2 <> "nobreak") | |
End If | |
Case LineTypes.PauseCmd | |
If isDebugMode Then | |
If runningMode = "RUN" Then runningMode = "BREAK" : Exit Sub | |
Else | |
RaiseEvent PrintEvent("Weiter mit beliebiger Taste ...", currentLine, False) | |
End If | |
Case LineTypes.CallMethod, LineTypes.CallGet, LineTypes.CallSet, LineTypes.FieldGet, LineTypes.FieldSet | |
Dim para As Object = getInvokeParaArray(line.params2) | |
Dim flags As BindingFlags = BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.IgnoreCase | |
If line.typ = LineTypes.FieldGet Then flags = flags Or BindingFlags.GetField | |
If line.typ = LineTypes.FieldSet Then flags = flags Or BindingFlags.SetField | |
If line.typ = LineTypes.CallGet Then flags = flags Or BindingFlags.GetProperty | |
If line.typ = LineTypes.CallSet Then flags = flags Or BindingFlags.SetProperty | |
If line.typ = LineTypes.CallMethod Then flags = flags Or BindingFlags.InvokeMethod | |
Dim targetObj As Object = Nothing | |
Dim typ As Type | |
If line.isStatic Then | |
typ = GetTypeByName(line.expression) | |
flags = flags Or BindingFlags.Static | |
Else | |
flags = flags Or BindingFlags.Instance | |
If globVars.TryGetValue(LCase(line.expression), targetObj) = False Then | |
Throw New SyntaxErrorException(0, 1002, "Var " + line.varName + " not defined") | |
End If | |
typ = targetObj.GetType() | |
End If | |
Dim result = typ.InvokeMember(line.expression2, flags, Nothing, targetObj, para) | |
If Not String.IsNullOrEmpty(line.varName) Then | |
'If globVars.ContainsKey(LCase(line.varName)) = False Then | |
' Throw New SyntaxErrorException(0, 1002, "Var " + line.varName + " not defined") | |
'End If | |
'globVars(LCase(line.varName)) = result | |
setVarByName(line.varName, result, False) | |
End If | |
End Select | |
currentLine += 1 | |
End Sub | |
Function varDump(ByVal varCont As Object) As String | |
If varCont Is Nothing Then | |
varCont = "(NOTHING)" | |
ElseIf TypeOf varCont Is Object() Then | |
Dim txt(varCont.length - 1) As String | |
For i = 0 To txt.Length - 1 | |
txt(i) = varDump(varCont(i)) | |
Next | |
varCont = "[" + Join(txt, ", ") + "]" | |
ElseIf TypeOf varCont Is List(Of Object) Then | |
Dim txt(varCont.count - 1) As String | |
For i = 0 To txt.Count - 1 | |
txt(i) = varDump(varCont(i)) | |
Next | |
varCont = "[" + Join(txt, ", ") + "]" | |
Else | |
varCont = "(" + varCont.GetType.Name + ") """ + varCont.ToString + """" | |
End If | |
Return varCont | |
End Function | |
Function GetTypeByName(ByVal name As String) As Type | |
name = LCase(name) | |
If usedTypeCache.ContainsKey(name) Then | |
Return Type.GetTypeFromHandle(usedTypeCache(name)) | |
End If | |
For Each kvp In usedAssemblies | |
Dim a As Assembly = kvp.Value | |
For Each typ As Type In a.GetTypes | |
If LCase(typ.FullName) = name Then | |
usedTypeCache.Add(name, typ.TypeHandle) | |
Return typ | |
End If | |
Next | |
Next | |
Throw New Exception("Type " + name + " not found. check for typos or references") | |
End Function | |
Function findNextIfChange(ByVal startLine As Integer) As Integer | |
Dim ifLevel As Integer = linesC(startLine).ifIndent | |
For i = startLine + 1 To linesC.Length - 1 | |
If ifLevel = linesC(i).ifIndent Then Return i | |
Next | |
End Function | |
Function findJumpTarget(ByVal name As String) As Integer | |
For I = 0 To linesC.Length - 1 | |
If LCase(linesC(I).jumpTarget) = LCase(name) Then Return I | |
Next | |
Return -1 | |
End Function | |
Function getMin(ByVal ParamArray vars() As Integer) As Integer | |
getMin = -1 | |
For i = 1 To vars.Length - 1 | |
If (vars(i) < getMin And vars(i) > -1) Or getMin = -1 Then getMin = vars(i) | |
Next | |
End Function | |
Function ParseExpression(ByVal stat As String) As Object | |
'stat = Trim(stat) | |
'If stat.Contains("""") Then | |
'Return ParseStringExpression(stat) | |
'Else | |
Return ParseBoolExpression(stat) | |
'End If | |
End Function | |
Function ParseStringExpression(ByVal stat As String) As String | |
If stat.Chars(0) <> """"c Or stat.Chars(stat.Length - 1) <> """"c Then Throw New SyntaxErrorException(0, 1003, "Invalid String Expression") | |
stat = stat.Substring(1, stat.Length - 2) | |
Dim rFindStrings As New Regex("\$\(([a-zA-Z0-9_]+)\)") | |
Return rFindStrings.Replace(stat, AddressOf replaceWithVar) | |
End Function | |
Function replaceWithVar(ByVal m As Match) As String | |
Return getVarByName(m.Groups(1).Value) | |
End Function | |
Sub findString(ByVal str As String, ByRef istart As Integer, ByRef iend As Integer) | |
For i = 0 To str.Length - 1 | |
If str(i) = """"c Then | |
If i < str.Length - 1 AndAlso str(i + 1) = """"c Then i += 1 : Continue For | |
If istart = -1 Then istart = i Else iend = i : Exit Sub | |
End If | |
Next | |
End Sub | |
Sub findStringEnd(ByVal str As String, ByVal istart As Integer, ByRef iend As Integer) | |
For i = istart + 1 To str.Length - 1 | |
If str(i) = """"c Then | |
If i < str.Length - 1 AndAlso str(i + 1) = """"c Then i += 1 : Continue For | |
iend = i : Exit Sub | |
End If | |
Next | |
End Sub | |
Sub findFirstOp(ByVal stat As String, ByRef op As String, ByRef index As Integer) | |
index = -1 | |
Dim sqbLevel As Integer = 0, brLevel As Integer = 0 'sqb=squareBracket | |
Dim isInStr As Boolean = False | |
Dim vonPos As Integer = 1 | |
Dim lst As New List(Of Object) | |
For i = 0 To stat.Length - 1 | |
If stat(i) = """"c Then | |
If i < stat.Length - 1 AndAlso stat(i + 1) = """"c Then i += 1 : Continue For | |
isInStr = Not isInStr | |
End If | |
If Not isInStr Then | |
If stat(i) = "["c Then sqbLevel += 1 | |
If stat(i) = "]"c Then sqbLevel -= 1 | |
If stat(i) = "("c Then brLevel += 1 | |
If stat(i) = ")"c Then brLevel -= 1 | |
If sqbLevel = 0 And brLevel = 0 Then | |
If stat(i) = "+"c Or stat(i) = "-"c Or stat(i) = "*"c Or stat(i) = "/"c _ | |
Or stat(i) = "&"c Or stat(i) = "|"c Or stat(i) = "="c Or stat(i) = "~"c _ | |
Or stat(i) = ">"c Or stat(i) = "<"c Then | |
index = i | |
op = stat(i) | |
Exit Sub | |
End If | |
End If | |
End If | |
Next | |
End Sub | |
Function ParseListExpression(ByVal stat As String) As List(Of Object) | |
Dim sqbLevel As Integer = 0 'sqb=squareBracket | |
Dim isInStr As Boolean = False | |
Dim vonPos As Integer = 1 | |
Dim lst As New List(Of Object) | |
For i = 0 To stat.Length - 1 | |
If stat(i) = """"c Then | |
If i < stat.Length - 1 AndAlso stat(i + 1) = """"c Then i += 1 : Continue For | |
isInStr = Not isInStr | |
End If | |
If Not isInStr Then | |
If stat(i) = "["c Then sqbLevel += 1 | |
If stat(i) = "]"c Then sqbLevel -= 1 | |
If sqbLevel < 0 Then Throw New SyntaxErrorException(0, 1006, "Too many closing square Brackets") | |
If (sqbLevel = 1 And stat(i) = ","c) Or sqbLevel = 0 Then | |
Dim expr = stat.Substring(vonPos, i - vonPos) | |
If Trim(expr) <> "" Then lst.Add(ParseBoolExpression(expr)) | |
vonPos = i + 1 | |
End If | |
End If | |
Next | |
If sqbLevel > 0 Then Throw New SyntaxErrorException(0, 1006, "Not enough closing square Brackets") | |
Return lst | |
End Function | |
Function getInvokeParaArray(ByVal expr As String) As Object() | |
If String.IsNullOrEmpty(expr) Then Return Nothing | |
Dim para As Object = ParseBoolExpression(expr) | |
If para Is Nothing Then Return Nothing | |
If Not TypeOf para Is Object() Then | |
If TypeOf para Is List(Of Object) Then | |
para = para.ToArray | |
Else | |
para = New Object() {para} | |
End If | |
End If | |
Return para | |
End Function | |
Function ParseBoolExpression(ByVal stat As String) As Object | |
stat = Trim(stat) | |
If String.IsNullOrEmpty(stat) Then Return Nothing | |
If IsNumeric(stat) Then | |
ParseBoolExpression = Val(stat) | |
If stat.Contains(".") Then Return CSng(ParseBoolExpression) Else Return CInt(ParseBoolExpression) | |
End If | |
If LCase(stat) = "true" Then Return True | |
If LCase(stat) = "false" Then Return False | |
If stat.StartsWith("new ", StringComparison.InvariantCultureIgnoreCase) Then | |
Dim m = Regex.Match(stat, "[Nn][Ee][Ww]\s+([A-Za-z0-9_.]+)\s*(.*)") | |
If m.Success Then | |
Dim typ = GetTypeByName(m.Groups(1).Value) | |
Dim para = getInvokeParaArray(m.Groups(2).Value) | |
Return Activator.CreateInstance(typ, para) | |
'Return typ.InvokeMember(Nothing, BindingFlags.CreateInstance Or BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, Nothing, para) | |
End If | |
End If | |
'Dim startPos = 0 | |
If stat.StartsWith("[") Then Return ParseListExpression(stat) | |
'If stat.StartsWith("""") Then startPos = -1 : findStringEnd(stat, 0, startPos) | |
'If stat.StartsWith("(") Then startPos = stat.IndexOf(")") | |
'If startPos = -1 Then Throw New SyntaxErrorException(0, 1001, "No matching ) found") | |
'Dim orPos, andPos, plusPos, minusPos, mulPos, divPos, eqPos As Integer | |
'orPos = stat.IndexOf("|", startPos) | |
'andPos = stat.IndexOf("&", startPos) | |
'plusPos = stat.IndexOf("+", startPos) | |
'minusPos = stat.IndexOf("-", startPos) | |
'mulPos = stat.IndexOf("*", startPos) | |
'divPos = stat.IndexOf("/", startPos) | |
'eqPos = stat.IndexOf("==", startPos) | |
'Dim firstPos = getMin(orPos, andPos, plusPos, minusPos, mulPos, divPos, eqPos) | |
Dim index As Integer, op As String | |
findFirstOp(stat, op, index) | |
If index = -1 Then | |
If stat.StartsWith("!") Then | |
Return Not ParseBoolExpression(stat.Substring(1)) | |
ElseIf stat.StartsWith("{") Then | |
Dim typPos = stat.IndexOf("}") | |
Dim typName = stat.Substring(1, typPos - 1) | |
Dim typ = GetTypeByName(typName) | |
Return Microsoft.VisualBasic.CompilerServices.Conversions.ChangeType(ParseBoolExpression(stat.Substring(typPos + 1)), typ) | |
ElseIf stat.StartsWith("""") Then | |
Return ParseStringExpression(stat) | |
ElseIf stat.StartsWith("(") Then | |
Return ParseBoolExpression(stat.Substring(1, stat.Length - 2)) | |
Else | |
Dim m = Regex.Match(stat, "^([a-zA-Z]+)\((.*)\)$") | |
If m.Success Then | |
Return ExpressionFunction(m.Groups(1).Value, m.Groups(2).Value) | |
End If | |
Dim indexPos = stat.IndexOf("[") | |
If indexPos > -1 Then | |
Dim varName = stat.Substring(0, indexPos) | |
Dim varIndex = ParseBoolExpression(stat.Substring(indexPos + 1, stat.IndexOf("]") - indexPos - 1)) | |
Dim varCont As Object = getVarByName(varName) | |
Return varCont(varIndex) | |
Else | |
Return getVarByName(stat) | |
End If | |
End If | |
End If | |
Dim var1, var2 As String | |
var1 = stat.Substring(0, index) | |
var2 = stat.Substring(index + 1) | |
Select Case op | |
Case "=" | |
Return ParseBoolExpression(var1) = ParseBoolExpression(var2) | |
Case "~" | |
Return ParseBoolExpression(var1) <> ParseBoolExpression(var2) | |
Case "|" | |
Return ParseBoolExpression(var1) Or ParseBoolExpression(var2) | |
Case "&" | |
Return ParseBoolExpression(var1) And ParseBoolExpression(var2) | |
Case "+" | |
Return ParseBoolExpression(var1) + ParseBoolExpression(var2) | |
Case "-" | |
Return ParseBoolExpression(var1) - ParseBoolExpression(var2) | |
Case "*" | |
Return ParseBoolExpression(var1) * ParseBoolExpression(var2) | |
Case "/" | |
Return ParseBoolExpression(var1) / ParseBoolExpression(var2) | |
Case ">" | |
Return ParseBoolExpression(var1) > ParseBoolExpression(var2) | |
Case "<" | |
Return ParseBoolExpression(var1) < ParseBoolExpression(var2) | |
End Select | |
End Function | |
Function ExpressionFunction(ByVal funcName As String, ByVal exp As String) As Object | |
Select Case UCase(funcName) | |
Case "STR" | |
Return CStr(ParseBoolExpression(exp)) | |
Case "INT" | |
Return CInt(Val(ParseBoolExpression(exp))) | |
Case "PRN" | |
Return varDump(ParseBoolExpression(exp)) | |
Case "COLOR" | |
If exp.StartsWith("#") = False Then exp = "#" + exp | |
Return Drawing.ColorTranslator.FromHtml(exp) | |
Case "LEN" | |
Dim obj = ParseBoolExpression(exp) | |
If TypeOf obj Is Array Or TypeOf obj Is String Then | |
Return obj.length | |
Else | |
Return obj.count | |
End If | |
Case "EVAL" | |
Return ParseBoolExpression(ParseBoolExpression(exp)) | |
Case "SCRIPTFILESPEC" | |
Return scriptFilespec | |
End Select | |
End Function | |
Function getVarByName(ByVal varName As String) As Object | |
If varName = "§" Then Return goSubStack.Peek.para | |
If Not globVars.TryGetValue(LCase(varName), getVarByName) Then | |
Throw New SyntaxErrorException(0, 1002, "Var '" + varName + "' not defined") | |
End If | |
End Function | |
Sub setVarByName(ByVal varName As String, ByVal content As Object, ByVal autoCreate As Boolean) | |
Dim indexPos = varName.IndexOf("[") | |
If indexPos > -1 Then | |
Dim varName2 = varName.Substring(0, indexPos) | |
If autoCreate = False AndAlso globVars.ContainsKey(LCase(varName2)) = False Then | |
Throw New SyntaxErrorException(0, 1002, "Var " + varName2 + " not defined") | |
End If | |
Dim varIndex = ParseBoolExpression(varName.Substring(indexPos + 1, varName.IndexOf("]") - indexPos - 1)) | |
If varIndex Is Nothing Then | |
globVars(LCase(varName2)).add(content) | |
Else | |
globVars(LCase(varName2))(varIndex) = content | |
End If | |
Else | |
If autoCreate = False AndAlso globVars.ContainsKey(LCase(varName)) = False Then | |
Throw New SyntaxErrorException(0, 1002, "Var " + varName + " not defined") | |
End If | |
globVars(LCase(varName)) = content | |
End If | |
End Sub | |
End Class |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment