OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [go/] [doc/] [reader.go] - Blame information for rev 747

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 747 jeremybenn
// Copyright 2009 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
 
5
package doc
6
 
7
import (
8
        "go/ast"
9
        "go/token"
10
        "regexp"
11
        "sort"
12
        "strconv"
13
)
14
 
15
// ----------------------------------------------------------------------------
16
// function/method sets
17
//
18
// Internally, we treat functions like methods and collect them in method sets.
19
 
20
// methodSet describes a set of methods. Entries where Decl == nil are conflict
21
// entries (more then one method with the same name at the same embedding level).
22
//
23
type methodSet map[string]*Func
24
 
25
// recvString returns a string representation of recv of the
26
// form "T", "*T", or "BADRECV" (if not a proper receiver type).
27
//
28
func recvString(recv ast.Expr) string {
29
        switch t := recv.(type) {
30
        case *ast.Ident:
31
                return t.Name
32
        case *ast.StarExpr:
33
                return "*" + recvString(t.X)
34
        }
35
        return "BADRECV"
36
}
37
 
38
// set creates the corresponding Func for f and adds it to mset.
39
// If there are multiple f's with the same name, set keeps the first
40
// one with documentation; conflicts are ignored.
41
//
42
func (mset methodSet) set(f *ast.FuncDecl) {
43
        name := f.Name.Name
44
        if g := mset[name]; g != nil && g.Doc != "" {
45
                // A function with the same name has already been registered;
46
                // since it has documentation, assume f is simply another
47
                // implementation and ignore it. This does not happen if the
48
                // caller is using go/build.ScanDir to determine the list of
49
                // files implementing a package.
50
                return
51
        }
52
        // function doesn't exist or has no documentation; use f
53
        recv := ""
54
        if f.Recv != nil {
55
                var typ ast.Expr
56
                // be careful in case of incorrect ASTs
57
                if list := f.Recv.List; len(list) == 1 {
58
                        typ = list[0].Type
59
                }
60
                recv = recvString(typ)
61
        }
62
        mset[name] = &Func{
63
                Doc:  f.Doc.Text(),
64
                Name: name,
65
                Decl: f,
66
                Recv: recv,
67
                Orig: recv,
68
        }
69
        f.Doc = nil // doc consumed - remove from AST
70
}
71
 
72
// add adds method m to the method set; m is ignored if the method set
73
// already contains a method with the same name at the same or a higher
74
// level then m.
75
//
76
func (mset methodSet) add(m *Func) {
77
        old := mset[m.Name]
78
        if old == nil || m.Level < old.Level {
79
                mset[m.Name] = m
80
                return
81
        }
82
        if old != nil && m.Level == old.Level {
83
                // conflict - mark it using a method with nil Decl
84
                mset[m.Name] = &Func{
85
                        Name:  m.Name,
86
                        Level: m.Level,
87
                }
88
        }
89
}
90
 
91
// ----------------------------------------------------------------------------
92
// Named types
93
 
94
// baseTypeName returns the name of the base type of x (or "")
95
// and whether the type is imported or not.
96
//
97
func baseTypeName(x ast.Expr) (name string, imported bool) {
98
        switch t := x.(type) {
99
        case *ast.Ident:
100
                return t.Name, false
101
        case *ast.SelectorExpr:
102
                if _, ok := t.X.(*ast.Ident); ok {
103
                        // only possible for qualified type names;
104
                        // assume type is imported
105
                        return t.Sel.Name, true
106
                }
107
        case *ast.StarExpr:
108
                return baseTypeName(t.X)
109
        }
110
        return
111
}
112
 
113
// A namedType represents a named unqualified (package local, or possibly
114
// predeclared) type. The namedType for a type name is always found via
115
// reader.lookupType.
116
//
117
type namedType struct {
118
        doc  string       // doc comment for type
119
        name string       // type name
120
        decl *ast.GenDecl // nil if declaration hasn't been seen yet
121
 
122
        isEmbedded bool                // true if this type is embedded
123
        isStruct   bool                // true if this type is a struct
124
        embedded   map[*namedType]bool // true if the embedded type is a pointer
125
 
126
        // associated declarations
127
        values  []*Value // consts and vars
128
        funcs   methodSet
129
        methods methodSet
130
}
131
 
