Code Generator via XML Dateien

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

Firstdeathmaker

Betreff: Code Generator via XML Dateien

BeitragSa, März 07, 2009 17:42
Antworten mit Zitat
Benutzer-Profile anzeigen
Der CodeGenerator erzeugt aus XML Dateien schnell Blitzmax Sourcecode. Das besondere daran ist, dass er automatisch Getter&Setter für Field's erzeugen kann, und auch Verknüpfungen zwischen zwei Objekten erzeugen sowie zerstören und einen kompletten Destruktor anlegen kann.

Näheres dazu in den paar Tut1-4.xml Dateien, in welchen ich ein paar Beispiele gemacht habe und welche der Downloadversion beiliegen. Zum ausführen die CodeGenerator.exe starten und den Dateinamen der zu übersetzenden xml-Datei eintippen (inklusive Dateiendung).

Als Verknüpfungen zwischen 2 Objekten habe ich bisher:
Ein Objekt verweist auf ein Anderes (a.b->b) linkType="mono121"
Beide Objekte verweisen auf das Gegenüber (a.b->b b.a->a) linkType="bi121"
Viele Objekte verweisen auf eines (a.d->d, b.d->d. c.d->d) (Per Linked List) linkType="monoM12"

Man kann damit zwar nicht alles machen, aber man kann sich am Anfang eine Menge Tipparbeit sparen.


<Hier> gibt es eine kompilierte Windows Version mit den Tutorial Beispielen und dem Source.

Achtung, zum kompilieren dieses Codes braucht man das libxml mod von bruce, das man sich <hier> runterladen kann.


Code: [AUSKLAPPEN]
'This BMX file was edited with BLIde ( http://www.blide.org )
SuperStrict
Import bah.libxml

Local cg:TCodeGenerator = New TCodeGenerator
Local file:String = Input("File to parse: ")
cg.start(file)
Print "<Program shuts down in 3 sec>"
Delay 3000
End


