Dynamic recursive type arrays 
Author Message
 Dynamic recursive type arrays

Is there a way to implement dynamic recursive type arrays? What it is? This
is a dynamic recursive type array:

        Type Jens
                SubJens() As Jens
        End Type

on which you should be able to perform:

        Redim Jens.SubJens(x)

But VB won't allow any of these. So, is there another way to do this?

--
VB Info: http://www.*-*-*.com/ ~balchen/vb/visual.htm
FAQ: http://www.*-*-*.com/ ~balchen/vb/faq.htm
Knowledge Base: http://www.*-*-*.com/ ~balchen/vb/kb.htm



Thu, 28 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
>Is there a way to implement dynamic recursive type arrays? What it is? This
>is a dynamic recursive type array:
>    Type Jens
>            SubJens() As Jens
>    End Type

I don't think it is possible because you are trying to declare
SubJens() as a UDT of Jens that has not yet been defined.

Quote:
>on which you should be able to perform:
>    Redim Jens.SubJens(x)

Even if you could wouldn't this be incorrect? This would have to be
something like
Redim JensNew as Jens
Redim JensNew.SubJens(x)
Don't you have to assign a name to a UDT and are not allowed to use it
directly? (i.e.
Type MyType
    abc as string
End Type
Dim My as MyType
You can't reference MyType.abc, can you? It must be My.abc......

Quote:
>But VB won't allow any of these. So, is there another way to do this?

Also I don't have a clue as to what value  Jens.SubJens(x) could hold
since it would not be defined as anything other than itself. How would
you intend to use this?

At least this is an interesting question and not "How do I keep my
form on Top" <g>.

Jim Huguley

* Those who sit and think *
* Mostly sit              *



Thu, 28 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays

I probably went a little overboard, but why not use objects.

Here's an example:

1.      Insert a Module into your project and add the following code:

Option Explicit

Public Sub Main()

    Dim o As Parent
    Set o = New Parent

    With o
        .Name = "Larry"
        .Age = 28
    End With

    'Here are Larry's Children
    o.AddChild o, "Epson", 1
    o.AddChild o, "PC", 1
    o.AddChild o, "Smokey", 3

    'Here are Smokey's Children, Larry's Grandchildren.
    Dim gc As Parent
    Set gc = o.cChildren("Smokey")
    gc.AddChild gc, "Spit-Fire", 1
    gc.AddChild gc, "Tar-Baby", 47

    'Note this prints 47 in the debug window.
    debug.print o.cChildren("Smokey").cChildren("Spit-Fire").age

    stop

End Sub

2.      Insert a new class into your project and name it Parent.  Then add the
following code:

Option Explicit

Public Name As String
Public Age As Integer

Public oParent As Parent
Public cChildren As New Collection

Public Sub AddChild(oParent As Parent, sName As String, iAge As Integer)

    Dim Child As Parent
    Set Child = New Parent

    With Child
        Set .oParent = oParent
        .Name = sName
        .Age = iAge
    End With

    cChildren.Add Item:=Child, Key:=sName
    Set Child = Nothing

End Sub

NOTES:

        Since collections are dynamic, you can create an class and place a
collection in it.  The AddChild routine places new instances of the Parent
class into the collection.  Using this method, I can have as many kids,
grandkids, etc. as I want!  Hate to see my grocery bill!



Quote:
> Is there a way to implement dynamic recursive type arrays? What it is?
This
> is a dynamic recursive type array:

>    Type Jens
>            SubJens() As Jens
>    End Type

> on which you should be able to perform:

>    Redim Jens.SubJens(x)

> But VB won't allow any of these. So, is there another way to do this?

> --
> VB Info: http://www.sn.no/~balchen/vb/visual.htm
> FAQ: http://www.sn.no/~balchen/vb/faq.htm
> Knowledge Base: http://www.sn.no/~balchen/vb/kb.htm



Thu, 28 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
>I don't think it is possible because you are trying to declare
>SubJens() as a UDT of Jens that has not yet been defined.

Exactly. So I'm looking for a way around it.

Quote:
>>        Redim Jens.SubJens(x)

>Even if you could wouldn't this be incorrect?

Being picky, are we?

Quote:
>You can't reference MyType.abc, can you? It must be My.abc......

Of course, you are correct, but that is the least of my problems.

Quote:
>>But VB won't allow any of these. So, is there another way to do this?
>Also I don't have a clue as to what value  Jens.SubJens(x) could hold
>since it would not be defined as anything other than itself. How would
>you intend to use this?

I'm creating a command interpreter, and at one stage in the processing of
code, I do a recursive check for expressions. Each expression can have
several sub-expressions. As in this example:

   if ( gethostname ( a + b ) = c )

(pardon the C syntax)

You can break this down into:

expr1 ::= name ( expr2 ) ' "if ( )"

expr2 ::= expr3 operator expr4 ' gethostname ( a + b ) = c

expr3 ::= name ( expr5 ) ' "gethostname ( )"

expr5 ::= name operator name ' a + b

expr4 ::= name ' c

When I do an expression parse, I would like to do this:

        GetExp "if ( gethostname ( a + b ) = c )", Exp

        Sub GetExp (ByVal CmdString, Exp As ExpType)

                ' blah blah parse blah blah
                ' Get sub expressions
                While SubExpressions
                        Redim Exp.SubExp(+1)
                        GetExp "the parsed out expression" Exp.SubExp(+1)
           Wend

        End Sub

Get the point? A recursive expressions parser. As Larry Morris pointed out,
this could be possible in VB 4.0, but it wouldn't be possible in VB 3.0
unless someone thinks of something clever.

--
VB Info: http://www.sn.no/~balchen/vb/visual.htm
FAQ: http://www.sn.no/~balchen/vb/faq.htm
Knowledge Base: http://www.sn.no/~balchen/vb/kb.htm



Thu, 28 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays



Quote:
>SNIP<

>    'Here are Smokey's Children, Larry's Grandchildren.
>    Dim gc As Parent
>    Set gc = o.cChildren("Smokey")
>    gc.AddChild gc, "Spit-Fire", 1
>    gc.AddChild gc, "Tar-Baby", 47

>    'Note this prints 47 in the debug window.
>    debug.print o.cChildren("Smokey").cChildren("Spit-Fire").age

Am I missing something here? I haven't used objects or classes and
UDT's. Shouldn't the above code print 1 to the debug window?

Ed

********************************************************
**  Southern Indiana Bass Fishing / Outdoor Software  **
**    HTTP://www.evansville.net/~dlion/mypage.htm    **
********************************************************



Thu, 28 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
>I probably went a little overboard, but why not use objects.

Execpt that you should've used Tar-Baby as Key in the last line, it was a
great sample. But: upon testing this code, VB 4.0 grants me with a GPF...

Quote:
>    Set gc = o.cChildren("Smokey")

... right here. If I press Ignore four times, VB returns and gives me a
"Subscript out of range" error.

Duh. Forget it. Once I restarted VB, it worked like charm. I think I hate
VB 4.0, even though I like classes more than ever.

But, being who I am, this solution has only academic value. Thanks anyway -
it was fun.

--
VB Info: http://www.sn.no/~balchen/vb/visual.htm
FAQ: http://www.sn.no/~balchen/vb/faq.htm
Knowledge Base: http://www.sn.no/~balchen/vb/kb.htm



Fri, 29 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
>Is there a way to implement dynamic recursive type arrays? What it is? This
>is a dynamic recursive type array:
>    Type Jens
>            SubJens() As Jens
>    End Type
>on which you should be able to perform:
>    Redim Jens.SubJens(x)
>But VB won't allow any of these. So, is there another way to do this?

This is a first for me to see you ask a question. Anyway, I don't
beleive any language can do this. It is unresolvable.

Normally when you want to do something like this, you are creating a
reference to another object like in a linked list. That is, the
SubType does not contain Type, but SubType *POINTS* to another copy of
Type.

In C for example,

struct Jens {
  struct Jens * SubJens

Quote:
}

I would probably try to use an unbounded Array for this. But as an
exercise, you could test the following in VB4. This is 1st iteration
(out of my head), completely untested. (I only have VB3 which does not
support objects in Types)

Option Explicit

Type Jens
    item As Integer
    SubJens As object
'              ^^^^^^
'              VB3 Fails here
End Type

Sub walkJens ()
    Dim j1 As Jens
    Dim j2 As Jens
    Dim j3 As Jens
    Dim jref As object

    j1.item = 1
    Set j1.SubJens = j2

    j2.item = 2
    Set j2.SubJens = j2

    j3.item = 3
    Set j2.SubJens = j3

    Set jref = j1
    For a = 1 To 3
        Debug.Print jref.item, Hex$(jref.SubJens)
        Set jref = jref.SubJens
    Next
End Sub

  __   __   _______________________________
 //)) //)) | Richard RUDEK. MicroDek.      | Hey, Whadda ya
//\\ //\\  | Chatswood, Sydney. Australia. | want for nuting...
           `-------------------------------'



Sun, 31 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:

>>I don't think it is possible because you are trying to declare
>>SubJens() as a UDT of Jens that has not yet been defined.
>Exactly. So I'm looking for a way around it.
>>>    Redim Jens.SubJens(x)

<snip>

Quote:
>I'm creating a command interpreter, and at one stage in the processing of
>code, I do a recursive check for expressions. Each expression can have
>several sub-expressions. As in this example:
>   if ( gethostname ( a + b ) = c )
>(pardon the C syntax)
>You can break this down into:
>expr1 ::= name ( expr2 ) ' "if ( )"
>expr2 ::= expr3 operator expr4 ' gethostname ( a + b ) = c
>expr3 ::= name ( expr5 ) ' "gethostname ( )"
>expr5 ::= name operator name ' a + b
>expr4 ::= name ' c
>When I do an expression parse, I would like to do this:
>    GetExp "if ( gethostname ( a + b ) = c )", Exp
>    Sub GetExp (ByVal CmdString, Exp As ExpType)
>            ' blah blah parse blah blah
>            ' Get sub expressions
>            While SubExpressions
>                    Redim Exp.SubExp(+1)
>                    GetExp "the parsed out expression" Exp.SubExp(+1)
>       Wend
>    End Sub
>Get the point? A recursive expressions parser. As Larry Morris pointed out,
>this could be possible in VB 4.0, but it wouldn't be possible in VB 3.0
>unless someone thinks of something clever.

I'm confused. Looking up expression:

"A combination of functions, operators, variables, and constants that
yield a string or numeric result.  An expression can perform a
calculation, manipulate characters, or test data."

As a consequence, an expression parser has to understand the language
constructs. That is, interpret.

I think the easiest approach would be to instead treat the data stream
as a collection of tokens. Tokens can be commands, operators or data.

Commands are interpreted, setting certain properties, parameter counts
etc. If parameters are involved, then in reality what you have is the
possiblity of an expression.

Each expression, in turn has to be run by the interpreter again to
break up the expression into command, data and more parameters.

Anyway, I would start by looking at some of the C code out there that
implement an interpreter, or compiler ?

You may already have done this, But from the above, I'm not sure I
understand how a "Dynamic Recursive Type Array" would operate here.

This thread is interesting to me because I will be starting a new
project soon that I intend to have interpreted/compiled scripts
visually constructed. But my starting point will probably be a
Java/postscript/BASIC Interpreter or a C compiler.

I also doubt wether I'll write it in VB.
  __   __   _______________________________
 //)) //)) | Richard RUDEK. MicroDek.      | Hey, Whadda ya
//\\ //\\  | Chatswood, Sydney. Australia. | want for nuting...
           `-------------------------------'



Sun, 31 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
> Normally when you want to do something like this, you are creating a
> reference to another object like in a linked list. That is, the
> SubType does not contain Type, but SubType *POINTS* to another copy of
> Type.

So I could do this in VB using some Longs and the hmemcpy API. I already
thought of that, but then I meet a problem: The Redim.

Imagine SubJens is an integer array now. I've tried the following:

1:

        Redim Jens.SubJens(10)

2:

        Sub RedimArray (Array() As Integer)
                Redim Array(Ubound(Array) + 1)
        End Sub

        RedimArray Jens.SubJens()

None of these work. So the criteria 1) subitems of the same type, and 2)
redimable, aren't met.

