JSON serializer


SUBMITTED BY: Guest

DATE: Aug. 12, 2014, 1:08 p.m.

FORMAT: C#

SIZE: 36.4 kB

HITS: 23783

  1. 'Coded by: Electroalek<http://electroalek.com/>, 2014
  2. Imports System
  3. Imports System.Text
  4. Imports System.Xml.Serialization
  5. Imports System.Collections.Generic
  6. Imports System.Text.RegularExpressions
  7. Class JSONSerializer
  8. #Region "Deserializer"
  9. Public Shared Function Deserialize(ByVal input As String) As JSON
  10. Dim output As New JSON
  11. Dim Tree As ParseTree = New Parser(New Scanner).Parse(input)
  12. If Tree.Errors.Count = 0 Then
  13. For Each l As KeyValuePair(Of String, Object) In GetPairs(Tree.Nodes(0).Nodes(0).Nodes)
  14. output.Add(l.Key, l.Value)
  15. Next
  16. End If
  17. Return output
  18. End Function
  19. Private Shared Function GetPairs(ByVal nodes As List(Of ParseNode)) As JSON
  20. Dim output As New JSON
  21. For Each n As ParseNode In nodes
  22. Select Case n.Token.Type
  23. Case TokenType.PAIR
  24. output.Add(GetPair(n.Nodes))
  25. Case TokenType.BLOCK
  26. output.Add(GetPairs(n.Nodes))
  27. End Select
  28. Next
  29. Return output
  30. End Function
  31. Private Shared Function GetPair(ByVal nodes As List(Of ParseNode)) As KeyValuePair(Of String, Object)
  32. Dim key As String = String.Empty
  33. Dim value As Object = Nothing
  34. For Each n As ParseNode In nodes
  35. Select Case n.Token.Type
  36. Case TokenType.P_KEY
  37. key = RemoveQuotes(n.Nodes(0).Token.Text)
  38. Case TokenType.P_VALUE
  39. value = GetValue(n.Nodes)
  40. End Select
  41. Next
  42. Return New KeyValuePair(Of String, Object)(key, value)
  43. End Function
  44. Private Shared Function GetValue(ByVal nodes As List(Of ParseNode)) As Object
  45. Dim output As Object = Nothing
  46. For Each n As ParseNode In nodes
  47. Select Case n.Token.Type
  48. Case TokenType.VALUE
  49. Return GetValueType(n.Nodes)
  50. Case TokenType.BLOCK
  51. Return GetPairs(n.Nodes)
  52. End Select
  53. Next
  54. Return output
  55. End Function
  56. Private Shared Function GetValueType(ByVal nodes As List(Of ParseNode)) As Object
  57. For Each n As ParseNode In nodes
  58. Select Case n.Token.Type
  59. Case TokenType.T_STR
  60. Return RemoveQuotes(n.Token.Text)
  61. Case TokenType.T_INT
  62. Return Integer.Parse(n.Token.Text)
  63. Case TokenType.T_DBL
  64. Return Double.Parse(n.Token.Text)
  65. Case TokenType.T_BOOL
  66. Return Boolean.Parse(n.Token.Text)
  67. End Select
  68. Next
  69. Return Nothing
  70. End Function
  71. #Region "TinyPG"
  72. '<% @TinyPG Language="vb" %>
  73. 'T_INT -> @"(?!,)([+-]*[0-9]+)(?<!,)";
  74. 'T_DBL -> @"(?!,)([+-]*[0-9]+[.,][0-9]+)(?<!,)";
  75. 'T_STR -> @"""(.*?)""";
  76. 'T_BOOL -> @"True|False";
  77. 'C_OPEN -> @"\{|\[";
  78. 'C_CLOSE -> @"\}|\]";
  79. 'C_COMMA -> @"\,";
  80. 'C_COLON -> @"\:";
  81. 'EOF -> @"^$";
  82. '[Skip] WS -> @"(\s|\t)+";
  83. 'Start -> BLOCK EOF;
  84. 'P_KEY -> T_STR;
  85. 'P_VALUE -> VALUE | BLOCK;
  86. 'PAIR -> P_KEY C_COLON P_VALUE;
  87. 'BLOCK -> C_OPEN (PAIR | BLOCK | VALUE | C_COLON | C_COMMA)* C_CLOSE;
  88. 'VALUE -> T_STR | T_INT | T_DBL | T_BOOL;
  89. Private Enum TokenType
  90. 'Non terminal tokens:
  91. _NONE_ = 0
  92. _UNDETERMINED_ = 1
  93. 'Non terminal tokens:
  94. Start = 2
  95. P_KEY = 3
  96. P_VALUE = 4
  97. PAIR = 5
  98. BLOCK = 6
  99. VALUE = 7
  100. 'Terminal tokens:
  101. T_INT = 8
  102. T_DBL = 9
  103. T_STR = 10
  104. T_BOOL = 11
  105. C_OPEN = 12
  106. C_CLOSE = 13
  107. C_COMMA = 14
  108. C_COLON = 15
  109. EOF = 16
  110. WS = 17
  111. End Enum
  112. Private Class Parser
  113. Private m_scanner As Scanner
  114. Private m_tree As ParseTree
  115. Public Sub New(ByVal scanner As Scanner)
  116. m_scanner = scanner
  117. End Sub
  118. Public Function Parse(ByVal input As String) As ParseTree
  119. m_tree = New ParseTree()
  120. Return Parse(input, m_tree)
  121. End Function
  122. Public Function Parse(ByVal input As String, ByVal tree As ParseTree) As ParseTree
  123. m_scanner.Init(input)
  124. m_tree = tree
  125. ParseStart(m_tree)
  126. m_tree.Skipped = m_scanner.Skipped
  127. Return m_tree
  128. End Function
  129. Private Sub ParseStart(ByVal parent As ParseNode)
  130. Dim tok As Token
  131. Dim n As ParseNode
  132. Dim node As ParseNode = parent.CreateNode(m_scanner.GetToken(TokenType.Start), "Start")
  133. parent.Nodes.Add(node)
  134. ParseBLOCK(node)
  135. tok = m_scanner.Scan(TokenType.EOF)
  136. n = node.CreateNode(tok, tok.ToString())
  137. node.Token.UpdateRange(tok)
  138. node.Nodes.Add(n)
  139. If tok.Type <> TokenType.EOF Then
  140. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.EOF.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  141. Return
  142. End If
  143. parent.Token.UpdateRange(node.Token)
  144. End Sub
  145. Private Sub ParseP_KEY(ByVal parent As ParseNode)
  146. Dim tok As Token
  147. Dim n As ParseNode
  148. Dim node As ParseNode = parent.CreateNode(m_scanner.GetToken(TokenType.P_KEY), "P_KEY")
  149. parent.Nodes.Add(node)
  150. tok = m_scanner.Scan(TokenType.T_STR)
  151. n = node.CreateNode(tok, tok.ToString())
  152. node.Token.UpdateRange(tok)
  153. node.Nodes.Add(n)
  154. If tok.Type <> TokenType.T_STR Then
  155. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.T_STR.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  156. Return
  157. End If
  158. parent.Token.UpdateRange(node.Token)
  159. End Sub
  160. Private Sub ParseP_VALUE(ByVal parent As ParseNode)
  161. Dim tok As Token
  162. Dim node As ParseNode = parent.CreateNode(m_scanner.GetToken(TokenType.P_VALUE), "P_VALUE")
  163. parent.Nodes.Add(node)
  164. tok = m_scanner.LookAhead(TokenType.T_STR, TokenType.T_INT, TokenType.T_DBL, TokenType.T_BOOL, TokenType.C_OPEN)
  165. Select Case tok.Type
  166. Case TokenType.T_STR
  167. ParseVALUE(node)
  168. Case TokenType.T_INT
  169. ParseVALUE(node)
  170. Case TokenType.T_DBL
  171. ParseVALUE(node)
  172. Case TokenType.T_BOOL
  173. ParseVALUE(node)
  174. Case TokenType.C_OPEN
  175. ParseBLOCK(node)
  176. Case Else
  177. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found.", &H2, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  178. Exit Select
  179. End Select
  180. parent.Token.UpdateRange(node.Token)
  181. End Sub
  182. Private Sub ParsePAIR(ByVal parent As ParseNode)
  183. Dim tok As Token
  184. Dim n As ParseNode
  185. Dim node As ParseNode = parent.CreateNode(m_scanner.GetToken(TokenType.PAIR), "PAIR")
  186. parent.Nodes.Add(node)
  187. ParseP_KEY(node)
  188. tok = m_scanner.Scan(TokenType.C_COLON)
  189. n = node.CreateNode(tok, tok.ToString())
  190. node.Token.UpdateRange(tok)
  191. node.Nodes.Add(n)
  192. If tok.Type <> TokenType.C_COLON Then
  193. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.C_COLON.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  194. Return
  195. End If
  196. ParseP_VALUE(node)
  197. parent.Token.UpdateRange(node.Token)
  198. End Sub
  199. Private Sub ParseBLOCK(ByVal parent As ParseNode)
  200. Dim tok As Token
  201. Dim n As ParseNode
  202. Dim node As ParseNode = parent.CreateNode(m_scanner.GetToken(TokenType.BLOCK), "BLOCK")
  203. parent.Nodes.Add(node)
  204. tok = m_scanner.Scan(TokenType.C_OPEN)
  205. n = node.CreateNode(tok, tok.ToString())
  206. node.Token.UpdateRange(tok)
  207. node.Nodes.Add(n)
  208. If tok.Type <> TokenType.C_OPEN Then
  209. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.C_OPEN.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  210. Return
  211. End If
  212. tok = m_scanner.LookAhead(TokenType.T_STR, TokenType.C_OPEN, TokenType.T_INT, TokenType.T_DBL, TokenType.T_BOOL, TokenType.C_COLON, TokenType.C_COMMA)
  213. While tok.Type = TokenType.T_STR Or tok.Type = TokenType.C_OPEN Or tok.Type = TokenType.T_INT Or tok.Type = TokenType.T_DBL Or tok.Type = TokenType.T_BOOL Or tok.Type = TokenType.C_COLON Or tok.Type = TokenType.C_COMMA
  214. tok = m_scanner.LookAhead(TokenType.T_STR, TokenType.C_OPEN, TokenType.T_INT, TokenType.T_DBL, TokenType.T_BOOL, TokenType.C_COLON, TokenType.C_COMMA)
  215. Select Case tok.Type
  216. Case TokenType.T_STR
  217. ParsePAIR(node)
  218. Case TokenType.C_OPEN
  219. ParseBLOCK(node)
  220. Case TokenType.T_STR
  221. ParseVALUE(node)
  222. Case TokenType.T_INT
  223. ParseVALUE(node)
  224. Case TokenType.T_DBL
  225. ParseVALUE(node)
  226. Case TokenType.T_BOOL
  227. ParseVALUE(node)
  228. Case TokenType.C_COLON
  229. tok = m_scanner.Scan(TokenType.C_COLON)
  230. n = node.CreateNode(tok, tok.ToString())
  231. node.Token.UpdateRange(tok)
  232. node.Nodes.Add(n)
  233. If tok.Type <> TokenType.C_COLON Then
  234. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.C_COLON.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  235. Return
  236. End If
  237. Case TokenType.C_COMMA
  238. tok = m_scanner.Scan(TokenType.C_COMMA)
  239. n = node.CreateNode(tok, tok.ToString())
  240. node.Token.UpdateRange(tok)
  241. node.Nodes.Add(n)
  242. If tok.Type <> TokenType.C_COMMA Then
  243. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.C_COMMA.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  244. Return
  245. End If
  246. Case Else
  247. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found.", &H2, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  248. Exit Select
  249. End Select
  250. tok = m_scanner.LookAhead(TokenType.T_STR, TokenType.C_OPEN, TokenType.T_INT, TokenType.T_DBL, TokenType.T_BOOL, TokenType.C_COLON, TokenType.C_COMMA)
  251. End While
  252. tok = m_scanner.Scan(TokenType.C_CLOSE)
  253. n = node.CreateNode(tok, tok.ToString())
  254. node.Token.UpdateRange(tok)
  255. node.Nodes.Add(n)
  256. If tok.Type <> TokenType.C_CLOSE Then
  257. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.C_CLOSE.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  258. Return
  259. End If
  260. parent.Token.UpdateRange(node.Token)
  261. End Sub
  262. Private Sub ParseVALUE(ByVal parent As ParseNode)
  263. Dim tok As Token
  264. Dim n As ParseNode
  265. Dim node As ParseNode = parent.CreateNode(m_scanner.GetToken(TokenType.VALUE), "VALUE")
  266. parent.Nodes.Add(node)
  267. tok = m_scanner.LookAhead(TokenType.T_STR, TokenType.T_INT, TokenType.T_DBL, TokenType.T_BOOL)
  268. Select Case tok.Type
  269. Case TokenType.T_STR
  270. tok = m_scanner.Scan(TokenType.T_STR)
  271. n = node.CreateNode(tok, tok.ToString())
  272. node.Token.UpdateRange(tok)
  273. node.Nodes.Add(n)
  274. If tok.Type <> TokenType.T_STR Then
  275. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.T_STR.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  276. Return
  277. End If
  278. Case TokenType.T_INT
  279. tok = m_scanner.Scan(TokenType.T_INT)
  280. n = node.CreateNode(tok, tok.ToString())
  281. node.Token.UpdateRange(tok)
  282. node.Nodes.Add(n)
  283. If tok.Type <> TokenType.T_INT Then
  284. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.T_INT.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  285. Return
  286. End If
  287. Case TokenType.T_DBL
  288. tok = m_scanner.Scan(TokenType.T_DBL)
  289. n = node.CreateNode(tok, tok.ToString())
  290. node.Token.UpdateRange(tok)
  291. node.Nodes.Add(n)
  292. If tok.Type <> TokenType.T_DBL Then
  293. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.T_DBL.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  294. Return
  295. End If
  296. Case TokenType.T_BOOL
  297. tok = m_scanner.Scan(TokenType.T_BOOL)
  298. n = node.CreateNode(tok, tok.ToString())
  299. node.Token.UpdateRange(tok)
  300. node.Nodes.Add(n)
  301. If tok.Type <> TokenType.T_BOOL Then
  302. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found. Expected " + TokenType.T_BOOL.ToString(), &H1001, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  303. Return
  304. End If
  305. Case Else
  306. m_tree.Errors.Add(New ParseError("Unexpected token '" + tok.Text.Replace("\n", "") + "' found.", &H2, 0, tok.StartPos, tok.StartPos, tok.EndPos - tok.StartPos))
  307. Exit Select
  308. End Select
  309. parent.Token.UpdateRange(node.Token)
  310. End Sub
  311. End Class
  312. Private Class Scanner
  313. Public Input As String
  314. Public StartPos As Integer = 0
  315. Public EndPos As Integer = 0
  316. Public CurrentLine As Integer
  317. Public CurrentColumn As Integer
  318. Public CurrentPosition As Integer
  319. Public Skipped As List(Of Token)
  320. Public Patterns As Dictionary(Of TokenType, Regex)
  321. Private LookAheadToken As Token
  322. Private Tokens As List(Of TokenType)
  323. Private SkipList As List(Of TokenType)
  324. Public Sub New()
  325. Dim regex As Regex
  326. Patterns = New Dictionary(Of TokenType, Regex)()
  327. Tokens = New List(Of TokenType)()
  328. LookAheadToken = Nothing
  329. Skipped = New List(Of Token)()
  330. SkipList = New List(Of TokenType)()
  331. SkipList.Add(TokenType.WS)
  332. regex = New Regex("(?!,)([+-]*[0-9]+)(?<!,)", RegexOptions.Compiled)
  333. Patterns.Add(TokenType.T_INT, regex)
  334. Tokens.Add(TokenType.T_INT)
  335. regex = New Regex("(?!,)([+-]*[0-9]+[.,][0-9]+)(?<!,)", RegexOptions.Compiled)
  336. Patterns.Add(TokenType.T_DBL, regex)
  337. Tokens.Add(TokenType.T_DBL)
  338. regex = New Regex("""(.*?)""", RegexOptions.Compiled)
  339. Patterns.Add(TokenType.T_STR, regex)
  340. Tokens.Add(TokenType.T_STR)
  341. regex = New Regex("True|False", RegexOptions.Compiled)
  342. Patterns.Add(TokenType.T_BOOL, regex)
  343. Tokens.Add(TokenType.T_BOOL)
  344. regex = New Regex("\{|\[", RegexOptions.Compiled)
  345. Patterns.Add(TokenType.C_OPEN, regex)
  346. Tokens.Add(TokenType.C_OPEN)
  347. regex = New Regex("\}|\]", RegexOptions.Compiled)
  348. Patterns.Add(TokenType.C_CLOSE, regex)
  349. Tokens.Add(TokenType.C_CLOSE)
  350. regex = New Regex("\,", RegexOptions.Compiled)
  351. Patterns.Add(TokenType.C_COMMA, regex)
  352. Tokens.Add(TokenType.C_COMMA)
  353. regex = New Regex("\:", RegexOptions.Compiled)
  354. Patterns.Add(TokenType.C_COLON, regex)
  355. Tokens.Add(TokenType.C_COLON)
  356. regex = New Regex("^$", RegexOptions.Compiled)
  357. Patterns.Add(TokenType.EOF, regex)
  358. Tokens.Add(TokenType.EOF)
  359. regex = New Regex("(\s|\t)+", RegexOptions.Compiled)
  360. Patterns.Add(TokenType.WS, regex)
  361. Tokens.Add(TokenType.WS)
  362. End Sub
  363. Public Sub Init(ByVal input As String)
  364. Me.Input = input
  365. StartPos = 0
  366. EndPos = 0
  367. CurrentLine = 0
  368. CurrentColumn = 0
  369. CurrentPosition = 0
  370. Skipped = New List(Of Token)()
  371. LookAheadToken = Nothing
  372. End Sub
  373. Public Function GetToken(ByVal type As TokenType) As Token
  374. Dim t As New Token(Me.StartPos, Me.EndPos)
  375. t.Type = type
  376. Return t
  377. End Function
  378. Public Function Scan(ByVal ParamArray expectedtokens As TokenType()) As Token
  379. Dim tok As Token = LookAhead(expectedtokens)
  380. ' temporarely retrieve the lookahead
  381. LookAheadToken = Nothing
  382. ' reset lookahead token, so scanning will continue
  383. StartPos = tok.EndPos
  384. EndPos = tok.EndPos
  385. ' set the tokenizer to the new scan position
  386. Return tok
  387. End Function
  388. Public Function LookAhead(ByVal ParamArray expectedtokens As TokenType()) As Token
  389. Dim i As Integer
  390. Dim start As Integer = StartPos
  391. Dim tok As Token = Nothing
  392. Dim scantokens As List(Of TokenType)
  393. ' this prevents double scanning and matching
  394. ' increased performance
  395. If LookAheadToken IsNot Nothing AndAlso LookAheadToken.Type <> TokenType._UNDETERMINED_ AndAlso LookAheadToken.Type <> TokenType._NONE_ Then
  396. Return LookAheadToken
  397. End If
  398. If expectedtokens.Length = 0 Then
  399. scantokens = Tokens
  400. Else
  401. scantokens = New List(Of TokenType)(expectedtokens)
  402. scantokens.AddRange(SkipList)
  403. End If
  404. Do
  405. Dim len As Integer = -1
  406. Dim index As TokenType = Integer.MaxValue
  407. Dim m_input As String = Input.Substring(start)
  408. tok = New Token(start, EndPos)
  409. For i = 0 To scantokens.Count - 1
  410. Dim r As Regex = Patterns(scantokens(i))
  411. Dim m As Match = r.Match(m_input)
  412. If m.Success AndAlso m.Index = 0 AndAlso ((m.Length > len) OrElse (scantokens(i) < index AndAlso m.Length = len)) Then
  413. len = m.Length
  414. index = scantokens(i)
  415. End If
  416. Next i
  417. If index >= 0 AndAlso len >= 0 Then
  418. tok.EndPos = start + len
  419. tok.Text = Input.Substring(tok.StartPos, len)
  420. tok.Type = index
  421. Else
  422. If tok.StartPos < tok.EndPos - 1 Then
  423. tok.Text = Input.Substring(tok.StartPos, 1)
  424. End If
  425. End If
  426. If SkipList.Contains(tok.Type) Then
  427. start = tok.EndPos
  428. Skipped.Add(tok)
  429. Else
  430. tok.Skipped = Skipped
  431. Skipped = New List(Of Token)
  432. End If
  433. Loop While SkipList.Contains(tok.Type)
  434. LookAheadToken = tok
  435. Return tok
  436. End Function
  437. End Class
  438. Private Class Token
  439. Private m_startPos As Integer
  440. Private m_endPos As Integer
  441. Private m_text As String
  442. Private m_value As Object
  443. Private m_skipped As List(Of Token)
  444. Public Property StartPos() As Integer
  445. Get
  446. Return m_startPos
  447. End Get
  448. Set(ByVal value As Integer)
  449. m_startPos = value
  450. End Set
  451. End Property
  452. Public Property EndPos() As Integer
  453. Get
  454. Return m_endPos
  455. End Get
  456. Set(ByVal value As Integer)
  457. m_endPos = value
  458. End Set
  459. End Property
  460. Public ReadOnly Property Length() As Integer
  461. Get
  462. Return m_endPos - m_startPos
  463. End Get
  464. End Property
  465. Public Property Text() As String
  466. Get
  467. Return m_text
  468. End Get
  469. Set(ByVal value As String)
  470. m_text = value
  471. End Set
  472. End Property
  473. Public Property Skipped() As List(Of Token)
  474. Get
  475. Return m_skipped
  476. End Get
  477. Set(ByVal value As List(Of Token))
  478. m_skipped = value
  479. End Set
  480. End Property
  481. Public Property Value() As Object
  482. Get
  483. Return m_value
  484. End Get
  485. Set(ByVal value As Object)
  486. Me.m_value = value
  487. End Set
  488. End Property
  489. <XmlAttribute()> _
  490. Public Type As TokenType
  491. Public Sub New()
  492. Me.New(0, 0)
  493. End Sub
  494. Public Sub New(ByVal start As Integer, ByVal endPos As Integer)
  495. Type = TokenType._UNDETERMINED_
  496. m_startPos = start
  497. m_endPos = endPos
  498. Text = ""
  499. ' must initialize with empty string, may cause null reference exceptions otherwise
  500. Value = Nothing
  501. End Sub
  502. Public Sub UpdateRange(ByVal token As Token)
  503. If token.StartPos < m_startPos Then
  504. m_startPos = token.StartPos
  505. End If
  506. If token.EndPos > m_endPos Then
  507. m_endPos = token.EndPos
  508. End If
  509. End Sub
  510. Public Overloads Overrides Function ToString() As String
  511. If Text <> Nothing Then
  512. Return Type.ToString() + " '" + Text + "'"
  513. Else
  514. Return Type.ToString()
  515. End If
  516. End Function
  517. End Class
  518. <Serializable()> _
  519. Private Class ParseErrors
  520. Inherits List(Of ParseError)
  521. Public Sub New()
  522. End Sub
  523. End Class
  524. <Serializable()> _
  525. Private Class ParseError
  526. Private m_message As String
  527. Private m_code As Integer
  528. Private m_line As Integer
  529. Private m_col As Integer
  530. Private m_pos As Integer
  531. Private m_length As Integer
  532. Public ReadOnly Property Code() As Integer
  533. Get
  534. Return m_code
  535. End Get
  536. End Property
  537. Public ReadOnly Property Line() As Integer
  538. Get
  539. Return m_line
  540. End Get
  541. End Property
  542. Public ReadOnly Property Column() As Integer
  543. Get
  544. Return m_col
  545. End Get
  546. End Property
  547. Public ReadOnly Property Position() As Integer
  548. Get
  549. Return m_pos
  550. End Get
  551. End Property
  552. Public ReadOnly Property Length() As Integer
  553. Get
  554. Return m_length
  555. End Get
  556. End Property
  557. Public ReadOnly Property Message() As String
  558. Get
  559. Return m_message
  560. End Get
  561. End Property
  562. Public Sub New(ByVal message As String, ByVal code As Integer, ByVal node As ParseNode)
  563. Me.New(message, code, 0, node.Token.StartPos, node.Token.StartPos, node.Token.Length)
  564. End Sub
  565. Public Sub New(ByVal message As String, ByVal code As Integer, ByVal line As Integer, ByVal col As Integer, ByVal pos As Integer, ByVal length As Integer)
  566. m_message = message
  567. m_code = code
  568. m_line = line
  569. m_col = col
  570. m_pos = pos
  571. m_length = length
  572. End Sub
  573. End Class
  574. <Serializable()> _
  575. Private Class ParseTree
  576. Inherits ParseNode
  577. Public Errors As ParseErrors
  578. Public Skipped As List(Of Token)
  579. Public Sub New()
  580. MyBase.New(New Token(), "ParseTree")
  581. Token.Type = TokenType.Start
  582. Token.Text = "Root"
  583. Skipped = New List(Of Token)()
  584. Errors = New ParseErrors()
  585. End Sub
  586. Public Function PrintTree() As String
  587. Dim sb As New StringBuilder()
  588. Dim indent As Integer = 0
  589. PrintNode(sb, Me, indent)
  590. Return sb.ToString()
  591. End Function
  592. Private Sub PrintNode(ByVal sb As StringBuilder, ByVal node As ParseNode, ByVal indent As Integer)
  593. Dim space As String = "".PadLeft(indent, " "c)
  594. sb.Append(space)
  595. sb.AppendLine(node.Text)
  596. For Each n As ParseNode In node.Nodes
  597. PrintNode(sb, n, indent + 2)
  598. Next
  599. End Sub
  600. Public Overloads Function Eval(ByVal ParamArray paramlist As Object()) As Object
  601. Return Nodes(0).Eval(Me, paramlist)
  602. End Function
  603. End Class
  604. <Serializable()>
  605. <XmlInclude(GetType(ParseTree))>
  606. Private Class ParseNode
  607. Protected m_text As String
  608. Protected m_nodes As List(Of ParseNode)
  609. Public ReadOnly Property Nodes() As List(Of ParseNode)
  610. Get
  611. Return m_nodes
  612. End Get
  613. End Property
  614. <XmlIgnore()> _
  615. Public Parent As ParseNode
  616. Public Token As Token
  617. <XmlIgnore()> _
  618. Public Property Text() As String
  619. ' text to display in parse tree
  620. Get
  621. Return m_text
  622. End Get
  623. Set(ByVal value As String)
  624. m_text = value
  625. End Set
  626. End Property
  627. Public Overridable Function CreateNode(ByVal token As Token, ByVal text As String) As ParseNode
  628. Dim node As New ParseNode(token, text)
  629. node.Parent = Me
  630. Return node
  631. End Function
  632. Protected Sub New(ByVal token As Token, ByVal text As String)
  633. Me.Token = token
  634. m_text = text
  635. m_nodes = New List(Of ParseNode)()
  636. End Sub
  637. Protected Function GetValue(ByVal tree As ParseTree, ByVal type As TokenType, ByVal index As Integer) As Object
  638. Return GetValueByRef(tree, type, index)
  639. End Function
  640. Protected Function GetValueByRef(ByVal tree As ParseTree, ByVal type As TokenType, ByRef index As Integer) As Object
  641. Dim o As Object = Nothing
  642. If index < 0 Then
  643. Return o
  644. End If
  645. ' left to right
  646. For Each node As ParseNode In nodes
  647. If node.Token.Type = type Then
  648. System.Math.Max(System.Threading.Interlocked.Decrement(index), index + 1)
  649. If index < 0 Then
  650. o = node.Eval(tree)
  651. Exit For
  652. End If
  653. End If
  654. Next
  655. Return o
  656. End Function
  657. Friend Function Eval(ByVal tree As ParseTree, ByVal ParamArray paramlist As Object()) As Object
  658. Dim Value As Object = Nothing
  659. Select Case Token.Type
  660. Case TokenType.Start
  661. Value = EvalStart(tree, paramlist)
  662. Exit Select
  663. Case TokenType.P_KEY
  664. Value = EvalP_KEY(tree, paramlist)
  665. Exit Select
  666. Case TokenType.P_VALUE
  667. Value = EvalP_VALUE(tree, paramlist)
  668. Exit Select
  669. Case TokenType.PAIR
  670. Value = EvalPAIR(tree, paramlist)
  671. Exit Select
  672. Case TokenType.BLOCK
  673. Value = EvalBLOCK(tree, paramlist)
  674. Exit Select
  675. Case TokenType.VALUE
  676. Value = EvalVALUE(tree, paramlist)
  677. Exit Select
  678. Case Else
  679. Value = Token.Text
  680. Exit Select
  681. End Select
  682. Return Value
  683. End Function
  684. Protected Overridable Function EvalStart(ByVal tree As ParseTree, ByVal ParamArray paramlist As Object()) As Object
  685. Return "Could not interpret input; no semantics implemented."
  686. End Function
  687. Protected Overridable Function EvalP_KEY(ByVal tree As ParseTree, ByVal ParamArray paramlist As Object()) As Object
  688. Throw New NotImplementedException()
  689. End Function
  690. Protected Overridable Function EvalP_VALUE(ByVal tree As ParseTree, ByVal ParamArray paramlist As Object()) As Object
  691. Throw New NotImplementedException()
  692. End Function
  693. Protected Overridable Function EvalPAIR(ByVal tree As ParseTree, ByVal ParamArray paramlist As Object()) As Object
  694. Throw New NotImplementedException()
  695. End Function
  696. Protected Overridable Function EvalBLOCK(ByVal tree As ParseTree, ByVal ParamArray paramlist As Object()) As Object
  697. Throw New NotImplementedException()
  698. End Function
  699. Protected Overridable Function EvalVALUE(ByVal tree As ParseTree, ByVal ParamArray paramlist As Object()) As Object
  700. Throw New NotImplementedException()
  701. End Function
  702. End Class
  703. #End Region
  704. #End Region
  705. #Region "Serializer"
  706. Public Shared Function Serialize(ByVal data As JSON) As String
  707. Dim sb As New StringBuilder
  708. Dim t As Type = Nothing
  709. sb.Append("{")
  710. For Each i As KeyValuePair(Of String, Object) In data
  711. t = i.Value.GetType
  712. sb.Append(String.Format("""{0}"":", i.Key))
  713. If IsNumeric(t) Or t Is GetType(Boolean) Then
  714. sb.Append(i.Value.ToString)
  715. ElseIf t Is GetType(JSON) Then
  716. sb.Append(Serialize(i.Value))
  717. Else
  718. sb.Append(String.Format("""{0}""", i.Value.ToString))
  719. End If
  720. sb.Append(",")
  721. Next
  722. sb.Remove(sb.Length - 1, 1)
  723. sb.Append("}")
  724. Return sb.ToString
  725. End Function
  726. #End Region
  727. #Region "Helpers"
  728. Public Class JSON
  729. Inherits Dictionary(Of String, Object)
  730. Private id As Integer = 0
  731. Public Overloads Sub Add(ByVal key As String, ByVal value As Object)
  732. If value Is Nothing Then value = String.Empty
  733. If MyBase.ContainsKey(key) Then
  734. MyBase.Item(key) = value
  735. Else
  736. MyBase.Add(key, value)
  737. End If
  738. End Sub
  739. Public Overloads Sub Add(ByVal item As KeyValuePair(Of String, Object))
  740. Add(item.Key, item.Value)
  741. End Sub
  742. Public Overloads Sub Add(ByVal value As Object)
  743. Add(id, value)
  744. id += 1
  745. End Sub
  746. Public Overloads Sub Remove(ByVal key As String)
  747. MyBase.Remove(key)
  748. id -= 1
  749. End Sub
  750. End Class
  751. Private Shared Function RemoveQuotes(ByVal t As String) As String
  752. If t(0) = """" Then t = t.Substring(1, t.Length - 1)
  753. If t(t.Length - 1) = """" Then t = t.Substring(0, t.Length - 1)
  754. Return t
  755. End Function
  756. Private Shared Function IsNumeric(ByVal t As Type) As Boolean
  757. Select Case Type.GetTypeCode(t)
  758. Case TypeCode.Byte,
  759. TypeCode.Decimal,
  760. TypeCode.Double,
  761. TypeCode.Int16,
  762. TypeCode.Int32,
  763. TypeCode.Int64,
  764. TypeCode.SByte,
  765. TypeCode.Single,
  766. TypeCode.UInt16,
  767. TypeCode.UInt32,
  768. TypeCode.UInt64
  769. Return True
  770. End Select
  771. Return False
  772. End Function
  773. #End Region
  774. End Class

comments powered by Disqus