Type TCodeGenerator
   Field out:TStream
   Field default_out:String
   Field types:TMap
   
   Method New()
      types = CreateMap()
      default_out = "Default.bmx"
   End Method

   Method start(filename:String)
      Print "Start processing file: " + filename
      Local doc:TxmlDoc = TxmlDoc.parseFile(filename)
      If doc = Null Print "Could not parse document";Return
      Local node:TxmlNode = doc.getRootElement()
      If node = Null Print "Could not get a root element"; Return
      out = WriteFile(node.getAttribute("out"))
       If out = Null out = WriteFile(default_out)
      Parse(node)
      doc.free()
      write()
      CloseFile(out)
      Print "Successfully finished"
   End Method
   
   Method parse(node:TxmlNode)
      If node.getName() <> "CodeGenerator" Return
      
      Local children:TList = node.getChildren()
      If children = Null Return
      For Local c:TxmlNode = EachIn children
         Select c.getName()
            Case "Type" parseType(c)
            Case "Connect" parseConnection(c)
         End Select
      Next
   End Method
   
   Method write()
      For Local t:DType = EachIn MapValues(Self.types)
         t.write(out)
      Next
   End Method
   
   Method parseType:DType(node:TxmlNode)
      Local t:DType
      If node.getName() <> "Type" Return Null

      Local id:String = node.getAttribute("id")
      Local bbdoc:Byte = Byte(node.getAttribute("bbdoc"))
      t = DType.Create(id)
      t.bbdoc = bbdoc
      addType(t)

      Local children:TList = node.getChildren()
      If children = Null Return t
      For Local c:TxmlNode = EachIn children
         Select c.getName()
            Case "Field"
               parseField(c, t)
         End Select
      Next
      Return t
   End Method
   
   Method addType(t:DType)
      MapInsert(types, t.id, t)
   End Method
   
   Method getType:DType(id:String)
      Return DType(MapValueForKey(types, id))
   End Method
   
   Method parseField(node:TxmlNode, t:DType)
      Local f:DField
      If node.getName() <> "Field" Return
      
      Local id:String = node.getAttribute("id")
      Local datatype:String = node.getAttribute("datatype")
      Local val:String = node.getAttribute("value")
      Local set:Byte = Byte(node.getAttribute("setter"))
      Local get:Byte = Byte(node.getAttribute("getter"))
      
      f = DField.Create(id, datatype, val)
      t.addVar(f)
      
      If set
         Local setter:DMethod = New DMethod
         setter.setId("set" + Upper(Left(id, 1)) + Mid(id, 2))
         setter.setBBdocText("setter for " + f.id + ":" + f.datatype)
         setter.addInput("val:" + f.datatype)
         setter.addContent("Self." + f.id + " = " + "val")
         t.addMethod(setter)
      End If
      
      If get
         Local getter:DMethod = New DMethod
         getter.setId("get" + Upper(Left(id, 1)) + Mid(id, 2))
         getter.setBBdocText("getter for " + f.id + ":" + f.datatype)
         getter.setOutput(f.datatype)
         getter.addContent("Return " + f.id)
         t.addMethod(getter)
      EndIf
      
      Local children:TList = node.getChildren()
      If children = Null Return
      For Local c:TxmlNode = EachIn children
         Select c.getName()
         End Select
      Next
   End Method
   
   Method parseConnection(node:TxmlNode)
      If node.getName() <> "Connect" Return
      
      Local type1:DType = Self.getType("T" + node.getAttribute("type1"))
      Local type2:DType = Self.getType("T" + node.getAttribute("type2"))
      
      Local t1Name:String = node.getAttribute("link1Id")
      Local t2Name:String = node.getAttribute("link2Id")
      
      If t1Name = "" t1Name:String = Mid(type1.id, 2)
      If t2Name = "" t2Name:String = Mid(type2.id, 2)
      
      Local linkType:String = node.getAttribute("linkType")
      
      
      
      If linkType = "bi121"
         bidirectionalOneToOne(type1:DType, type2:DType, t1Name:String, t2Name:String)
      ElseIf linkType = "mono121"
         monoDirectionalOneToOne(type1:DType, type2:DType, t1Name:String, t2Name:String)
      ElseIf linkType = "monoM21"
         monoDirectionalManyToOne(type1:DType, type2:DType, t1Name:String, t2Name:String)
      EndIf
      
      
      Local children:TList = node.getChildren()
      If children = Null Return
      For Local c:TxmlNode = EachIn children
      Next
   End Method
   
   Method bidirectionalOneToOne(type1:DType, type2:DType, t1Name:String, t2Name:String)
         Local linkVarName:String = Lower(t2Name) + "Link"
         Local m1Linker:DMethod = New DMethod
         Local m1LinkRemover:DMethod = New DMethod
         
         If t1Name = "" t1Name:String = Mid(type1.id, 2)
         If t2Name = "" t2Name:String = Mid(type2.id, 2)
         
         type1.addLinkVar(DField.Create(linkVarName, type2.id))
         m1Linker.setId("set" + t2Name)
         m1Linker.addInput("obj:" + type2.id)
         m1Linker.addInput("call:byte = true")
         m1Linker.addContent("if call obj.set" + t1Name + "(Self,false)")
         m1Linker.addContent(linkVarName + " = obj")
         type1.addMethod(m1Linker)
         
         m1LinkRemover.setId("remove" + t2Name)
         m1LinkRemover.addContent("if " + linkVarName + " = Null Return")
         m1LinkRemover.addContent("local val:" + type2.id + " = " + linkVarName)
         m1LinkRemover.addContent(linkVarName + " = Null")
         m1LinkRemover.addContent("val.remove" + t1Name + "()")
         type1.addMethod(m1LinkRemover)
         type1.destructor.addContent(m1LinkRemover.id + "()")
   
         linkVarName:String = Lower(t1Name) + "Link"
         Local m2Linker:DMethod = New DMethod
         Local m2LinkRemover:DMethod = New DMethod
         
         type2.addLinkVar(DField.Create(linkVarName, type1.id))
         m2Linker.setId("set" + t1Name)
         m2Linker.addInput("obj:" + type1.id)
         m2Linker.addInput("call:byte = true")
         m2Linker.addContent("if call obj.set" + t2Name + "(Self,false)")
         m2Linker.addContent(linkVarName + " = obj")
         type2.addMethod(m2Linker)
         
         m2LinkRemover.setId("remove" + t1Name)
         m2LinkRemover.addContent("if " + linkVarName + " = Null Return")
         m2LinkRemover.addContent("local val:" + type1.id + " = " + linkVarName)
         m2LinkRemover.addContent(linkVarName + " = Null")
         m2LinkRemover.addContent("val.remove" + t2Name + "()")
         type2.addMethod(m2LinkRemover)
         type2.destructor.addContent(m2LinkRemover.id + "()")
   End Method
   
   Method monoDirectionalOneToOne(type1:DType, type2:DType, t1Name:String, t2Name:String)
         Local linkVarName:String = Lower(t2Name) + "Link"
         Local m1Linker:DMethod = New DMethod
         Local m1LinkRemover:DMethod = New DMethod
         
         t1Name = Upper(Left(t1Name, 1)) + Mid(t1Name, 2)
         t2Name = Upper(Left(t2Name, 1)) + Mid(t2Name, 2)
         
         type1.addLinkVar(DField.Create(linkVarName, type2.id))
         m1Linker.setId("set" + t2Name)
         m1Linker.addInput("obj:" + type2.id)
         m1Linker.addContent(linkVarName + " = obj")
         type1.addMethod(m1Linker)
         
         m1LinkRemover.setId("remove" + t2Name)
         m1LinkRemover.addContent("if " + linkVarName + " = Null Return")
         m1LinkRemover.addContent(linkVarName + " = Null")
         type1.addMethod(m1LinkRemover)
         type1.destructor.addContent(m1LinkRemover.id + "()")
   End Method
   
   Method monoDirectionalManyToOne(type1:DType, type2:DType, t1Name:String, t2Name:String)
         Local linkVarName:String = Lower(t2Name) + "Link"
         Local collectionName:String = Lower(t1Name) + "List"
         Local m1Linker:DMethod = New DMethod
         Local m1LinkRemover:DMethod = New DMethod
         
         t2Name = Upper(Left(t2Name, 1)) + Mid(t2Name, 2)
         t1Name = Upper(Left(t1Name, 1)) + Mid(t1Name, 2)
         
         type1.addLinkVar(DField.Create(linkVarName, type2.id))
         type1.addLinkVar(DField.Create("_" + linkVarName, "TLink"))
         m1Linker.setId("set" + t2Name)
         m1Linker.addInput("obj:" + type2.id)
         m1Linker.addInput("call:byte = True")
         m1Linker.addContent("If " + linkVarName + " <> Null self.remove" + t2Name + "()")
         m1Linker.addContent(linkVarName + " = obj")
         m1Linker.addContent("if call _" + linkVarName + " = obj.add" + t1Name + "(self,false)")
         type1.addMethod(m1Linker)
         
         m1LinkRemover.setId("remove" + t2Name)
         m1LinkRemover.addContent("if " + linkVarName + " = Null Return")
         m1LinkRemover.addContent(linkVarName + " = Null")
         m1LinkRemover.addContent("removeLink(_" + linkVarName + ")")
         type1.addMethod(m1LinkRemover)
         type1.destructor.addContent(m1LinkRemover.id + "()")
         
         
         type2.addLinkVar(DField.Create(collectionName, "TList", "New TList"))
         Local m2Linker:DMethod = New DMethod
         m2Linker.setId("add" + t1Name)
         m2Linker.addInput("obj:" + type1.id)
         m2Linker.addInput("call:byte = true")
         m2Linker.addContent("if call obj.set" + t2Name + "(self,false)")
         m2Linker.addContent("return " + collectionName + ".addLast(obj)")
         m2Linker.setOutput("TLink")
         type2.addMethod(m2Linker)
         
         Local m2Remover:DMethod = New DMethod
         m2Remover.setId("removeAll" + t1Name + "Links")
         m2Remover.addContent("For local obj:" + type1.id + " = eachin Self." + collectionName)
         m2Remover.addContent(Chr(9) + "obj.remove" + t2Name + "()")
         m2Remover.addContent("Next")
         type2.addMethod(m2Remover)
         type2.destructor.addContent(m2Remover.id + "()")
   End Method