132
// ----------------------------------------------------------------------------
133
// AST reader
134
 
135
// reader accumulates documentation for a single package.
136
// It modifies the AST: Comments (declaration documentation)
137
// that have been collected by the reader are set to nil
138
// in the respective AST nodes so that they are not printed
139
// twice (once when printing the documentation and once when
140
// printing the corresponding AST node).
141
//
142
type reader struct {
143
        mode Mode
144
 
145
        // package properties
146
        doc       string // package documentation, if any
147
        filenames []string
148
        bugs      []string
149
 
150
        // declarations
151
        imports map[string]int
152
        values  []*Value // consts and vars
153
        types   map[string]*namedType
154
        funcs   methodSet
155
}
156
 
157
func (r *reader) isVisible(name string) bool {
158
        return r.mode&AllDecls != 0 || ast.IsExported(name)
159
}
160
 
161
// lookupType returns the base type with the given name.
162
// If the base type has not been encountered yet, a new
163
// type with the given name but no associated declaration
164
// is added to the type map.
165
//
166
func (r *reader) lookupType(name string) *namedType {
167
        if name == "" || name == "_" {
168
                return nil // no type docs for anonymous types
169
        }
170
        if typ, found := r.types[name]; found {
171
                return typ
172
        }
173
        // type not found - add one without declaration
174
        typ := &namedType{
175
                name:     name,
176
                embedded: make(map[*namedType]bool),
177
                funcs:    make(methodSet),
178
                methods:  make(methodSet),
179
        }
180
        r.types[name] = typ
181
        return typ
182
}
183
 
184
// recordAnonymousField registers fieldType as the type of an
185
// anonymous field in the parent type. If the field is imported
186
// (qualified name) or the parent is nil, the field is ignored.
187
// The function returns the field name.
188
//
189
func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {
190
        fname, imp := baseTypeName(fieldType)
191
        if parent == nil || imp {
192
                return
193
        }
194
        if ftype := r.lookupType(fname); ftype != nil {
195
                ftype.isEmbedded = true
196
                _, ptr := fieldType.(*ast.StarExpr)
197
                parent.embedded[ftype] = ptr
198
        }
199
        return
200
}
201
 
202
func (r *reader) readDoc(comment *ast.CommentGroup) {
203
        // By convention there should be only one package comment
204
        // but collect all of them if there are more then one.
205
        text := comment.Text()
206
        if r.doc == "" {
207
                r.doc = text
208
                return
209
        }
210
        r.doc += "\n" + text
211
}
212
 
213
func specNames(specs []ast.Spec) []string {
214
        names := make([]string, 0, len(specs)) // reasonable estimate
215
        for _, s := range specs {
216
                // s guaranteed to be an *ast.ValueSpec by readValue
217
                for _, ident := range s.(*ast.ValueSpec).Names {
218
                        names = append(names, ident.Name)
219
                }
220
        }
221
        return names
222
}
223
 
