Quiz #4: Text Spintax

Erel

B4X founder
Staff member
Licensed User
Longtime User
Here is a recursive parser quiz.
The challenge (requested here) is to implement a text Spintax module.

Usage example:
B4X:
Sub Activity_Create(FirstTime As Boolean)
   Dim s As String = "{This {article|content} is {spun|spun-out}|Any {article|content} {can|could} be {spun|spun-out}}"
   Dim sentences As List = Spintax(s)
   For Each sentence As StringBuilder In sentences
      Log(sentence)
   Next
End Sub
The output:
This article is spun
This article is spun-out
This content is spun
This content is spun-out
Any article can be spun
Any article can be spun-out
Any article could be spun
Any article could be spun-out
Any content can be spun
Any content can be spun-out
Any content could be spun
Any content could be spun-out
Online Spintax tool: UberToolz : Output Content From Nested Spintax
What is Spintax: What is spintax

So are there any parser gurus here???
 

MLDev

Active Member
Licensed User
Longtime User
This won't output all possible sentences but it'll randomly output one.

B4X:
Dim st As String = "{Hey|Yo|Good day|Btw|Hiya|Heya|Howdy|Whats up|Hello|Hi|Guess what|Sup|} {Chung|Chen| Melton|Hill|Puckett|Song|Hamilton|Bender} your {funny|witty|strange|fantastic}"

Log(Spintax(st))
   
Sub Spintax(s As String)
   If s.Contains("{") Then
      Dim startBrace As Int = s.IndexOf("}")
      
      Do While s.CharAt(startBrace) <> "{"
         startBrace = startBrace - 1
      Loop
      
      Dim block As String = s.SubString2(startBrace, s.IndexOf("}") + 1)
      Dim items() As String
      items = Regex.Split("\|", block.SubString2(1, block.Length - 1))
      s = s.SubString2(0, s.IndexOf(block)) & items(Rnd(0, items.Length)) & s.SubString(s.IndexOf(block) + block.Length)
      
      Return Spintax(s)
   Else
      Return s
   End If
End Sub

I'm still working on getting it to output all.
 
Last edited:
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
Ok, found some time finally for this. I am on a non b4a PC, so here's an attempt in vb.
I didn't use indexOf or any sort of regEx, on purpose, since for me quizzes are fun, so I tried something more 'basic'. No time to catch 'faulty' user inputs, and probably the code has a lot of bugs but it did generate the output of the given sample. I think it can easily be translated to b4a :)
B4X:
Option Explicit
Private Sub Command1_Click()
Dim s As String
s = Text1.Text
spinTax (s)
End Sub

Public Function spinTax(s As String) As String

    Dim myL As Integer
    myL = Len(s)
    If myL = 0 Then
        spinTax = "nothing to spin"
    Else
        Dim cnt As Integer, s1(3) As String
        Dim spinStart As Integer, spinEnd As Integer, spinMid As Integer
        Dim countFirst As Integer
        cnt = 1
        Do Until cnt = myL + 1 Or spinEnd > 0
        Dim ch As String
        ch = Mid(s, cnt, 1)
        If ch = "{" And spinStart = 0 Then
            spinStart = cnt
        ElseIf ch = "{" Then
            countFirst = countFirst + 1
        ElseIf ch = "}" And countFirst = 0 Then
            spinEnd = cnt
        ElseIf ch = "}" Then
            countFirst = countFirst - 1
        ElseIf ch = "|" And countFirst = 0 Then
            spinMid = cnt
        End If
        cnt = cnt + 1
        Loop
        If spinStart > 0 Then
            s1(0) = Mid(s, 1, spinStart - 1)
            s1(1) = Mid(s, spinStart + 1, spinMid - 1 - spinStart)
            s1(2) = Mid(s, spinMid + 1, spinEnd - spinMid - 1)
            s1(3) = Mid(s, spinEnd + 1, myL - spinEnd)
            spinTax (s1(0) & s1(1) & s1(3))
            spinTax (s1(0) & s1(2) & s1(3))
        Else
            spinTax = s
            List1.AddItem spinTax
        End If
    End If
End Function
 

Attachments

  • spintax.PNG
    spintax.PNG
    15.1 KB · Views: 413
Last edited:
Upvote 0

MLDev

Active Member
Licensed User
Longtime User
I know, this is slow and ugly! :sign0013: But it works.

Right now it's returning a list of strings. I've never used StringBuilders.

B4X:
Sub Globals
   Dim counts As List
   Dim output As List
   Dim counts2(100) As Int