End Type

Type DType
   Field bbdoc:Byte
   Field id:String
   Field vars:TList
   Field linkVars:TList
   Field methods:TList
   Field destructor:DMethod
   
   Method New()
      vars = New TList
      linkVars = New TList
      methods = New TList
      destructor = New DMethod
   End Method

   Function Create:DType(id:String)
      Local t:DType = New DType
      t.id = "T" + id
      t.destructor.setId("remove")
      Return t
   End Function
   
   Method write(stream:TStream)
      WriteLine stream, ""
      If bbdoc
         WriteLine stream, "Rem"
         WriteLine stream, Chr(9) + "bbdoc: " + Self.id
         WriteLine stream, Chr(9) + "about:"
         WriteLine stream, "End Rem"
      End If
      WriteLine stream, "Type " + id
      If Not vars.IsEmpty()
         WriteLine stream, ""
         For Local v:DField = EachIn vars
            v.write(stream)
         Next
      EndIf
      
      If Not linkVars.IsEmpty()
         WriteLine stream, ""
         For Local v:DField = EachIn linkVars
            v.write(stream)
         Next
      EndIf
      
      If Not methods.IsEmpty()
         WriteLine(stream, "")
         For Local m:DMethod = EachIn methods
            m.write(stream)
            WriteLine(stream, "")
         Next
      EndIf
      WriteLine(stream, "")
      Self.destructor.write(stream)
      WriteLine stream, "End Type"
      WriteLine stream, ""
   End Method
   
   Method addVar(v:DField)
      vars.AddLast(v)
   End Method
   
   Method addLinkVar(v:DField)
      linkVars.AddLast(v)
   End Method
   
   Method addMethod(m:DMethod)
      methods.AddLast(m)
   End Method