224
// readValue processes a const or var declaration.
225
//
226
func (r *reader) readValue(decl *ast.GenDecl) {
227
        // determine if decl should be associated with a type
228
        // Heuristic: For each typed entry, determine the type name, if any.
229
        //            If there is exactly one type name that is sufficiently
230
        //            frequent, associate the decl with the respective type.
231
        domName := ""
232
        domFreq := 0
233
        prev := ""
234
        n := 0
235
        for _, spec := range decl.Specs {
236
                s, ok := spec.(*ast.ValueSpec)
237
                if !ok {
238
                        continue // should not happen, but be conservative
239
                }
240
                name := ""
241
                switch {
242
                case s.Type != nil:
243
                        // a type is present; determine its name
244
                        if n, imp := baseTypeName(s.Type); !imp {
245
                                name = n
246
                        }
247
                case decl.Tok == token.CONST:
248
                        // no type is present but we have a constant declaration;
249
                        // use the previous type name (w/o more type information
250
                        // we cannot handle the case of unnamed variables with
251
                        // initializer expressions except for some trivial cases)
252
                        name = prev
253
                }
254
                if name != "" {
255
                        // entry has a named type
256
                        if domName != "" && domName != name {
257
                                // more than one type name - do not associate
258
                                // with any type
259
                                domName = ""
260
                                break
261
                        }
262
                        domName = name
263
                        domFreq++
264
                }
265
                prev = name
266
                n++
267
        }
268
 
269
        // nothing to do w/o a legal declaration
270
        if n == 0 {
271
                return
272
        }
273
 
274
        // determine values list with which to associate the Value for this decl
275
        values := &r.values
276
        const threshold = 0.75
277
        if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
278
                // typed entries are sufficiently frequent
279
                if typ := r.lookupType(domName); typ != nil {
280
                        values = &typ.values // associate with that type
281
                }
282
        }
283
 
284
        *values = append(*values, &Value{
285
                Doc:   decl.Doc.Text(),
286
                Names: specNames(decl.Specs),
287
                Decl:  decl,
288
                order: len(*values),
289
        })
290
        decl.Doc = nil // doc consumed - remove from AST
291
}
292
 
293
// fields returns a struct's fields or an interface's methods.
294
//
295
func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {
296
        var fields *ast.FieldList
297
        switch t := typ.(type) {
298
        case *ast.StructType:
299
                fields = t.Fields
300
                isStruct = true
301
        case *ast.InterfaceType:
302
                fields = t.Methods
303
        }
304
        if fields != nil {
305
                list = fields.List
306
        }
307
        return
308
}
309
 
310
// readType processes a type declaration.
311
//
312
func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
313
        typ := r.lookupType(spec.Name.Name)
314
        if typ == nil {
315
                return // no name or blank name - ignore the type
316
        }
317
 
318
        // A type should be added at most once, so info.decl
319
        // should be nil - if it is not, simply overwrite it.
320
        typ.decl = decl
321
 
322
        // compute documentation
323
        doc := spec.Doc
324
        spec.Doc = nil // doc consumed - remove from AST
325
        if doc == nil {
326
                // no doc associated with the spec, use the declaration doc, if any
327
                doc = decl.Doc
328
        }
329
        decl.Doc = nil // doc consumed - remove from AST
330
        typ.doc = doc.Text()
331
 
332
        // record anonymous fields (they may contribute methods)
333
        // (some fields may have been recorded already when filtering
334
        // exports, but that's ok)
335
        var list []*ast.Field
336
        list, typ.isStruct = fields(spec.Type)
337
        for _, field := range list {
338
                if len(field.Names) == 0 {
339
                        r.recordAnonymousField(typ, field.Type)
340
                }
341
        }
342
}
343
 
344
// readFunc processes a func or method declaration.
345
//
346
func (r *reader) readFunc(fun *ast.FuncDecl) {
347
        // strip function body
348
        fun.Body = nil
349
 
350
        // associate methods with the receiver type, if any
351
        if fun.Recv != nil {
352
                // method
353
                recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
354
                if imp {
355
                        // should not happen (incorrect AST);
356
                        // don't show this method
357
                        return
358
                }
359
                if typ := r.lookupType(recvTypeName); typ != nil {
360
                        typ.methods.set(fun)
361
                }
362
                // otherwise ignore the method
363
                // TODO(gri): There may be exported methods of non-exported types
364
                // that can be called because of exported values (consts, vars, or
365
                // function results) of that type. Could determine if that is the
366
                // case and then show those methods in an appropriate section.
367
                return
368
        }
369
 
370
        // associate factory functions with the first visible result type, if any
371
        if fun.Type.Results.NumFields() >= 1 {
372
                res := fun.Type.Results.List[0]
373
                if len(res.Names) <= 1 {
374
                        // exactly one (named or anonymous) result associated
375
                        // with the first type in result signature (there may
376
                        // be more than one result)
377
                        if n, imp := baseTypeName(res.Type); !imp && r.isVisible(n) {
378
                                if typ := r.lookupType(n); typ != nil {
379
                                        // associate function with typ
380
                                        typ.funcs.set(fun)
381
                                        return
382
                                }
383
                        }
384
                }
385
        }
386
 
387
        // just an ordinary function
388
        r.funcs.set(fun)
389
}
390
 
