Надеюсь, что то, что получислось - настоящий автомат (ака State Machine), потому что раньше я их никогда не писал
Назначение: парсилка INI-файлов.
- Код: Выделить всё
Option Explicit On
Option Strict On
Module Main
Sub Main()
Dim Section As INIParser.Section, Value As INIParser.Value
For Each Section In New INIParser.INIParser(New IO.FileStream(Command(), IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read))
Console.WriteLine("[" & Section.Name & "]")
For Each Value In Section
Console.WriteLine(Value.Name & CStr(IIf(Value.Value Is Nothing, "", "=" & Value.Value)))
Next
Next
End Sub
End Module
Namespace Automata
Friend Module General
''emulates operator= of C
Friend Function Assign(ByRef LHS As Object, ByVal RHS As Object) As Object
LHS = RHS
Assign = LHS
End Function
End Module
Class Automaton
Structure TransitionEntry
Public NextState As Integer
Public Tag As Object
Sub New(ByVal NextState As Integer, ByVal Tag As Object)
Me.NextState = NextState : Me.Tag = Tag
End Sub
End Structure
Public Delegate Function Group(ByVal _Byte As Integer) As Integer
Private Shared Function DefaultGroup(ByVal _Byte As Integer) As Integer
Return _Byte
End Function
Public Delegate Sub Transition(ByVal _Byte As Integer, ByVal OldState As Integer, ByVal NewState As Integer, ByVal TransitionTag As Object)
Private Shared Sub DefaultTransition(ByVal _Byte As Integer, ByVal OldState As Integer, ByVal NewState As Integer, ByVal TransitionTag As Object)
End Sub
Private m_States(,) As TransitionEntry, m_Transition As Transition
Private m_Group As Group, m_State As Integer
Sub New(ByVal States(,) As TransitionEntry, Optional ByVal Group As Group = Nothing, Optional ByVal Transition As Transition = Nothing)
: If Group Is Nothing Then Group = AddressOf DefaultGroup
: If Transition Is Nothing Then Transition = AddressOf DefaultTransition
If States.Rank <> 2 Then Throw New ArgumentException
ReDim m_States(States.GetLength(0) - 1, States.GetLength(1) - 1)
Array.Copy(States, m_States, States.GetLength(0) * States.GetLength(1))
m_Group = Group
m_Transition = Transition
m_State = 0
End Sub
Sub Run(ByVal Stream As IO.Stream)
Dim CurByte As Object, PrevState As Integer, TransitionEntry As TransitionEntry
While CInt(Assign(CurByte, Stream.ReadByte)) >= 0
PrevState = m_State
TransitionEntry = m_States(m_State, m_Group(CInt(CurByte)))
m_State = TransitionEntry.NextState
m_Transition(CInt(CurByte), PrevState, m_State, TransitionEntry.Tag)
End While
End Sub
End Class
End Namespace
Namespace INIParser
Class Value
Implements IComparable, ICloneable
Public Overloads Function CompareTo(ByVal obj As Object) As Integer Implements IComparable.CompareTo
If TypeOf obj Is Value Then
Dim RHS As Value = CType(obj, Value)
Return m_Value.CompareTo(RHS.m_Value)
End If
Throw New ArgumentException
End Function
Public Function Clone() As Object Implements System.ICloneable.Clone
Return New Value(m_Name, m_Value)
End Function
Private m_Name As String, m_Value As String
Friend Sub New(ByVal Name As String, Optional ByVal Value As String = Nothing)
m_Name = Name : m_Value = Value
End Sub
ReadOnly Property Name() As String
Get
Name = m_Name
End Get
End Property
ReadOnly Property Value() As String
Get
Value = m_Value
End Get
End Property
End Class
Class Section
Inherits System.Collections.ReadOnlyCollectionBase
Implements ICloneable
Public Function Clone() As Object Implements System.ICloneable.Clone
Return New Section(m_Name, InnerList)
End Function
Private m_Name As String
Friend Sub New(ByVal Name As String)
m_Name = Name
End Sub
Friend Sub New(ByVal Name As String, ByVal Values As ArrayList)
m_Name = Name : InnerList.AddRange(CType(Values.Clone, ArrayList))
End Sub
ReadOnly Property Name() As String
Get
Name = m_Name
End Get
End Property
Friend Sub Add(ByVal Value As Value)
InnerList.Add(Value)
End Sub
Default ReadOnly Property Value(ByVal Index As Integer) As Value
Get
Return CType(InnerList(Index), Value)
End Get
End Property
Default ReadOnly Property Value(ByVal Name As String) As Value
Get
Return CType(InnerList(InnerList.BinarySearch(New Value(Name, Nothing))), Value)
End Get
End Property
End Class
Class INIParser
Inherits System.Collections.ReadOnlyCollectionBase
Implements ICloneable
Public Function Clone() As Object Implements System.ICloneable.Clone
Return New INIParser(InnerList)
End Function
Private Shared INIStates As Integer(,) = {{7, 0, 0, 0, 3, 1, 7, 4, 7}, _
{7, 1, 7, 7, 1, 7, 6, 7, 7}, _
{2, 2, 2, 0, 2, 2, 2, 2, 2}, _
{7, 5, 5, 0, 3, 7, 7, 4, 2}, _
{4, 4, 4, 0, 4, 4, 4, 4, 4}, _
{7, 5, 5, 0, 7, 7, 7, 4, 2}, _
{7, 6, 6, 0, 7, 7, 7, 4, 7}, _
{7, 7, 7, 7, 7, 7, 7, 7, 7}}
Private States(,) As Automata.Automaton.TransitionEntry
Private Shared Function INIGroup(ByVal _Byte As Integer) As Integer
Select Case _Byte
Case 32 : Return 1 'Space
Case 9 : Return 2 'Tab
Case 13, 10 : Return 3 'Eol
Case 48 To 57, 65 To 90, 97 To 122, 95 : Return 4 'Alphanumeric, _
Case 91 : Return 5 '[
Case 93 : Return 6 ']
Case 59 : Return 7 ';
Case 61 : Return 8 '=
Case Else : Return 0
End Select
End Function
Private Shared Eol() As Byte = {10}
Private SectionName, ValueName, Value As String
Private CurSection As Section
Private Delegate Sub Commit()
Private Sub CommitSection()
If Not (CurSection Is Nothing) Then InnerList.Add(CurSection)
CurSection = New Section(SectionName)
End Sub
Private Sub CommitName() '''commits empty value
If CurSection Is Nothing Then Throw New FormatException
CurSection.Add(New Value(ValueName))
End Sub
Private Sub CommitValue()
If CurSection Is Nothing Then Throw New FormatException
CurSection.Add(New Value(ValueName, Value))
End Sub
Private Sub InitializeStates()
ReDim States(UBound(INIStates, 1), UBound(INIStates, 2))
Dim s, g As Integer : For s = 0 To UBound(INIStates, 1) : For g = 0 To UBound(INIStates, 2)
States(s, g).NextState = INIStates(s, g)
Next g, s
States(1, 6).Tag = CType(AddressOf CommitSection, Commit)
States(2, 3).Tag = CType(AddressOf CommitValue, Commit)
States(3, 3).Tag = CType(AddressOf CommitName, Commit)
States(3, 7).Tag = CType(AddressOf CommitName, Commit)
States(5, 3).Tag = CType(AddressOf CommitName, Commit)
States(5, 7).Tag = CType(AddressOf CommitName, Commit)
End Sub
Private Sub INITransition(ByVal _Byte As Integer, ByVal OldState As Integer, ByVal NewState As Integer, ByVal TransitionTag As Object)
Select Case NewState
Case 1 : If OldState = NewState Then SectionName = SectionName & Chr(_Byte) Else SectionName = ""
Case 2 : If OldState = NewState Then Value = Value & Chr(_Byte) Else Value = ""
Case 3 : If OldState = NewState Then ValueName = ValueName & Chr(_Byte) Else ValueName = Chr(_Byte)
Case 7 : Throw New FormatException
End Select
If Not (TransitionTag Is Nothing) Then CType(TransitionTag, Commit)()
End Sub
Sub New(ByVal Stream As IO.Stream)
InitializeStates()
With New Automata.Automaton(States, AddressOf INIGroup, AddressOf INITransition)
.Run(Stream)
.Run(New System.IO.MemoryStream(Eol)) 'eof treated as eol
CommitSection()
End With
End Sub
Private Sub New(ByVal Sections As ArrayList)
InnerList.AddRange(CType(Sections.Clone, ArrayList))
End Sub
Default ReadOnly Property Section(ByVal Index As Integer) As Section
Get
Return CType(InnerList(Index), Section)
End Get
End Property
Default ReadOnly Property Section(ByVal Name As String) As Section
Get
Return CType(InnerList(InnerList.BinarySearch(New Section(Name))), Section)
End Get
End Property
End Class
End Namespace
Ещё есть то же самое на C#, может выложить в соответствующем ему разделе?
ЗЫ: правда что ли в C# нету опциональных параметров?