End Type

Type DField
   Field id:String
   Field datatype:String
   Field val:String
   
   Function Create:DField(id:String, datatype:String, val:String = "")
      Local v:DField = New DField
      Select datatype
         Case "" datatype = "Int"
         Case "%" datatype = "Int"
         Case "#" datatype = "Float"
      End Select
      v.id = id
      v.datatype = datatype
      v.val = val
      Return v
   End Function
   
   Method write(stream:TStream)
      Local l:String = Chr(9) + "Field " + id + ":" + datatype
      If val <> ""
         If Lower(datatype) = "string"
            l:+" = " + Chr(34)+val+Chr(34)
         Else
            l:+" = " + val
         EndIf
      EndIf
      WriteLine stream, l
   End Method
End Type

Type DMethod
   Field bbdoc:Byte
   Field bbdocText:String
   Field id:String
   Field inputs:TList
   Field output:String
   Field content:TList

   
   Method New()
      inputs = New TList
      output = ""
      content = New TList
   End Method
   
   Method setId(id:String)
      Self.id = id
   End Method
   
   Method addContent(line:String)
      content.AddLast(line)
   End Method
   
   Method addInput(line:String)
      inputs.AddLast(line)
   End Method
   
   Method setOutput(line:String)
      output = line
   End Method
   
   Method setBBdocText(txt:String)
      Self.bbdocText = txt
      Self.bbdoc = True
      If bbdocText = "" bbdocText = Self.id
   End Method
   
   Method write(stream:TStream)
      If bbdoc
         WriteLine stream, Chr(9) + "Rem"
         WriteLine stream, Chr(9) + Chr(9) + "bbdoc: " + bbdocText
         For Local l:String = EachIn inputs
            WriteLine stream, Chr(9) + Chr(9) + "var: " + l
         Next
         WriteLine stream, Chr(9) + "End Rem"
      End If
      Local l:String
      l = Chr(9) + "Method " + id
      If output <> "" l:+":" + output
      l:+"("
      While inputs.Count() > 0
         Local v:String = String(inputs.RemoveFirst())
         If inputs.IsEmpty()
            l:+v
         Else
            l:+v + " , "
         EndIf
      Wend
      l:+")"
      WriteLine stream, l
      For l = EachIn content
         WriteLine stream, Chr(9) + Chr(9) + l
      Next
      WriteLine stream, Chr(9) + "End Method"
   End Method
