[vala] dova: Accept list, set, and map literals and tuples
- From: Jürg Billeter <juergbi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala] dova: Accept list, set, and map literals and tuples
- Date: Sat, 13 Mar 2010 19:46:58 +0000 (UTC)
commit 84b6b23be9a93fc1d17b2b8a9f5134e0f3054ccc
Author: Jürg Billeter <j bitron ch>
Date: Sat Mar 13 20:43:56 2010 +0100
dova: Accept list, set, and map literals and tuples
codegen/valaccodegenerator.vala | 16 +++++
codegen/valaccodemodule.vala | 16 +++++
vala/Makefile.am | 3 +
vala/valacodenode.vala | 2 +-
vala/valacodevisitor.vala | 32 ++++++++++
vala/valaelementaccess.vala | 34 +++++++++++
vala/valalistliteral.vala | 113 ++++++++++++++++++++++++++++++++++++
vala/valamapliteral.vala | 120 +++++++++++++++++++++++++++++++++++++++
vala/valaparser.vala | 87 ++++++++++++++++++++++++++++-
vala/valasemanticanalyzer.vala | 4 +
vala/valasetliteral.vala | 97 +++++++++++++++++++++++++++++++
vala/valatuple.vala | 39 +++++++++++-
12 files changed, 557 insertions(+), 6 deletions(-)
---
diff --git a/codegen/valaccodegenerator.vala b/codegen/valaccodegenerator.vala
index 35dff33..fd465d1 100644
--- a/codegen/valaccodegenerator.vala
+++ b/codegen/valaccodegenerator.vala
@@ -260,6 +260,22 @@ public class Vala.CCodeGenerator : CodeGenerator {
head.visit_string_literal (expr);
}
+ public override void visit_list_literal (ListLiteral expr) {
+ head.visit_list_literal (expr);
+ }
+
+ public override void visit_set_literal (SetLiteral expr) {
+ head.visit_set_literal (expr);
+ }
+
+ public override void visit_map_literal (MapLiteral expr) {
+ head.visit_map_literal (expr);
+ }
+
+ public override void visit_tuple (Tuple expr) {
+ head.visit_tuple (expr);
+ }
+
public override void visit_null_literal (NullLiteral expr) {
head.visit_null_literal (expr);
}
diff --git a/codegen/valaccodemodule.vala b/codegen/valaccodemodule.vala
index ebbc298..939c49f 100644
--- a/codegen/valaccodemodule.vala
+++ b/codegen/valaccodemodule.vala
@@ -239,6 +239,22 @@ public abstract class Vala.CCodeModule {
next.visit_string_literal (expr);
}
+ public virtual void visit_list_literal (ListLiteral expr) {
+ next.visit_list_literal (expr);
+ }
+
+ public virtual void visit_set_literal (SetLiteral expr) {
+ next.visit_set_literal (expr);
+ }
+
+ public virtual void visit_map_literal (MapLiteral expr) {
+ next.visit_map_literal (expr);
+ }
+
+ public virtual void visit_tuple (Tuple expr) {
+ next.visit_tuple (expr);
+ }
+
public virtual void visit_null_literal (NullLiteral expr) {
next.visit_null_literal (expr);
}
diff --git a/vala/Makefile.am b/vala/Makefile.am
index 28a85ae..72c2b88 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -86,11 +86,13 @@ libvalacore_la_VALASOURCES = \
valainterfacetype.vala \
valainvalidtype.vala \
valalambdaexpression.vala \
+ valalistliteral.vala \
valaliteral.vala \
valalocalvariable.vala \
valalockable.vala \
valalockstatement.vala \
valaloop.vala \
+ valamapliteral.vala \
valamarkupreader.vala \
valamember.vala \
valamemberaccess.vala \
@@ -121,6 +123,7 @@ libvalacore_la_VALASOURCES = \
valascanner.vala \
valascope.vala \
valasemanticanalyzer.vala \
+ valasetliteral.vala \
valasignal.vala \
valasignaltype.vala \
valasizeofexpression.vala \
diff --git a/vala/valacodenode.vala b/vala/valacodenode.vala
index d424623..8f83cfd 100644
--- a/vala/valacodenode.vala
+++ b/vala/valacodenode.vala
@@ -189,7 +189,7 @@ public abstract class Vala.CodeNode {
public virtual void get_used_variables (Collection<LocalVariable> collection) {
}
- public string get_temp_name () {
+ public static string get_temp_name () {
return "." + (++last_temp_nr).to_string ();
}
}
diff --git a/vala/valacodevisitor.vala b/vala/valacodevisitor.vala
index 7744674..f11a3ef 100644
--- a/vala/valacodevisitor.vala
+++ b/vala/valacodevisitor.vala
@@ -477,6 +477,38 @@ public abstract class Vala.CodeVisitor {
}
/**
+ * Visit operation called for list literals.
+ *
+ * @param lit a list literal
+ */
+ public virtual void visit_list_literal (ListLiteral lit) {
+ }
+
+ /**
+ * Visit operation called for set literals.
+ *
+ * @param lit a set literal
+ */
+ public virtual void visit_set_literal (SetLiteral lit) {
+ }
+
+ /**
+ * Visit operation called for map literals.
+ *
+ * @param lit a map literal
+ */
+ public virtual void visit_map_literal (MapLiteral lit) {
+ }
+
+ /**
+ * Visit operation called for tuples.
+ *
+ * @param tuple a tuple
+ */
+ public virtual void visit_tuple (Tuple tuple) {
+ }
+
+ /**
* Visit operation called for null literals.
*
* @param lit a null literal
diff --git a/vala/valaelementaccess.vala b/vala/valaelementaccess.vala
index 9cfe3c8..c96ee7a 100644
--- a/vala/valaelementaccess.vala
+++ b/vala/valaelementaccess.vala
@@ -152,6 +152,40 @@ public class Vala.ElementAccess : Expression {
}
value_type = analyzer.unichar_type;
+ } else if (analyzer.context.profile == Profile.DOVA && container_type == analyzer.tuple_type.data_type) {
+ if (get_indices ().size != 1) {
+ error = true;
+ Report.error (source_reference, "Element access with more than one dimension is not supported for tuples");
+ return false;
+ }
+ var index = get_indices ().get (0) as IntegerLiteral;
+ if (index == null) {
+ error = true;
+ Report.error (source_reference, "Element access with non-literal index is not supported for tuples");
+ return false;
+ }
+ int i = index.value.to_int ();
+ if (container.value_type.get_type_arguments ().size == 0) {
+ error = true;
+ Report.error (source_reference, "Element access is not supported for untyped tuples");
+ return false;
+ }
+ if (i < 0 || i >= container.value_type.get_type_arguments ().size) {
+ error = true;
+ Report.error (source_reference, "Index out of range");
+ return false;
+ }
+
+ value_type = container.value_type.get_type_arguments ().get (i);
+
+ // replace element access by call to generic get method
+ var ma = new MemberAccess (container, "get");
+ ma.add_type_argument (value_type);
+ var get_call = new MethodCall (ma);
+ get_call.add_argument (index);
+ get_call.target_type = this.target_type;
+ parent_node.replace_expression (this, get_call);
+ return get_call.check (analyzer);
} else if (container is MemberAccess && container.symbol_reference is Signal) {
index_int_type_check = false;
diff --git a/vala/valalistliteral.vala b/vala/valalistliteral.vala
new file mode 100644
index 0000000..9dd6a4a
--- /dev/null
+++ b/vala/valalistliteral.vala
@@ -0,0 +1,113 @@
+/* valalistliteral.vala
+ *
+ * Copyright (C) 2009 Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Jürg Billeter <j bitron ch>
+ */
+
+public class Vala.ListLiteral : Literal {
+ private List<Expression> expression_list = new ArrayList<Expression> ();
+
+ public DataType element_type { get; private set; }
+
+ public ListLiteral (SourceReference? source_reference = null) {
+ this.source_reference = source_reference;
+ }
+
+ public override void accept_children (CodeVisitor visitor) {
+ foreach (Expression expr in expression_list) {
+ expr.accept (visitor);
+ }
+ }
+
+ public override void accept (CodeVisitor visitor) {
+ visitor.visit_list_literal (this);
+ }
+
+ public void add_expression (Expression expr) {
+ expression_list.add (expr);
+ expr.parent_node = this;
+ }
+
+ public List<Expression> get_expressions () {
+ return expression_list;
+ }
+
+ public override bool is_pure () {
+ return false;
+ }
+
+ public override void replace_expression (Expression old_node, Expression new_node) {
+ for (int i = 0; i < expression_list.size; i++) {
+ if (expression_list[i] == old_node) {
+ expression_list[i] = new_node;
+ }
+ }
+ }
+
+ public override bool check (SemanticAnalyzer analyzer) {
+ if (checked) {
+ return !error;
+ }
+
+ checked = true;
+
+ // list literals are also allowed for constant arrays,
+ // however, they are currently handled by InitializerList
+ // therefore transform this expression if necessary
+ var array_type = target_type as ArrayType;
+ if (array_type != null && array_type.inline_allocated) {
+ var initializer = new InitializerList (source_reference);
+ initializer.target_type = target_type;
+ foreach (var expr in expression_list) {
+ initializer.append (expr);
+ }
+
+ analyzer.replaced_nodes.add (this);
+ parent_node.replace_expression (this, initializer);
+ return initializer.check (analyzer);
+ }
+
+ var list_type = new ObjectType ((Class) analyzer.context.root.scope.lookup ("Dova").scope.lookup ("List"));
+ list_type.value_owned = true;
+
+ bool fixed_element_type = false;
+ if (target_type != null && target_type.data_type == list_type.data_type && target_type.get_type_arguments ().size == 1) {
+ element_type = target_type.get_type_arguments ().get (0).copy ();
+ fixed_element_type = true;
+ }
+
+ foreach (var expr in expression_list) {
+ if (fixed_element_type) {
+ expr.target_type = element_type;
+ }
+ if (!expr.check (analyzer)) {
+ return false;
+ }
+ if (element_type == null) {
+ element_type = expr.value_type.copy ();
+ }
+ }
+
+ element_type.value_owned = true;
+ list_type.add_type_argument (element_type);
+ value_type = list_type;
+
+ return !error;
+ }
+}
diff --git a/vala/valamapliteral.vala b/vala/valamapliteral.vala
new file mode 100644
index 0000000..bf50a78
--- /dev/null
+++ b/vala/valamapliteral.vala
@@ -0,0 +1,120 @@
+/* valamapliteral.vala
+ *
+ * Copyright (C) 2009 Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Jürg Billeter <j bitron ch>
+ */
+
+public class Vala.MapLiteral : Literal {
+ private List<Expression> keys = new ArrayList<Expression> ();
+ private List<Expression> values = new ArrayList<Expression> ();
+
+ public DataType map_key_type { get; private set; }
+ public DataType map_value_type { get; private set; }
+
+ public MapLiteral (SourceReference? source_reference = null) {
+ this.source_reference = source_reference;
+ }
+
+ public override void accept_children (CodeVisitor visitor) {
+ for (int i = 0; i < keys.size; i++) {
+ keys[i].accept (visitor);
+ values[i].accept (visitor);
+ }
+ }
+
+ public override void accept (CodeVisitor visitor) {
+ visitor.visit_map_literal (this);
+ }
+
+ public void add_key (Expression expr) {
+ keys.add (expr);
+ expr.parent_node = this;
+ }
+
+ public void add_value (Expression expr) {
+ values.add (expr);
+ expr.parent_node = this;
+ }
+
+ public List<Expression> get_keys () {
+ return keys;
+ }
+
+ public List<Expression> get_values () {
+ return values;
+ }
+
+ public override bool is_pure () {
+ return false;
+ }
+
+ public override void replace_expression (Expression old_node, Expression new_node) {
+ for (int i = 0; i < keys.size; i++) {
+ if (keys[i] == old_node) {
+ keys[i] = new_node;
+ }
+ if (values[i] == old_node) {
+ values[i] = new_node;
+ }
+ }
+ }
+
+ public override bool check (SemanticAnalyzer analyzer) {
+ if (checked) {
+ return !error;
+ }
+
+ checked = true;
+
+ var map_type = new ObjectType ((Class) analyzer.context.root.scope.lookup ("Dova").scope.lookup ("Map"));
+ map_type.value_owned = true;
+
+ bool fixed_element_type = false;
+ if (target_type != null && target_type.data_type == map_type.data_type && target_type.get_type_arguments ().size == 2) {
+ map_key_type = target_type.get_type_arguments ().get (0).copy ();
+ map_value_type = target_type.get_type_arguments ().get (1).copy ();
+ fixed_element_type = true;
+ }
+
+ for (int i = 0; i < keys.size; i++) {
+ if (fixed_element_type) {
+ keys[i].target_type = map_key_type;
+ values[i].target_type = map_value_type;
+ }
+ if (!keys[i].check (analyzer)) {
+ return false;
+ }
+ if (!values[i].check (analyzer)) {
+ return false;
+ }
+ if (map_key_type == null) {
+ map_key_type = keys[i].value_type.copy ();
+ map_value_type = values[i].value_type.copy ();
+ }
+ }
+
+ map_key_type.value_owned = true;
+ map_value_type.value_owned = true;
+ map_type.add_type_argument (map_key_type);
+ map_type.add_type_argument (map_value_type);
+ value_type = map_type;
+
+ return !error;
+ }
+}
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index 2f03484..e92a4e8 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -551,11 +551,18 @@ public class Vala.Parser : CodeVisitor {
break;
case TokenType.OPEN_BRACE:
if (context.profile == Profile.DOVA) {
- expr = parse_simple_name ();
+ expr = parse_set_literal ();
} else {
expr = parse_initializer ();
}
break;
+ case TokenType.OPEN_BRACKET:
+ if (context.profile == Profile.DOVA) {
+ expr = parse_list_literal ();
+ } else {
+ expr = parse_simple_name ();
+ }
+ break;
case TokenType.OPEN_PARENS:
expr = parse_tuple ();
break;
@@ -1526,6 +1533,32 @@ public class Vala.Parser : CodeVisitor {
variable_type = parse_type ();
}
do {
+ if (variable_type == null && accept (TokenType.OPEN_PARENS)) {
+ // tuple
+ var begin = get_location ();
+
+ string[] identifiers = {};
+ do {
+ identifiers += parse_identifier ();
+ } while (accept (TokenType.COMMA));
+ expect (TokenType.CLOSE_PARENS);
+
+ expect (TokenType.ASSIGN);
+ var tuple = parse_expression ();
+ var tuple_local = new LocalVariable (null, CodeNode.get_temp_name (), tuple, get_src (begin));
+ block.add_statement (new DeclarationStatement (tuple_local, tuple_local.source_reference));
+
+ for (int i = 0; i < identifiers.length; i++) {
+ var temp_access = new MemberAccess.simple (tuple_local.name, tuple_local.source_reference);
+ var ea = new ElementAccess (temp_access, tuple_local.source_reference);
+ ea.append_index (new IntegerLiteral (i.to_string ()));
+ var local = new LocalVariable (null, identifiers[i], ea, tuple_local.source_reference);
+ block.add_statement (new DeclarationStatement (local, local.source_reference));
+ }
+
+ continue;
+ }
+
DataType type_copy = null;
if (variable_type != null) {
type_copy = variable_type.copy ();
@@ -2312,6 +2345,58 @@ public class Vala.Parser : CodeVisitor {
return initializer;
}
+ ListLiteral parse_list_literal () throws ParseError {
+ var begin = get_location ();
+ expect (TokenType.OPEN_BRACKET);
+ var initializer = new ListLiteral (get_src (begin));
+ if (current () != TokenType.CLOSE_BRACKET) {
+ do {
+ var init = parse_expression ();
+ initializer.add_expression (init);
+ } while (accept (TokenType.COMMA));
+ }
+ expect (TokenType.CLOSE_BRACKET);
+ return initializer;
+ }
+
+ Expression parse_set_literal () throws ParseError {
+ var begin = get_location ();
+ expect (TokenType.OPEN_BRACE);
+ var set = new SetLiteral (get_src (begin));
+ bool first = true;
+ if (current () != TokenType.CLOSE_BRACE) {
+ do {
+ var expr = parse_expression ();
+ if (first && accept (TokenType.COLON)) {
+ // found colon after expression, it's a map
+ rollback (begin);
+ return parse_map_literal ();
+ }
+ first = false;
+ set.add_expression (expr);
+ } while (accept (TokenType.COMMA));
+ }
+ expect (TokenType.CLOSE_BRACE);
+ return set;
+ }
+
+ Expression parse_map_literal () throws ParseError {
+ var begin = get_location ();
+ expect (TokenType.OPEN_BRACE);
+ var map = new MapLiteral (get_src (begin));
+ if (current () != TokenType.CLOSE_BRACE) {
+ do {
+ var key = parse_expression ();
+ map.add_key (key);
+ expect (TokenType.COLON);
+ var value = parse_expression ();
+ map.add_value (value);
+ } while (accept (TokenType.COMMA));
+ }
+ expect (TokenType.CLOSE_BRACE);
+ return map;
+ }
+
Method parse_method_declaration (List<Attribute>? attrs) throws ParseError {
var begin = get_location ();
var access = parse_access_modifier ();
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index 51b8906..bafc246 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -136,6 +136,8 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
public DataType garray_type;
public DataType gvaluearray_type;
public Class gerror_type;
+ public DataType list_type;
+ public DataType tuple_type;
public int next_lambda_id = 0;
@@ -203,6 +205,8 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
object_type = (Class) dova_ns.scope.lookup ("Object");
type_type = new ObjectType ((Class) dova_ns.scope.lookup ("Type"));
+ list_type = new ObjectType ((Class) dova_ns.scope.lookup ("List"));
+ tuple_type = new ObjectType ((Class) dova_ns.scope.lookup ("Tuple"));
}
current_symbol = root_symbol;
diff --git a/vala/valasetliteral.vala b/vala/valasetliteral.vala
new file mode 100644
index 0000000..4f490a5
--- /dev/null
+++ b/vala/valasetliteral.vala
@@ -0,0 +1,97 @@
+/* valasetliteral.vala
+ *
+ * Copyright (C) 2009 Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Jürg Billeter <j bitron ch>
+ */
+
+public class Vala.SetLiteral : Literal {
+ private List<Expression> expression_list = new ArrayList<Expression> ();
+
+ public DataType element_type { get; private set; }
+
+ public SetLiteral (SourceReference? source_reference = null) {
+ this.source_reference = source_reference;
+ }
+
+ public override void accept_children (CodeVisitor visitor) {
+ foreach (Expression expr in expression_list) {
+ expr.accept (visitor);
+ }
+ }
+
+ public override void accept (CodeVisitor visitor) {
+ visitor.visit_set_literal (this);
+ }
+
+ public void add_expression (Expression expr) {
+ expression_list.add (expr);
+ expr.parent_node = this;
+ }
+
+ public List<Expression> get_expressions () {
+ return expression_list;
+ }
+
+ public override bool is_pure () {
+ return false;
+ }
+
+ public override void replace_expression (Expression old_node, Expression new_node) {
+ for (int i = 0; i < expression_list.size; i++) {
+ if (expression_list[i] == old_node) {
+ expression_list[i] = new_node;
+ }
+ }
+ }
+
+ public override bool check (SemanticAnalyzer analyzer) {
+ if (checked) {
+ return !error;
+ }
+
+ checked = true;
+
+ var set_type = new ObjectType ((Class) analyzer.context.root.scope.lookup ("Dova").scope.lookup ("Set"));
+ set_type.value_owned = true;
+
+ bool fixed_element_type = false;
+ if (target_type != null && target_type.data_type == set_type.data_type && target_type.get_type_arguments ().size == 1) {
+ element_type = target_type.get_type_arguments ().get (0).copy ();
+ fixed_element_type = true;
+ }
+
+ foreach (var expr in expression_list) {
+ if (fixed_element_type) {
+ expr.target_type = element_type;
+ }
+ if (!expr.check (analyzer)) {
+ return false;
+ }
+ if (element_type == null) {
+ element_type = expr.value_type.copy ();
+ }
+ }
+
+ element_type.value_owned = true;
+ set_type.add_type_argument (element_type);
+ value_type = set_type;
+
+ return !error;
+ }
+}
diff --git a/vala/valatuple.vala b/vala/valatuple.vala
index b723d39..a842ee6 100644
--- a/vala/valatuple.vala
+++ b/vala/valatuple.vala
@@ -32,6 +32,16 @@ public class Vala.Tuple : Expression {
this.source_reference = source_reference;
}
+ public override void accept_children (CodeVisitor visitor) {
+ foreach (Expression expr in expression_list) {
+ expr.accept (visitor);
+ }
+ }
+
+ public override void accept (CodeVisitor visitor) {
+ visitor.visit_tuple (this);
+ }
+
public void add_expression (Expression expr) {
expression_list.add (expr);
}
@@ -44,6 +54,14 @@ public class Vala.Tuple : Expression {
return false;
}
+ public override void replace_expression (Expression old_node, Expression new_node) {
+ for (int i = 0; i < expression_list.size; i++) {
+ if (expression_list[i] == old_node) {
+ expression_list[i] = new_node;
+ }
+ }
+ }
+
public override bool check (SemanticAnalyzer analyzer) {
if (checked) {
return !error;
@@ -51,9 +69,22 @@ public class Vala.Tuple : Expression {
checked = true;
- Report.error (source_reference, "tuples are not supported");
- error = true;
- return false;
+ if (analyzer.context.profile != Profile.DOVA) {
+ Report.error (source_reference, "tuples are not supported");
+ error = true;
+ return false;
+ }
+
+ value_type = new ObjectType ((Class) analyzer.context.root.scope.lookup ("Dova").scope.lookup ("Tuple"));
+ value_type.value_owned = true;
+
+ foreach (var expr in expression_list) {
+ if (!expr.check (analyzer)) {
+ return false;
+ }
+ value_type.add_type_argument (expr.value_type.copy ());
+ }
+
+ return !error;
}
}
-
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]