End Sub

Sub Activity_Create(FirstTime As Boolean)
   counts.Initialize
   output.Initialize

   'Dim st As String = "{Hey|Yo|Good day|Btw|Hiya|Heya|Howdy|Whats up|Hello|Hi|Guess what|Sup|} {Chung|Chen| Melton|Hill|Puckett|Song|Hamilton|Bender} your {funny|witty|strange|fantastic}"
   Dim st As String = "{This {article|content} is {spun|spun-out}|Any {article|content} {can|could} be {spun|spun-out}}"
   Dim sentences As List = Spintax(st)
   For Each sentence As String In sentences
      Log(sentence)
   Next
End Sub

Sub Spintax(s As String) As List
   firstSpintax(s)
   perm(s, counts.Size - 1)
   output.Sort(True)
   For i = output.Size - 1 To 1 Step -1
      If output.Get(i) = output.Get(i - 1) Then output.RemoveAt(i)
   Next
   
   Return output
End Sub

Sub perm(s As String, count As Int)
   If count = -1 Then
      output.Add(nextSpintax(0, s))
      Return
   Else
      For j = 0 To counts.Get(count) - 1
         counts2(count) = j
         perm(s, count - 1)
      Next
   End If
End Sub

Sub firstSpintax(s As String)
   If s.Contains("{") Then
      Dim startBrace As Int = s.IndexOf("}")
      
      Do While s.CharAt(startBrace) <> "{"
         startBrace = startBrace - 1
      Loop
      
      Dim block As String = s.SubString2(startBrace, s.IndexOf("}") + 1)
      Dim items() As String
      items = Regex.Split("\|", block.SubString2(1, block.Length - 1))
      counts.Add(items.Length)
      s = s.SubString2(0, s.IndexOf(block)) & items(0) & s.SubString(s.IndexOf(block) + block.Length)
      
      Return firstSpintax(s)
   Else
      Return s
   End If
End Sub

Sub nextSpintax(count As Int, s As String)
   If s.Contains("{") Then
      Dim startBrace As Int = S.IndexOf("}")
      
      Do While s.CharAt(startBrace) <> "{"
         startBrace = startBrace - 1
      Loop
      
      Dim block As String = S.SubString2(startBrace, S.IndexOf("}") + 1)
      Dim items() As String
      items = Regex.Split("\|", block.SubString2(1, block.Length - 1))
      s = s.SubString2(0, s.IndexOf(block)) & items(counts2(count)) & s.SubString(s.IndexOf(block) + block.Length)
      
      Return nextSpintax(count + 1, s)
   Else
      Return s
   End If
End Sub
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Good job!!! It wasn't an easy one. Here is my code:

B4X:
Sub Process_Globals

End Sub

Sub Globals
   Private stringIndex As Int
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Dim s As String = "aaa {This {article|content} is {spun|spun-out}|Any {article|content} {can|could} be {spun|spun-out}}"
   Dim sentences As List = Spintax(s)
   For Each sentence As StringBuilder In sentences
      Log(sentence)
   Next
End Sub

Sub Spintax(s As String) As List
   Dim result As List
   result.Initialize
   Dim currentSection As List
   currentSection.Initialize
   Dim sb As StringBuilder
   sb.Initialize
   Do While stringIndex < s.Length
      Dim c As Char = s.CharAt(stringIndex)
      stringIndex = stringIndex + 1
      Select c
         Case "{"
            currentSection = AddListToList(Spintax(s), currentSection)
         Case "|"
            result.AddAll(currentSection)
            currentSection.Clear
         Case "}"
            result.AddAll(currentSection)
            Return result
         Case Else
            AddStringToList(c, currentSection)
      End Select
   Loop
   result.AddAll(currentSection)
   Return result
End Sub

Sub AddListToList(newItems As List, items As List) As List
   Dim result As List
   result.Initialize
   If items.Size = 0 Then Return newItems
   For Each sb As StringBuilder In items
      For Each newSb As StringBuilder In newItems
         Dim n As StringBuilder
         n.Initialize
         n.Append(sb).Append(newSb)
         result.Add(n)
      Next
   Next
   Return result
End Sub

Sub AddStringToList(str As String, items As List)
   If items.Size = 0 Then
      Dim sb As StringBuilder
      sb.Initialize
      items.Add(sb)
   End If
   For Each sb As StringBuilder In items
      sb.Append(str)
   Next
End Sub
 
Upvote 0
Top