391
var (
392
        bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):
393
        bug_content = regexp.MustCompile("[^ \n\r\t]+")                    // at least one non-whitespace char
394
)
395
 
396
// readFile adds the AST for a source file to the reader.
397
//
398
func (r *reader) readFile(src *ast.File) {
399
        // add package documentation
400
        if src.Doc != nil {
401
                r.readDoc(src.Doc)
402
                src.Doc = nil // doc consumed - remove from AST
403
        }
404
 
405
        // add all declarations
406
        for _, decl := range src.Decls {
407
                switch d := decl.(type) {
408
                case *ast.GenDecl:
409
                        switch d.Tok {
410
                        case token.IMPORT:
411
                                // imports are handled individually
412
                                for _, spec := range d.Specs {
413
                                        if s, ok := spec.(*ast.ImportSpec); ok {
414
                                                if import_, err := strconv.Unquote(s.Path.Value); err == nil {
415
                                                        r.imports[import_] = 1
416
                                                }
417
                                        }
418
                                }
419
                        case token.CONST, token.VAR:
420
                                // constants and variables are always handled as a group
421
                                r.readValue(d)
422
                        case token.TYPE:
423
                                // types are handled individually
424
                                for _, spec := range d.Specs {
425
                                        if s, ok := spec.(*ast.TypeSpec); ok {
426
                                                // use an individual (possibly fake) declaration
427
                                                // for each type; this also ensures that each type
428
                                                // gets to (re-)use the declaration documentation
429
                                                // if there's none associated with the spec itself
430
                                                fake := &ast.GenDecl{
431
                                                        d.Doc, d.Pos(), token.TYPE, token.NoPos,
432
                                                        []ast.Spec{s}, token.NoPos,
433
                                                }
434
                                                r.readType(fake, s)
435
                                        }
436
                                }
437
                        }
438
                case *ast.FuncDecl:
439
                        r.readFunc(d)
440
                }
441
        }
442
 
443
        // collect BUG(...) comments
444
        for _, c := range src.Comments {
445
                text := c.List[0].Text
446
                if m := bug_markers.FindStringIndex(text); m != nil {
447
                        // found a BUG comment; maybe empty
448
                        if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
449
                                // non-empty BUG comment; collect comment without BUG prefix
450
                                list := append([]*ast.Comment(nil), c.List...) // make a copy
451
                                list[0].Text = text[m[1]:]
452
                                r.bugs = append(r.bugs, (&ast.CommentGroup{list}).Text())
453
                        }
454
                }
455
        }
456
        src.Comments = nil // consumed unassociated comments - remove from AST
457
}
458
 
459
func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
460
        // initialize reader
461
        r.filenames = make([]string, len(pkg.Files))
462
        r.imports = make(map[string]int)
463
        r.mode = mode
464
        r.types = make(map[string]*namedType)
465
        r.funcs = make(methodSet)
466
 
467
        // sort package files before reading them so that the
468
        // result result does not depend on map iteration order
469
        i := 0
470
        for filename := range pkg.Files {
471
                r.filenames[i] = filename
472
                i++
473
        }
474
        sort.Strings(r.filenames)
475
 
476
        // process files in sorted order
477
        for _, filename := range r.filenames {
478
                f := pkg.Files[filename]
479
                if mode&AllDecls == 0 {
480
                        r.fileExports(f)
481
                }
482
                r.readFile(f)
483
        }
484
}
485
 
486
// ----------------------------------------------------------------------------
487
// Types
488
 
