Skip to content

Instantly share code, notes, and snippets.

@luelista
Created October 24, 2013 10:53
Show Gist options
  • Save luelista/7135048 to your computer and use it in GitHub Desktop.
Save luelista/7135048 to your computer and use it in GitHub Desktop.
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