2008-10-20 16:03:40 -06:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
2008-10-14 19:14:01 -06:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2008-10-20 16:03:40 -06:00
|
|
|
package AST
|
2008-10-14 19:14:01 -06:00
|
|
|
|
2008-11-19 17:49:50 -07:00
|
|
|
import (
|
|
|
|
"array";
|
|
|
|
Scanner "scanner";
|
2009-02-06 12:10:25 -07:00
|
|
|
SymbolTable "symboltable";
|
2008-11-19 17:49:50 -07:00
|
|
|
)
|
2008-10-14 19:14:01 -06:00
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type (
|
2009-01-23 10:44:01 -07:00
|
|
|
Block struct;
|
2009-02-03 18:44:01 -07:00
|
|
|
Expr interface;
|
2008-10-14 19:14:01 -06:00
|
|
|
Decl struct;
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2009-01-23 10:44:01 -07:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Support
|
|
|
|
|
|
|
|
func assert(pred bool) {
|
|
|
|
if !pred {
|
|
|
|
panic("assertion failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-23 14:50:14 -07:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// All nodes have a source position and a token.
|
|
|
|
|
|
|
|
type Node struct {
|
|
|
|
Pos int; // source position (< 0 => unknown position)
|
|
|
|
Tok int; // identifying token
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-16 13:16:50 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Types
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
const /* form */ (
|
2009-01-09 17:28:09 -07:00
|
|
|
// BADTYPE types are compatible with any type and don't cause further errors.
|
|
|
|
// They are introduced only as a result of an error in the source code. A
|
|
|
|
// correct program cannot have BAD types.
|
2009-02-06 12:10:25 -07:00
|
|
|
BADTYPE = iota;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
|
|
|
// A type name
|
|
|
|
TYPENAME;
|
|
|
|
|
|
|
|
// composite types
|
2009-02-06 12:10:25 -07:00
|
|
|
ARRAY; STRUCT; INTERFACE; MAP; CHANNEL; FUNCTION; POINTER;
|
2009-01-20 15:40:40 -07:00
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
// open-ended parameter type
|
|
|
|
ELLIPSIS
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func FormStr(form int) string {
|
2009-01-09 17:28:09 -07:00
|
|
|
switch form {
|
|
|
|
case BADTYPE: return "BADTYPE";
|
|
|
|
case TYPENAME: return "TYPENAME";
|
|
|
|
case ARRAY: return "ARRAY";
|
|
|
|
case STRUCT: return "STRUCT";
|
|
|
|
case INTERFACE: return "INTERFACE";
|
|
|
|
case MAP: return "MAP";
|
|
|
|
case CHANNEL: return "CHANNEL";
|
|
|
|
case FUNCTION: return "FUNCTION";
|
|
|
|
case POINTER: return "POINTER";
|
|
|
|
case ELLIPSIS: return "ELLIPSIS";
|
|
|
|
}
|
|
|
|
return "<unknown Type form>";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
const /* channel mode */ (
|
2008-10-16 13:16:50 -06:00
|
|
|
FULL = iota;
|
|
|
|
SEND;
|
|
|
|
RECV;
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Type struct {
|
2009-01-15 18:16:41 -07:00
|
|
|
Id int; // unique id
|
2009-01-09 17:28:09 -07:00
|
|
|
|
2009-01-15 18:16:41 -07:00
|
|
|
Form int; // type form
|
|
|
|
Size int; // size in bytes
|
2009-02-06 12:10:25 -07:00
|
|
|
Scope *SymbolTable.Scope; // locals, fields & methods
|
2009-01-09 17:28:09 -07:00
|
|
|
|
|
|
|
// syntactic components
|
2009-01-15 18:16:41 -07:00
|
|
|
Pos int; // source position (< 0 if unknown position)
|
2009-02-03 18:44:01 -07:00
|
|
|
Expr Expr; // type name, array length
|
2009-01-15 18:16:41 -07:00
|
|
|
Mode int; // channel mode
|
|
|
|
Key *Type; // receiver type or map key
|
2009-01-23 14:50:14 -07:00
|
|
|
Elt *Type; // type name type, array, map, channel or pointer element type, function result type
|
2009-01-15 18:16:41 -07:00
|
|
|
List *array.Array; End int; // struct fields, interface methods, function parameters
|
2008-10-16 13:16:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-15 18:16:41 -07:00
|
|
|
var typeId int;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewType(pos, form int) *Type {
|
2009-01-09 17:28:09 -07:00
|
|
|
typ := new(Type);
|
2009-01-15 18:16:41 -07:00
|
|
|
typ.Id = typeId;
|
|
|
|
typeId++;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
2009-01-15 18:16:41 -07:00
|
|
|
typ.Pos = pos;
|
|
|
|
typ.Form = form;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
|
|
|
return typ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-23 14:50:14 -07:00
|
|
|
func (typ* Type) String() string {
|
|
|
|
if typ != nil {
|
|
|
|
return
|
|
|
|
"Type(" +
|
|
|
|
FormStr(typ.Form) +
|
|
|
|
")";
|
|
|
|
}
|
|
|
|
return "nil";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-03 18:44:01 -07:00
|
|
|
var BadType = NewType(0, Scanner.ILLEGAL);
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Expressions
|
|
|
|
|
|
|
|
type (
|
2009-02-04 19:28:41 -07:00
|
|
|
ExprVisitor interface;
|
2009-02-03 18:44:01 -07:00
|
|
|
|
|
|
|
Expr interface {
|
|
|
|
Pos() int;
|
2009-02-04 19:28:41 -07:00
|
|
|
Visit(v ExprVisitor);
|
2009-02-03 18:44:01 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
BadExpr struct {
|
|
|
|
Pos_ int;
|
|
|
|
};
|
|
|
|
|
|
|
|
Ident struct {
|
|
|
|
Pos_ int;
|
2009-02-06 12:10:25 -07:00
|
|
|
Obj *SymbolTable.Object;
|
2009-02-03 18:44:01 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
BinaryExpr struct {
|
|
|
|
Pos_, Tok int;
|
|
|
|
X, Y Expr;
|
|
|
|
};
|
|
|
|
|
|
|
|
UnaryExpr struct {
|
|
|
|
Pos_, Tok int;
|
|
|
|
X Expr;
|
|
|
|
};
|
|
|
|
|
|
|
|
BasicLit struct {
|
|
|
|
Pos_, Tok int;
|
|
|
|
Val string
|
|
|
|
};
|
|
|
|
|
|
|
|
FunctionLit struct {
|
|
|
|
Pos_ int; // position of "func"
|
|
|
|
Typ *Type;
|
|
|
|
Body *Block;
|
|
|
|
};
|
|
|
|
|
|
|
|
CompositeLit struct {
|
|
|
|
Pos_ int; // position of "{"
|
|
|
|
Typ *Type;
|
|
|
|
Elts Expr;
|
|
|
|
};
|
|
|
|
|
|
|
|
TypeLit struct {
|
|
|
|
Typ *Type;
|
|
|
|
};
|
|
|
|
|
|
|
|
Selector struct {
|
|
|
|
Pos_ int; // position of "."
|
|
|
|
X Expr;
|
|
|
|
Sel *Ident;
|
|
|
|
};
|
|
|
|
|
|
|
|
TypeGuard struct {
|
|
|
|
Pos_ int; // position of "."
|
|
|
|
X Expr;
|
|
|
|
Typ *Type;
|
|
|
|
};
|
|
|
|
|
|
|
|
Index struct {
|
|
|
|
Pos_ int; // position of "["
|
|
|
|
X, I Expr;
|
|
|
|
};
|
|
|
|
|
|
|
|
Call struct {
|
|
|
|
Pos_ int; // position of "("
|
|
|
|
F, Args Expr
|
|
|
|
};
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2009-02-04 19:28:41 -07:00
|
|
|
type ExprVisitor interface {
|
2009-02-03 18:44:01 -07:00
|
|
|
DoBadExpr(x *BadExpr);
|
|
|
|
DoIdent(x *Ident);
|
|
|
|
DoBinaryExpr(x *BinaryExpr);
|
|
|
|
DoUnaryExpr(x *UnaryExpr);
|
|
|
|
DoBasicLit(x *BasicLit);
|
|
|
|
DoFunctionLit(x *FunctionLit);
|
|
|
|
DoCompositeLit(x *CompositeLit);
|
|
|
|
DoTypeLit(x *TypeLit);
|
|
|
|
DoSelector(x *Selector);
|
|
|
|
DoTypeGuard(x *TypeGuard);
|
|
|
|
DoIndex(x *Index);
|
|
|
|
DoCall(x *Call);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (x *BadExpr) Pos() int { return x.Pos_; }
|
|
|
|
func (x *Ident) Pos() int { return x.Pos_; }
|
|
|
|
func (x *BinaryExpr) Pos() int { return x.Pos_; }
|
|
|
|
func (x *UnaryExpr) Pos() int { return x.Pos_; }
|
|
|
|
func (x *BasicLit) Pos() int { return x.Pos_; }
|
|
|
|
func (x *FunctionLit) Pos() int { return x.Pos_; }
|
|
|
|
func (x *CompositeLit) Pos() int { return x.Pos_; }
|
|
|
|
func (x *TypeLit) Pos() int { return x.Typ.Pos; }
|
|
|
|
func (x *Selector) Pos() int { return x.Pos_; }
|
|
|
|
func (x *TypeGuard) Pos() int { return x.Pos_; }
|
|
|
|
func (x *Index) Pos() int { return x.Pos_; }
|
|
|
|
func (x *Call) Pos() int { return x.Pos_; }
|
|
|
|
|
|
|
|
|
2009-02-04 19:28:41 -07:00
|
|
|
func (x *BadExpr) Visit(v ExprVisitor) { v.DoBadExpr(x); }
|
|
|
|
func (x *Ident) Visit(v ExprVisitor) { v.DoIdent(x); }
|
|
|
|
func (x *BinaryExpr) Visit(v ExprVisitor) { v.DoBinaryExpr(x); }
|
|
|
|
func (x *UnaryExpr) Visit(v ExprVisitor) { v.DoUnaryExpr(x); }
|
|
|
|
func (x *BasicLit) Visit(v ExprVisitor) { v.DoBasicLit(x); }
|
|
|
|
func (x *FunctionLit) Visit(v ExprVisitor) { v.DoFunctionLit(x); }
|
|
|
|
func (x *CompositeLit) Visit(v ExprVisitor) { v.DoCompositeLit(x); }
|
|
|
|
func (x *TypeLit) Visit(v ExprVisitor) { v.DoTypeLit(x); }
|
|
|
|
func (x *Selector) Visit(v ExprVisitor) { v.DoSelector(x); }
|
|
|
|
func (x *TypeGuard) Visit(v ExprVisitor) { v.DoTypeGuard(x); }
|
|
|
|
func (x *Index) Visit(v ExprVisitor) { v.DoIndex(x); }
|
|
|
|
func (x *Call) Visit(v ExprVisitor) { v.DoCall(x); }
|
2009-02-03 18:44:01 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Length of a comma-separated expression list.
|
|
|
|
func ExprLen(x Expr) int {
|
|
|
|
if x == nil {
|
|
|
|
return 0;
|
2009-01-23 14:50:14 -07:00
|
|
|
}
|
2009-02-03 18:44:01 -07:00
|
|
|
n := 1;
|
|
|
|
for {
|
|
|
|
if p, ok := x.(*BinaryExpr); ok && p.Tok == Scanner.COMMA {
|
|
|
|
n++;
|
|
|
|
x = p.Y;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n;
|
2009-01-23 14:50:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-03 18:44:01 -07:00
|
|
|
func ExprAt(x Expr, i int) Expr {
|
|
|
|
for j := 0; j < i; j++ {
|
|
|
|
assert(x.(*BinaryExpr).Tok == Scanner.COMMA);
|
|
|
|
x = x.(*BinaryExpr).Y;
|
|
|
|
}
|
|
|
|
if t, is_binary := x.(*BinaryExpr); is_binary && t.Tok == Scanner.COMMA {
|
|
|
|
x = t.X;
|
|
|
|
}
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (t *Type) Nfields() int {
|
|
|
|
if t.List == nil {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
nx, nt := 0, 0;
|
|
|
|
for i, n := 0, t.List.Len(); i < n; i++ {
|
|
|
|
if dummy, ok := t.List.At(i).(*TypeLit); ok {
|
|
|
|
nt++;
|
|
|
|
} else {
|
|
|
|
nx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nx == 0 {
|
|
|
|
return nt;
|
|
|
|
}
|
|
|
|
return nx;
|
|
|
|
}
|
2008-10-17 17:19:31 -06:00
|
|
|
|
|
|
|
|
2009-02-05 12:05:02 -07:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Blocks
|
|
|
|
//
|
|
|
|
// Syntactic constructs of the form:
|
|
|
|
//
|
|
|
|
// "{" StatementList "}"
|
|
|
|
// ":" StatementList
|
|
|
|
|
|
|
|
type Block struct {
|
|
|
|
Node;
|
|
|
|
List *array.Array;
|
|
|
|
End int; // position of closing "}" if present
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewBlock(pos, tok int) *Block {
|
|
|
|
assert(tok == Scanner.LBRACE || tok == Scanner.COLON);
|
|
|
|
b := new(Block);
|
|
|
|
b.Pos, b.Tok, b.List = pos, tok, array.New(0);
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-14 19:14:01 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Statements
|
|
|
|
|
2009-02-04 19:28:41 -07:00
|
|
|
type (
|
|
|
|
StatVisitor interface;
|
|
|
|
|
|
|
|
Stat interface {
|
|
|
|
Visit(v StatVisitor);
|
|
|
|
};
|
|
|
|
|
|
|
|
BadStat struct {
|
|
|
|
Pos int;
|
|
|
|
};
|
|
|
|
|
|
|
|
LabelDecl struct {
|
|
|
|
Pos int; // position of ":"
|
|
|
|
Label *Ident;
|
|
|
|
};
|
|
|
|
|
|
|
|
DeclarationStat struct {
|
|
|
|
Decl *Decl;
|
|
|
|
};
|
|
|
|
|
|
|
|
ExpressionStat struct {
|
|
|
|
Pos int; // position of Tok
|
|
|
|
Tok int; // INC, DEC, RETURN, GO, DEFER
|
|
|
|
Expr Expr;
|
|
|
|
};
|
2009-02-05 12:05:02 -07:00
|
|
|
|
|
|
|
CompositeStat struct {
|
|
|
|
Body *Block;
|
|
|
|
};
|
|
|
|
|
2009-02-04 19:28:41 -07:00
|
|
|
IfStat struct {
|
|
|
|
Pos int; // position of "if"
|
|
|
|
Init Stat;
|
|
|
|
Cond Expr;
|
|
|
|
Body *Block;
|
|
|
|
Else Stat;
|
|
|
|
};
|
|
|
|
|
|
|
|
ForStat struct {
|
|
|
|
Pos int; // position of "for"
|
|
|
|
Init Stat;
|
|
|
|
Cond Expr;
|
|
|
|
Post Stat;
|
|
|
|
Body *Block;
|
|
|
|
};
|
2009-02-05 12:05:02 -07:00
|
|
|
|
|
|
|
CaseClause struct {
|
|
|
|
Pos int; // position for "case" or "default"
|
|
|
|
Expr Expr; // nil means default case
|
|
|
|
Body *Block;
|
|
|
|
};
|
|
|
|
|
2009-02-04 19:28:41 -07:00
|
|
|
SwitchStat struct {
|
|
|
|
Pos int; // position of "switch"
|
|
|
|
Init Stat;
|
|
|
|
Tag Expr;
|
|
|
|
Body *Block;
|
|
|
|
};
|
|
|
|
|
|
|
|
SelectStat struct {
|
|
|
|
Pos int; // position of "select"
|
|
|
|
Body *Block;
|
|
|
|
};
|
|
|
|
|
|
|
|
ControlFlowStat struct {
|
|
|
|
Pos int; // position of Tok
|
|
|
|
Tok int; // BREAK, CONTINUE, GOTO, FALLTHROUGH
|
|
|
|
Label *Ident; // if any, or nil
|
|
|
|
};
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type StatVisitor interface {
|
|
|
|
DoBadStat(s *BadStat);
|
|
|
|
DoLabelDecl(s *LabelDecl);
|
|
|
|
DoDeclarationStat(s *DeclarationStat);
|
|
|
|
DoExpressionStat(s *ExpressionStat);
|
2009-02-05 12:05:02 -07:00
|
|
|
DoCompositeStat(s *CompositeStat);
|
2009-02-04 19:28:41 -07:00
|
|
|
DoIfStat(s *IfStat);
|
|
|
|
DoForStat(s *ForStat);
|
2009-02-05 12:05:02 -07:00
|
|
|
DoCaseClause(s *CaseClause);
|
2009-02-04 19:28:41 -07:00
|
|
|
DoSwitchStat(s *SwitchStat);
|
|
|
|
DoSelectStat(s *SelectStat);
|
|
|
|
DoControlFlowStat(s *ControlFlowStat);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *BadStat) Visit(v StatVisitor) { v.DoBadStat(s); }
|
|
|
|
func (s *LabelDecl) Visit(v StatVisitor) { v.DoLabelDecl(s); }
|
|
|
|
func (s *DeclarationStat) Visit(v StatVisitor) { v.DoDeclarationStat(s); }
|
|
|
|
func (s *ExpressionStat) Visit(v StatVisitor) { v.DoExpressionStat(s); }
|
2009-02-05 12:05:02 -07:00
|
|
|
func (s *CompositeStat) Visit(v StatVisitor) { v.DoCompositeStat(s); }
|
2009-02-04 19:28:41 -07:00
|
|
|
func (s *IfStat) Visit(v StatVisitor) { v.DoIfStat(s); }
|
|
|
|
func (s *ForStat) Visit(v StatVisitor) { v.DoForStat(s); }
|
2009-02-05 12:05:02 -07:00
|
|
|
func (s *CaseClause) Visit(v StatVisitor) { v.DoCaseClause(s); }
|
2009-02-04 19:28:41 -07:00
|
|
|
func (s *SwitchStat) Visit(v StatVisitor) { v.DoSwitchStat(s); }
|
|
|
|
func (s *SelectStat) Visit(v StatVisitor) { v.DoSelectStat(s); }
|
|
|
|
func (s *ControlFlowStat) Visit(v StatVisitor) { v.DoControlFlowStat(s); }
|
|
|
|
|
|
|
|
|
2008-10-14 19:14:01 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Declarations
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Decl struct {
|
2008-10-23 18:56:54 -06:00
|
|
|
Node;
|
2009-02-03 18:44:01 -07:00
|
|
|
Ident Expr; // nil for ()-style declarations
|
2009-01-15 18:16:41 -07:00
|
|
|
Typ *Type;
|
2009-02-03 18:44:01 -07:00
|
|
|
Val Expr;
|
|
|
|
Body *Block;
|
2008-10-14 19:14:01 -06:00
|
|
|
// list of *Decl for ()-style declarations
|
2009-01-15 18:16:41 -07:00
|
|
|
List *array.Array; End int;
|
2008-10-14 19:14:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 16:22:33 -07:00
|
|
|
func NewDecl(pos, tok int) *Decl {
|
2009-01-06 16:01:04 -07:00
|
|
|
d := new(Decl);
|
2009-01-20 16:22:33 -07:00
|
|
|
d.Pos, d.Tok = pos, tok;
|
2008-10-14 19:14:01 -06:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 16:22:33 -07:00
|
|
|
var BadDecl = NewDecl(0, Scanner.ILLEGAL);
|
2008-10-17 17:19:31 -06:00
|
|
|
|
|
|
|
|
2008-10-14 19:14:01 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Program
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Comment struct {
|
2009-01-15 18:16:41 -07:00
|
|
|
Pos int;
|
|
|
|
Text string;
|
2008-10-17 00:30:42 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewComment(pos int, text string) *Comment {
|
2009-01-06 16:01:04 -07:00
|
|
|
c := new(Comment);
|
2009-01-15 18:16:41 -07:00
|
|
|
c.Pos, c.Text = pos, text;
|
2008-10-17 00:30:42 -06:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Program struct {
|
2009-01-15 18:16:41 -07:00
|
|
|
Pos int; // tok is Scanner.PACKAGE
|
2009-02-03 18:44:01 -07:00
|
|
|
Ident Expr;
|
2009-01-15 18:16:41 -07:00
|
|
|
Decls *array.Array;
|
|
|
|
Comments *array.Array;
|
2008-10-14 19:14:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewProgram(pos int) *Program {
|
2009-01-06 16:01:04 -07:00
|
|
|
p := new(Program);
|
2009-01-15 18:16:41 -07:00
|
|
|
p.Pos = pos;
|
2008-10-14 19:14:01 -06:00
|
|
|
return p;
|
|
|
|
}
|