489
var predeclaredTypes = map[string]bool{
490
        "bool":       true,
491
        "byte":       true,
492
        "complex64":  true,
493
        "complex128": true,
494
        "error":      true,
495
        "float32":    true,
496
        "float64":    true,
497
        "int":        true,
498
        "int8":       true,
499
        "int16":      true,
500
        "int32":      true,
501
        "int64":      true,
502
        "rune":       true,
503
        "string":     true,
504
        "uint":       true,
505
        "uint8":      true,
506
        "uint16":     true,
507
        "uint32":     true,
508
        "uint64":     true,
509
        "uintptr":    true,
510
}
511
 
512
func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
513
        if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
514
                return f // shouldn't happen, but be safe
515
        }
516
 
517
        // copy existing receiver field and set new type
518
        newField := *f.Decl.Recv.List[0]
519
        _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
520
        var typ ast.Expr = ast.NewIdent(recvTypeName)
521
        if !embeddedIsPtr && origRecvIsPtr {
522
                typ = &ast.StarExpr{token.NoPos, typ}
523
        }
524
        newField.Type = typ
525
 
526
        // copy existing receiver field list and set new receiver field
527
        newFieldList := *f.Decl.Recv
528
        newFieldList.List = []*ast.Field{&newField}
529
 
530
        // copy existing function declaration and set new receiver field list
531
        newFuncDecl := *f.Decl
532
        newFuncDecl.Recv = &newFieldList
533
 
534
        // copy existing function documentation and set new declaration
535
        newF := *f
536
        newF.Decl = &newFuncDecl
537
        newF.Recv = recvString(typ)
538
        // the Orig field never changes
539
        newF.Level = level
540
 
541
        return &newF
542
}
543
 
544
// collectEmbeddedMethods collects the embedded methods of typ in mset.
545
//
546
func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int) {
547
        for embedded, isPtr := range typ.embedded {
548
                // Once an embedded type is embedded as a pointer type
549
                // all embedded types in those types are treated like
550
                // pointer types for the purpose of the receiver type
551
                // computation; i.e., embeddedIsPtr is sticky for this
552
                // embedding hierarchy.
553
                thisEmbeddedIsPtr := embeddedIsPtr || isPtr
554
                for _, m := range embedded.methods {
555
                        // only top-level methods are embedded
556
                        if m.Level == 0 {
557
                                mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
558
                        }
559
                }
560
                r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1)
561
        }
562
}
563
 
564
// computeMethodSets determines the actual method sets for each type encountered.
565
//
566
func (r *reader) computeMethodSets() {
567
        for _, t := range r.types {
568
                // collect embedded methods for t
569
                if t.isStruct {
570
                        // struct
571
                        r.collectEmbeddedMethods(t.methods, t, t.name, false, 1)
572
                } else {
573
                        // interface
574
                        // TODO(gri) fix this
575
                }
576
        }
577
}
578
 
579
// cleanupTypes removes the association of functions and methods with
580
// types that have no declaration. Instead, these functions and methods
581
// are shown at the package level. It also removes types with missing
582
// declarations or which are not visible.
583
//
584
func (r *reader) cleanupTypes() {
585
        for _, t := range r.types {
586
                visible := r.isVisible(t.name)
587
                if t.decl == nil && (predeclaredTypes[t.name] || t.isEmbedded && visible) {
588
                        // t.name is a predeclared type (and was not redeclared in this package),
589
                        // or it was embedded somewhere but its declaration is missing (because
590
                        // the AST is incomplete): move any associated values, funcs, and methods
591
                        // back to the top-level so that they are not lost.
592
                        // 1) move values
593
                        r.values = append(r.values, t.values...)
594
                        // 2) move factory functions
595
                        for name, f := range t.funcs {
596
                                // in a correct AST, package-level function names
597
                                // are all different - no need to check for conflicts
598
                                r.funcs[name] = f
599
                        }
600
                        // 3) move methods
601
                        for name, m := range t.methods {
602
                                // don't overwrite functions with the same name - drop them
603
                                if _, found := r.funcs[name]; !found {
604
                                        r.funcs[name] = m
605
                                }
606
                        }
607
                }
608
                // remove types w/o declaration or which are not visible
609
                if t.decl == nil || !visible {
610
                        delete(r.types, t.name)
611
                }
612
        }
613
}
614
 