--
VB Info: http://www.sn.no/~balchen/vb/visual.htm
FAQ: http://www.sn.no/~balchen/vb/faq.htm
Knowledge Base: http://www.sn.no/~balchen/vb/kb.htm



Sun, 31 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
> I'm confused. Looking up expression:

Don't. I don't know wether I'm using the word as anyone else would.

Quote:
> "A combination of functions, operators, variables, and constants that
> yield a string or numeric result.  An expression can perform a
> calculation, manipulate characters, or test data."

Well, that book isn't far off. :)

Quote:
> As a consequence, an expression parser has to understand the language
> constructs. That is, interpret.

No, it does not.

I've planned this in four steps:

1. Tokenizer (see below). Barry Wegman referred to this a lexical analyzer
2. Expression parser
3. Data storage (optional)
4. Interpreter

Steps 1-3 are to be generic to all languages I plan to implement using this
code. It's only step 4 that needs to be customized. Let me explain a little
further.

Say we have an input string like:

if(gethostname(a(i+1))=gethostname(b+c(a+1)+1)){printf(c);printf(b);}else{printf(a);};

(pardon the C syntax)

This is what I currently use for testing. The tokenizer generates a token
stream, based on simple criteria:

1. I have made an array of statements and tokens, so that when a substring
in the string matches a statement, a given token is returned (a statement
will also include "(", ")", "{", "}", and so on)