End Type



Kleines Anwendungsbeispiel

Man schreibt einfach:
Code: [AUSKLAPPEN]
<CodeGenerator out="Tut4.bmx">

   <Type id="WorldManager" bbdoc="1"/>

   <Type id="Driver">
      <Field id="name" datatype="String" value="Default" />
      <Field id="age" datatype="int"/>
   </Type>

   <Type id="Car">
      <Field id="brand" datatype="String"/>
   </Type>

   <Connect type1="Driver" type2="Car" link1Id="Owner" link2Id="Car" linkType="mono121" />
   <Connect type1="Car" type2="WorldManager" link1Id="Thing" link2Id="world" linkType="monoM21" />
</CodeGenerator>


Jagd das ganze durch das Programm, und erhält
Code: [AUSKLAPPEN]

Type TCar

   Field brand:String

   Field worldLink:TWorldManager
   Field _worldLink:TLink

   Method setWorld(obj:TWorldManager , call:Byte = True)
      If worldLink <> Null Self.removeWorld()
      worldLink = obj
      If call _worldLink = obj.addThing(Self,False)
   End Method

   Method removeWorld()
      If worldLink = Null Return
      worldLink = Null
      RemoveLink(_worldLink)
   End Method


   Method remove()
      removeWorld()
   End Method
End Type


Type TDriver

   Field name:String = "Default"
   Field age:Int

   Field carLink:TCar

   Method setCar(obj:TCar)
      carLink = obj
   End Method

   Method removeCar()
      If carLink = Null Return
      carLink = Null
   End Method


   Method remove()
      removeCar()
   End Method
End Type


Rem
   bbdoc: TWorldManager
   about:
End Rem
Type TWorldManager

   Field thingList:TList = New TList

   Method addThing:TLink(obj:TCar , call:Byte = True)
      If call obj.setWorld(Self,False)
      Return thingList.addLast(obj)
   End Method

   Method removeAllThingLinks()
      For Local obj:TCar = EachIn Self.thingList
         obj.removeWorld()
      Next
   End Method


   Method remove()
      removeAllThingLinks()
   End Method
End Type

www.illusion-games.de
Space War 3 | Space Race | Galaxy on Fire | Razoon
Gewinner des BCC #57 User posted image

Geeecko

BeitragMo, März 16, 2009 20:04
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich glaube es wäre interessanter, einen "Generator" zu schreiben,
der <nachdem auf F5 gedürckt worden ist, also vor dem eigentlichen compilervorgang> den quellcode so manipulieren würde,
das für bestimmte types pro variable zwei funktionen erstellt werden (set und get).
Hier mal ein Beispiel:

Anfang:
Code: [AUSKLAPPEN]

TCar
  Field speed:Int

  Method setSpeed(value:Int)
    If(Value < 0) Then
      Self.speed = value
    Else
      self.speed = 0
    EndIf
  End Method

  Method getSpeed.............
  .......
  End Method

End Type

Local myCar:TCar = new TCar
myCar.speed = 5
Print myCar.speed


Generator:
Code: [AUSKLAPPEN]

...
Local myCar:TCar = new TCar
myCar.setSpeed(5)
Print myCar.getSpeed()

Firstdeathmaker

BeitragSo, März 29, 2009 16:22
Antworten mit Zitat
Benutzer-Profile anzeigen
Hmm, das ist natürlich auch interessant, aber besser wäre es da vielleicht, das in den Editor zu integrieren. Ich persönlich habe mir dazu die community-edition etwas umgeschrieben, welche mir jetzt einen zusätzlichen Dialog anbietet mit welchem ich getter und setter erzeugen kann.

Aber mein Program da oben geht ja noch viel weiter: Es stellt Funktionen zum Assoziationsmanagement bereit.
www.illusion-games.de
Space War 3 | Space Race | Galaxy on Fire | Razoon
Gewinner des BCC #57 User posted image

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group