615
// ----------------------------------------------------------------------------
616
// Sorting
617
 
618
type data struct {
619
        n    int
620
        swap func(i, j int)
621
        less func(i, j int) bool
622
}
623
 
624
func (d *data) Len() int           { return d.n }
625
func (d *data) Swap(i, j int)      { d.swap(i, j) }
626
func (d *data) Less(i, j int) bool { return d.less(i, j) }
627
 
628
// sortBy is a helper function for sorting
629
func sortBy(less func(i, j int) bool, swap func(i, j int), n int) {
630
        sort.Sort(&data{n, swap, less})
631
}
632
 
633
func sortedKeys(m map[string]int) []string {
634
        list := make([]string, len(m))
635
        i := 0
636
        for key := range m {
637
                list[i] = key
638
                i++
639
        }
640
        sort.Strings(list)
641
        return list
642
}
643
 
644
// sortingName returns the name to use when sorting d into place.
645
//
646
func sortingName(d *ast.GenDecl) string {
647
        if len(d.Specs) == 1 {
648
                if s, ok := d.Specs[0].(*ast.ValueSpec); ok {
649
                        return s.Names[0].Name
650
                }
651
        }
652
        return ""
653
}
654
 
655
func sortedValues(m []*Value, tok token.Token) []*Value {
656
        list := make([]*Value, len(m)) // big enough in any case
657
        i := 0
658
        for _, val := range m {
659
                if val.Decl.Tok == tok {
660
                        list[i] = val
661
                        i++
662
                }
663
        }
664
        list = list[0:i]
665
 
666
        sortBy(
667
                func(i, j int) bool {
668
                        if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {
669
                                return ni < nj
670
                        }
671
                        return list[i].order < list[j].order
672
                },
673
                func(i, j int) { list[i], list[j] = list[j], list[i] },
674
                len(list),
675
        )
676
 
677
        return list
678
}
679
 
680
func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {
681
        list := make([]*Type, len(m))
682
        i := 0
683
        for _, t := range m {
684
                list[i] = &Type{
685
                        Doc:     t.doc,
686
                        Name:    t.name,
687
                        Decl:    t.decl,
688
                        Consts:  sortedValues(t.values, token.CONST),
689
                        Vars:    sortedValues(t.values, token.VAR),
690
                        Funcs:   sortedFuncs(t.funcs, true),
691
                        Methods: sortedFuncs(t.methods, allMethods),
692
                }
693
                i++
694
        }
695
 
696
        sortBy(
697
                func(i, j int) bool { return list[i].Name < list[j].Name },
698
                func(i, j int) { list[i], list[j] = list[j], list[i] },
699
                len(list),
700
        )
701
 
702
        return list
703
}
704
 
705
func removeStar(s string) string {
706
        if len(s) > 0 && s[0] == '*' {
707
                return s[1:]
708
        }
709
        return s
710
}
711
 
712
func sortedFuncs(m methodSet, allMethods bool) []*Func {
713
        list := make([]*Func, len(m))
714
        i := 0
715
        for _, m := range m {
716
                // determine which methods to include
717
                switch {
718
                case m.Decl == nil:
719
                        // exclude conflict entry
720
                case allMethods, m.Level == 0, !ast.IsExported(removeStar(m.Orig)):
721
                        // forced inclusion, method not embedded, or method
722
                        // embedded but original receiver type not exported
723
                        list[i] = m
724
                        i++
725
                }
726
        }
727
        list = list[0:i]
728
        sortBy(
729
                func(i, j int) bool { return list[i].Name < list[j].Name },
730
                func(i, j int) { list[i], list[j] = list[j], list[i] },
731
                len(list),
732
        )
733
        return list
734
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.