2. Anything that doesn't match a statement is a (varible, function, sub)
name, and the token for a name is returned

3. You have to tell the tokenizer which characters aren't allowed in a name

After the tokenizer had analyzed the input string with the given criteria,
it returns an array of tokens. The token array is sent to the expression
parser, which also has some information on the langauge (function start =
"(", function end = ")", and so on), and it generates a tree-structure of
statements, like this:

if
        gethostname
                a
                        i
                        +
                        1
        =
        gethostname
                b
                +
                c
                        a
                        +
                        1
                +
                1
{
        printf
                c
        ;
        printf
                b
        ;
else
{
        printf
                a
        ;
;

So every expression can have one or more sub expressions. This tree
structure makes it easier for the interpreter to interpret the data (IMO).

When the interpreter wants to interpret the data, it steps down the tree
structure until it reaches a dead end, at which point it returns. This way,
it can evaulate the expressions in an orderly manner, and recurse through
the entire array.

Quote:
> I think the easiest approach would be to instead treat the data stream
> as a collection of tokens. Tokens can be commands, operators or data.

I already do.

Quote:
> You may already have done this, But from the above, I'm not sure I
> understand how a "Dynamic Recursive Type Array" would operate here.

Do you see now?

Quote:
> This thread is interesting to me because I will be starting a new
> project soon that I intend to have interpreted/compiled scripts
> visually constructed. But my starting point will probably be a
> Java/Postscript/BASIC Interpreter or a C compiler.

With my code, you can interpret C code if you like :)

Quote:
> I also doubt wether I'll write it in VB.

It _is_ rather slow, but that doesn't worry me. The compiler doesn't need
to be fast, as long as the interpreter is.

--
VB Info: http://www.sn.no/~balchen/vb/visual.htm
FAQ: http://www.sn.no/~balchen/vb/faq.htm
Knowledge Base: http://www.sn.no/~balchen/vb/kb.htm



Sun, 31 Jan 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:

>> I'm confused. Looking up expression:
>Don't. I don't know wether I'm using the word as anyone else would.
>> "A combination of functions, operators, variables, and constants that
>> yield a string or numeric result.  An expression can perform a
>> calculation, manipulate characters, or test data."
>Well, that book isn't far off. :)
>> As a consequence, an expression parser has to understand the language
>> constructs. That is, interpret.
>No, it does not.
>I've planned this in four steps:
>1. Tokenizer (see below). Barry Wegman referred to this a lexical analyzer
>2. Expression parser
>3. Data storage (optional)
>4. Interpreter
>Steps 1-3 are to be generic to all languages I plan to implement using this
>code. It's only step 4 that needs to be customized. Let me explain a little
>further.
>Say we have an input string like:
>if(gethostname(a(i+1))=gethostname(b+c(a+1)+1)){printf(c);printf(b);}else{printf(a);};
>(pardon the C syntax)
>This is what I currently use for testing. The tokenizer generates a token
>stream, based on simple criteria:
>1. I have made an array of statements and tokens, so that when a substring
>in the string matches a statement, a given token is returned (a statement
>will also include "(", ")", "{", "}", and so on)
>2. Anything that doesn't match a statement is a (varible, function, sub)
>name, and the token for a name is returned
>3. You have to tell the tokenizer which characters aren't allowed in a name
>After the tokenizer had analyzed the input string with the given criteria,
>it returns an array of tokens. The token array is sent to the expression
>parser, which also has some information on the langauge (function start =
>"(", function end = ")", and so on), and it generates a tree-structure of
>statements, like this:
>if
>    gethostname
>            a
>                    i
>                    +
>                    1
>    =
>    gethostname
>            b
>            +
>            c
>                    a
>                    +
>                    1
>            +
>            1
>{
>    printf
>            c
>    ;
>    printf
>            b
>    ;
>else
>{
>    printf
>            a
>    ;
>;
>So every expression can have one or more sub expressions. This tree
>structure makes it easier for the interpreter to interpret the data (IMO).
>When the interpreter wants to interpret the data, it steps down the tree
>structure until it reaches a dead end, at which point it returns. This way,
>it can evaulate the expressions in an orderly manner, and recurse through
>the entire array.
>> I think the easiest approach would be to instead treat the data stream
>> as a collection of tokens. Tokens can be commands, operators or data.
>I already do.
>> You may already have done this, But from the above, I'm not sure I
>> understand how a "Dynamic Recursive Type Array" would operate here.
>Do you see now?
>> This thread is interesting to me because I will be starting a new
>> project soon that I intend to have interpreted/compiled scripts
>> visually constructed. But my starting point will probably be a
>> Java/Postscript/BASIC Interpreter or a C compiler.
>With my code, you can interpret C code if you like :)
>> I also doubt wether I'll write it in VB.
>It _is_ rather slow, but that doesn't worry me. The compiler doesn't need
>to be fast, as long as the interpreter is.

OK. What I was suggesting was that instead of tokenising all of the
data stream *before* it is given to the interpreter, you could try
having the interpreter *request* tokens from the data stream.

This way, you don't have to build and maintain a list of pointers etc.

Anyway, I'll take a look at this, and your previous reply to my
previous post (Sheeze, what a mouthful), and see what I can come up
with using VB3. I suspect it would be easier in VB4.

BTW, did you try that untested example I gave ?

  __   __   _______________________________
 //)) //)) | Richard RUDEK. MicroDek.      | Hey, Whadda ya
//\\ //\\  | Chatswood, Sydney. Australia. | want for nuting...
           `-------------------------------'



Mon, 01 Feb 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:

>> Normally when you want to do something like this, you are creating a
>> reference to another object like in a linked list. That is, the
>> SubType does not contain Type, but SubType *POINTS* to another copy of
>> Type.

<snip>

I think this should work on VB4, but for some reason my VB3 doesn't
like this. GPF's. I can't see why.

Basically, you create 3 unbounded arrays. Two object arrays, and one
string (or whatever).

The first object array (ObjectArray), contains either String or
SubObjectArrays, and is the Trunk of the Tree.

SubOjectArray can also contain String or SubObjectArrays. This last
part is what gives you recursion.

--Module--

Option Explicit

Global ObjectArray() As object
Global SubObjectArray() As object
Global StringArray$()

--- Form --

Option Explicit

Sub Command2_Click ()
    ReDim ObjectArray(4) As Object
    ReDim SubObjectArray(2) As Object
    ReDim StrArray(3) As String
    Dim OB As Object
    Dim a%

    'Fill StrArray
    StrArray(1) = "S1"
    StrArray(2) = "S2"
    StrArray(3) = "S3"

    'Linkup SubObjectArray to either StrArray or a lower SubObject
    Set SubObjectArray(1) = StrArray(2)
    Set SubObjectArray(2) = StrArray(3)
    Set SubObjectArray(3) = SubObjectArray(2)

    'Linkup ObjectArray to StrArray or SubObjects
    Set ObjectArray(1) = "Test"
    Set ObjectArray(2) = StrArray(1)
    Set ObjectArray(3) = SubObjectArray(1)
    Set ObjectArray(4) = SubObjectArray(3)

    'Show the results
    List1.Clear
    For a = 1 To 4
        Set OB = ObjectArray(a)
        Do Until VarType(OB) = 8
            Set OB = OB
            DoEvents
        Loop
        List1.AddItem OB
    Next
End Sub

  __   __   _______________________________
 //)) //)) | Richard RUDEK. MicroDek.      | Hey, Whadda ya
//\\ //\\  | Chatswood, Sydney. Australia. | want for nuting...
           `-------------------------------'



Mon, 01 Feb 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
>Get the point? A recursive expressions parser. As Larry Morris pointed out,
>this could be possible in VB 4.0, but it wouldn't be possible in VB 3.0
>unless someone thinks of something clever.

if you just want to evaluate the expression, not save the components
for later, then each time getexp finds a ( , it calls itself pointing
past the ( and returning the result on a ).
-------------------------------------------------------------



Mon, 01 Feb 1999 03:00:00 GMT  
 Dynamic recursive type arrays


Quote:
> OK. What I was suggesting was that instead of tokenising all of the
> data stream *before* it is given to the interpreter, you could try
> having the interpreter *request* tokens from the data stream.

Well, things would work out like this (I guess):

Send string to interpreter.
Interpreter asks tokenizer to tokenize, and then ask the expression parser
to parse it.
Interpreter then interprets and executes commands.

Quote:
> Anyway, I'll take a look at this, and your previous reply to my
> previous post (Sheeze, what a mouthful), and see what I can come up
> with using VB3. I suspect it would be easier in VB4.
> BTW, did you try that untested example I gave ?

No, I didn't. Larry Morris gave me some tips on how to do it using a class
and a collection, and I have that working already. Besides, that *******
redim works in VB 4.0, but not in VB 3.0. *aaaarrrgghh*

--
VB Info: http://www.sn.no/~balchen/vb/visual.htm
FAQ: http://www.sn.no/~balchen/vb/faq.htm
Knowledge Base: http://www.sn.no/~balchen/vb/kb.htm



Mon, 01 Feb 1999 03:00:00 GMT  
 Dynamic recursive type arrays

Quote:

> Is there a way to implement dynamic recursive type arrays? What it is? This
> is a dynamic recursive type array:
>         Type Jens
>                 SubJens() As Jens
>         End Type
> on which you should be able to perform:
>         Redim Jens.SubJens(x)
> But VB won't allow any of these. So, is there another way to do this?

Without trying it, and without thinking much why you might want to do
this, without much at all, really, couldn't you do it with a class?
Since a class is globally registered, it should be possible to have
a class Jens which can certainly dim x as object and which at run time
can be assigned to an object of class Jens and which can also support
methods which (at a pinch, the compiler might barf at this) might even
be able to return objects of class Jens? I can see trouble with this,
from recursion obviously, but this should be get-roundable (gettable
round?) by implementing a class which is a collection of Jens objects.
(On the grounds that recursion can always be replaced by iteration.)
Do you object, Jens?

[>8]
--
-------------------------------------------------------------------

BB 74 A4 EF 03 F8 44 C1 F3 75 FE C6 7E F9 6E 43         --- at home
-------------------------------------------------------------------



Tue, 02 Feb 1999 03:00:00 GMT  
 
 [ 15 post ] 

 Relevant Pages 

1. Dynamic Arrays in user defined Types

2. Passing dynamic array of custom type to form method from another form

3. Dynamic arrays of user-defined type

4. Split() and dynamic arrays...Type Mismatch Error.

5. Convert array of value type to array of reference type

6. Dynamic Data Structures: Dynamic Arrays?

7. Dynamic array within an array?

8. Recursive data types.

9. Recursive Types

10. Recursive Types

11. Recursive data types in VB?

12. how to declare recursive type in VB?

 

 
Powered by phpBB® Forum Software