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";
|
2009-01-15 15:19:35 -07:00
|
|
|
"utf8";
|
|
|
|
"unicode";
|
2008-11-19 17:49:50 -07:00
|
|
|
Scanner "scanner";
|
|
|
|
)
|
2008-10-14 19:14:01 -06:00
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type (
|
2009-01-09 17:28:09 -07:00
|
|
|
Object struct;
|
2008-10-14 19:14:01 -06:00
|
|
|
Type struct;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
2008-10-14 19:14:01 -06:00
|
|
|
Expr struct;
|
|
|
|
Stat struct;
|
|
|
|
Decl struct;
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Objects
|
|
|
|
|
|
|
|
// Object represents a language object, such as a constant, variable, type, etc.
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
const /* kind */ (
|
2009-01-09 17:28:09 -07:00
|
|
|
BADOBJ = iota; // error handling
|
|
|
|
NONE; // kind unknown
|
|
|
|
CONST; TYPE; VAR; FIELD; FUNC; BUILTIN; PACKAGE; LABEL;
|
|
|
|
END; // end of scope (import/export only)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func KindStr(kind int) string {
|
2009-01-09 17:28:09 -07:00
|
|
|
switch kind {
|
|
|
|
case BADOBJ: return "BADOBJ";
|
|
|
|
case NONE: return "NONE";
|
|
|
|
case CONST: return "CONST";
|
|
|
|
case TYPE: return "TYPE";
|
|
|
|
case VAR: return "VAR";
|
|
|
|
case FIELD: return "FIELD";
|
|
|
|
case FUNC: return "FUNC";
|
|
|
|
case BUILTIN: return "BUILTIN";
|
|
|
|
case PACKAGE: return "PACKAGE";
|
|
|
|
case LABEL: return "LABEL";
|
|
|
|
case END: return "END";
|
|
|
|
}
|
|
|
|
return "<unknown Object kind>";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Object 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
|
|
|
Pos int; // source position (< 0 if unknown position)
|
|
|
|
Kind int; // object kind
|
|
|
|
Ident string;
|
|
|
|
Typ *Type; // nil for packages
|
|
|
|
Pnolev int; // >= 0: package no., <= 0: function nesting level, 0: global level
|
2009-01-20 15:40:40 -07:00
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
// attached values
|
2009-01-15 18:16:41 -07:00
|
|
|
Block *array.Array; End int; // stats for function literals; end of block pos
|
2009-01-09 17:28:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-15 15:19:35 -07:00
|
|
|
func (obj *Object) IsExported() bool {
|
2009-01-15 18:16:41 -07:00
|
|
|
switch obj.Kind {
|
2009-01-15 15:19:35 -07:00
|
|
|
case NONE /* FUNC for now */, CONST, TYPE, VAR, FUNC:
|
2009-01-15 18:16:41 -07:00
|
|
|
ch, size := utf8.DecodeRuneInString(obj.Ident, 0);
|
2009-01-15 15:19:35 -07:00
|
|
|
return unicode.IsUpper(ch);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
var Universe_void_typ *Type // initialized by Universe to Universe.void_typ
|
2009-01-15 18:16:41 -07:00
|
|
|
var objectId int;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewObject(pos, kind int, ident string) *Object {
|
2009-01-09 17:28:09 -07:00
|
|
|
obj := new(Object);
|
2009-01-15 18:16:41 -07:00
|
|
|
obj.Id = objectId;
|
|
|
|
objectId++;
|
2009-01-20 15:40:40 -07:00
|
|
|
|
2009-01-15 18:16:41 -07:00
|
|
|
obj.Pos = pos;
|
|
|
|
obj.Kind = kind;
|
|
|
|
obj.Ident = ident;
|
|
|
|
obj.Typ = Universe_void_typ;
|
|
|
|
obj.Pnolev = 0;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Scopes
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Scope struct {
|
2009-01-16 16:31:34 -07:00
|
|
|
Parent *Scope;
|
2009-01-09 17:28:09 -07:00
|
|
|
entries map[string] *Object;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewScope(parent *Scope) *Scope {
|
2009-01-09 17:28:09 -07:00
|
|
|
scope := new(Scope);
|
2009-01-16 16:31:34 -07:00
|
|
|
scope.Parent = parent;
|
2009-01-09 17:28:09 -07:00
|
|
|
scope.entries = make(map[string]*Object, 8);
|
|
|
|
return scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (scope *Scope) LookupLocal(ident string) *Object {
|
|
|
|
obj, found := scope.entries[ident];
|
|
|
|
if found {
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (scope *Scope) Lookup(ident string) *Object {
|
|
|
|
for scope != nil {
|
|
|
|
obj := scope.LookupLocal(ident);
|
|
|
|
if obj != nil {
|
|
|
|
return obj;
|
|
|
|
}
|
2009-01-16 16:31:34 -07:00
|
|
|
scope = scope.Parent;
|
2009-01-09 17:28:09 -07:00
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-15 18:16:41 -07:00
|
|
|
func (scope *Scope) add(obj* Object) {
|
|
|
|
scope.entries[obj.Ident] = obj;
|
2009-01-09 17:28:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (scope *Scope) Insert(obj *Object) {
|
2009-01-15 18:16:41 -07:00
|
|
|
if scope.LookupLocal(obj.Ident) != nil {
|
2009-01-09 17:28:09 -07:00
|
|
|
panic("obj already inserted");
|
|
|
|
}
|
2009-01-15 18:16:41 -07:00
|
|
|
scope.add(obj);
|
2009-01-09 17:28:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (scope *Scope) InsertImport(obj *Object) *Object {
|
2009-01-15 18:16:41 -07:00
|
|
|
p := scope.LookupLocal(obj.Ident);
|
2009-01-09 17:28:09 -07:00
|
|
|
if p == nil {
|
2009-01-15 18:16:41 -07:00
|
|
|
scope.add(obj);
|
2009-01-09 17:28:09 -07:00
|
|
|
p = obj;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (scope *Scope) Print() {
|
|
|
|
print("scope {");
|
|
|
|
for key := range scope.entries {
|
|
|
|
print("\n ", key);
|
|
|
|
}
|
|
|
|
print("\n}\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-23 18:56:54 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// All nodes have a source position and and token.
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Node struct {
|
2009-01-15 18:16:41 -07:00
|
|
|
Pos int; // source position (< 0 => unknown position)
|
|
|
|
Tok int; // identifying token
|
2009-01-07 14:58:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-14 19:14:01 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Expressions
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Expr struct {
|
2008-10-23 18:56:54 -06:00
|
|
|
Node;
|
2009-01-15 18:16:41 -07:00
|
|
|
X, Y *Expr; // binary (X, Y) and unary (Y) expressions
|
|
|
|
Obj *Object;
|
2008-10-14 19:14:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-19 17:49:50 -07:00
|
|
|
func (x *Expr) Len() int {
|
2008-10-14 19:14:01 -06:00
|
|
|
if x == nil {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
n := 1;
|
2009-01-15 18:16:41 -07:00
|
|
|
for ; x.Tok == Scanner.COMMA; x = x.Y {
|
2008-10-14 19:14:01 -06:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewExpr(pos, tok int, x, y *Expr) *Expr {
|
2009-01-15 18:16:41 -07:00
|
|
|
if x != nil && x.Tok == Scanner.TYPE || y != nil && y.Tok == Scanner.TYPE {
|
2008-10-17 00:30:42 -06:00
|
|
|
panic("no type expression allowed");
|
|
|
|
}
|
2009-01-06 16:01:04 -07:00
|
|
|
e := new(Expr);
|
2009-01-15 18:16:41 -07:00
|
|
|
e.Pos, e.Tok, e.X, e.Y = pos, tok, x, y;
|
2008-10-14 19:14:01 -06:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-12 18:44:10 -07:00
|
|
|
// TODO probably don't need the tok parameter eventually
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewLit(tok int, obj *Object) *Expr {
|
2009-01-06 16:01:04 -07:00
|
|
|
e := new(Expr);
|
2009-01-15 18:16:41 -07:00
|
|
|
e.Pos, e.Tok, e.Obj = obj.Pos, tok, obj;
|
2008-10-14 19:14:01 -06:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
var BadExpr = NewExpr(0, Scanner.ILLEGAL, nil, nil);
|
2008-10-17 17:19:31 -06:00
|
|
|
|
|
|
|
|
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
|
|
|
// internal types
|
|
|
|
// We should never see one of these.
|
|
|
|
UNDEF = iota;
|
2009-01-20 15:40:40 -07:00
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
// VOID types are used when we don't have a type. Never exported.
|
|
|
|
// (exported type forms must be > 0)
|
|
|
|
VOID;
|
2009-01-20 15:40:40 -07:00
|
|
|
|
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.
|
|
|
|
BADTYPE;
|
2009-01-20 15:40:40 -07:00
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
// FORWARD types are forward-declared (incomplete) types. They can only
|
|
|
|
// be used as element types of pointer types and must be resolved before
|
|
|
|
// their internals are accessible.
|
|
|
|
FORWARD;
|
|
|
|
|
|
|
|
// TUPLE types represent multi-valued result types of functions and
|
|
|
|
// methods.
|
|
|
|
TUPLE;
|
2009-01-20 15:40:40 -07:00
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
// The type of nil.
|
|
|
|
NIL;
|
|
|
|
|
|
|
|
// A type name
|
|
|
|
TYPENAME;
|
|
|
|
|
|
|
|
// basic types
|
|
|
|
BOOL; UINT; INT; FLOAT; STRING; INTEGER;
|
2009-01-20 15:40:40 -07:00
|
|
|
|
2009-01-09 17:28:09 -07:00
|
|
|
// composite types
|
|
|
|
ALIAS; ARRAY; STRUCT; INTERFACE; MAP; CHANNEL; FUNCTION; METHOD; 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 VOID: return "VOID";
|
|
|
|
case BADTYPE: return "BADTYPE";
|
|
|
|
case FORWARD: return "FORWARD";
|
|
|
|
case TUPLE: return "TUPLE";
|
|
|
|
case NIL: return "NIL";
|
|
|
|
case TYPENAME: return "TYPENAME";
|
|
|
|
case BOOL: return "BOOL";
|
|
|
|
case UINT: return "UINT";
|
|
|
|
case INT: return "INT";
|
|
|
|
case FLOAT: return "FLOAT";
|
|
|
|
case STRING: return "STRING";
|
|
|
|
case ALIAS: return "ALIAS";
|
|
|
|
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 METHOD: return "METHOD";
|
|
|
|
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
|
|
|
Ref int; // for exporting only: >= 0 means already exported
|
|
|
|
Form int; // type form
|
|
|
|
Size int; // size in bytes
|
|
|
|
Obj *Object; // primary type object or NULL
|
|
|
|
Scope *Scope; // forwards, structs, interfaces, functions
|
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)
|
|
|
|
Expr *Expr; // type name, array length
|
|
|
|
Mode int; // channel mode
|
|
|
|
Key *Type; // receiver type or map key
|
|
|
|
Elt *Type; // array, map, channel or pointer element type, function result type
|
|
|
|
List *array.Array; End int; // struct fields, interface methods, function parameters
|
|
|
|
Scope *Scope; // struct fields, methods
|
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.Ref = -1; // not yet exported
|
|
|
|
typ.Pos = pos;
|
|
|
|
typ.Form = form;
|
2009-01-09 17:28:09 -07:00
|
|
|
|
|
|
|
return typ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-15 18:16:41 -07:00
|
|
|
func (t *Type) Nfields() int {
|
|
|
|
if t.List == nil {
|
2008-11-19 17:49:50 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2008-10-16 13:16:50 -06:00
|
|
|
nx, nt := 0, 0;
|
2009-01-15 18:16:41 -07:00
|
|
|
for i, n := 0, t.List.Len(); i < n; i++ {
|
|
|
|
if t.List.At(i).(*Expr).Tok == Scanner.TYPE {
|
2008-10-16 13:16:50 -06:00
|
|
|
nt++;
|
|
|
|
} else {
|
|
|
|
nx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nx == 0 {
|
|
|
|
return nt;
|
|
|
|
}
|
|
|
|
return nx;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-15 18:16:41 -07:00
|
|
|
// requires complete Type.Pos access
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewTypeExpr(typ *Type) *Expr {
|
2009-01-15 18:16:41 -07:00
|
|
|
obj := NewObject(typ.Pos, TYPE, "");
|
|
|
|
obj.Typ = typ;
|
2009-01-12 18:44:10 -07:00
|
|
|
return NewLit(Scanner.TYPE, obj);
|
2008-10-15 18:06:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
var BadType = NewType(0, Scanner.ILLEGAL);
|
2008-10-17 17:19:31 -06:00
|
|
|
|
|
|
|
|
2008-10-14 19:14:01 -06:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Statements
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
type Stat struct {
|
2008-10-23 18:56:54 -06:00
|
|
|
Node;
|
2009-01-15 18:16:41 -07:00
|
|
|
Init, Post *Stat;
|
|
|
|
Expr *Expr;
|
|
|
|
Block *array.Array; End int; // block end position
|
|
|
|
Decl *Decl;
|
2008-10-14 19:14:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
func NewStat(pos, tok int) *Stat {
|
2009-01-06 16:01:04 -07:00
|
|
|
s := new(Stat);
|
2009-01-15 18:16:41 -07:00
|
|
|
s.Pos, s.Tok = pos, tok;
|
2008-10-14 19:14:01 -06:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
var BadStat = NewStat(0, Scanner.ILLEGAL);
|
2008-10-17 17:19:31 -06:00
|
|
|
|
|
|
|
|
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-01-15 18:16:41 -07:00
|
|
|
Ident *Expr; // nil for ()-style declarations
|
|
|
|
Typ *Type;
|
|
|
|
Val *Expr;
|
2008-10-14 19:14:01 -06:00
|
|
|
// list of *Decl for ()-style declarations
|
|
|
|
// list of *Stat for func declarations (or nil for forward decl)
|
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
|
|
|
|
Ident *Expr;
|
|
|
|
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;
|
|
|
|
}
|