6 Commits

Author SHA1 Message Date
amery 123770119e parser: TextParser [WIP]
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-02 22:04:41 +00:00
amery 667803fea5 reflective: IsZero()
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-02 22:04:41 +00:00
amery a38f0c74e8 reflective: Add Reflection{} placeholder and the New() factory
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-02 16:18:56 +00:00
amery 7e0b7cfb47 reflective: introduce UnmarshalTypeError{}
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-02 16:14:11 +00:00
amery fd4f6439b8 reflective: introduce InvalidUnmarshalError{}
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-02 16:13:48 +00:00
amery f74745471d reflective: add placeholder for reflection assistant
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-02 15:48:08 +00:00
12 changed files with 153 additions and 207 deletions
+3 -5
View File
@@ -2,22 +2,20 @@ module asciigoat.org/core
go 1.19
require (
github.com/fatih/structtag v1.2.0
github.com/mgechev/revive v1.3.3
github.com/pkg/errors v0.9.1
)
require github.com/mgechev/revive v1.3.3
require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/tools v0.12.0 // indirect
)
+2
View File
@@ -0,0 +1,2 @@
// Package parser implements some common logic of asciigoat based parsers
package parser
+72
View File
@@ -0,0 +1,72 @@
package parser
import (
"bytes"
"io"
"strings"
"asciigoat.org/core/lexer"
)
// TextParser ...
type TextParser struct {
src *lexer.Reader
pos lexer.Position
}
// Position ...
func (p *TextParser) Position() lexer.Position {
return p.pos
}
// StepLine ...
func (p *TextParser) StepLine() {
p.src.Discard()
p.pos.StepLine()
}
// Step ...
func (p *TextParser) Step() {
s := p.src.Emit()
p.pos.StepN(len(s))
}
// Emit ...
func (p *TextParser) Emit() Token {
t := Token{
Value: p.src.Emit(),
Position: p.pos,
}
p.pos.StepN(t.Len())
return t
}
// Peek ...
func (p *TextParser) Peek() string {
return p.src.String()
}
// NewTextParser ...
func NewTextParser(r io.Reader) *TextParser {
if r == nil {
r = strings.NewReader("")
}
p := &TextParser{
src: lexer.NewReader(r),
}
p.pos.Reset()
return p
}
// NewTextParserBytes ...
func NewTextParserBytes(b []byte) *TextParser {
return NewTextParser(bytes.NewReader(b))
}
// NewTextParserString ...
func NewTextParserString(s string) *TextParser {
return NewTextParser(strings.NewReader(s))
}
+31
View File
@@ -0,0 +1,31 @@
package parser
import (
"fmt"
"asciigoat.org/core/lexer"
)
// Token ...
type Token struct {
lexer.Position
Type uint
Value string
}
// Len ...
func (t Token) Len() int {
return len(t.Value)
}
// String generates the '%v' value
func (t Token) String() string {
return t.Value
}
// GoString generates the '%#v' value
func (t Token) GoString() string {
return fmt.Sprintf("Token{Position{%v,%v}, %v, %q}",
t.Line, t.Column, t.Type, t.Value)
}
-16
View File
@@ -1,16 +0,0 @@
package reflection
import (
"fmt"
"reflect"
)
func structFieldName(sf reflect.StructField) string {
if sf.Anonymous {
idx := sf.Index[len(sf.Index)-1]
return fmt.Sprintf("%s-%v", "anonymous", idx)
}
return sf.Name
}
-40
View File
@@ -1,40 +0,0 @@
// Package reflection helps Marshalling/Unmarshalling data to structs
package reflection
import (
"reflect"
)
// Reflection provides Marshalling/Unmarshalling oriented view
// of a value
type Reflection struct {
v reflect.Value
types map[reflect.Type]TypeInfo
}
// Value returns the object it reflects
func (r *Reflection) Value() any {
return r.v.Interface()
}
// New creates a Reflection of the given pointer
func New(v any) (*Reflection, error) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Pointer || rv.IsNil() {
err := &InvalidUnmarshalError{Type: rv.Type()}
return nil, err
}
r := &Reflection{
v: rv,
types: make(map[reflect.Type]TypeInfo),
}
if err := r.scan(); err != nil {
return nil, err
}
return r, nil
}
-37
View File
@@ -1,37 +0,0 @@
package reflection
import (
"reflect"
)
func (r *Reflection) scanType(rt reflect.Type) error {
switch rt.Kind() {
case reflect.Pointer, reflect.Slice:
rt = rt.Elem()
}
if _, ok := r.types[rt]; ok {
// cached
return nil
}
ti, err := NewTypeInfo(rt)
if err != nil {
return &UnmarshalTypeError{Type: rt, Err: err}
}
r.types[rt] = ti
for i := range ti.fields {
err := r.scanType(ti.fields[i].sf.Type)
if err != nil {
return err
}
}
return nil
}
func (r *Reflection) scan() error {
return r.scanType(r.v.Type())
}
-108
View File
@@ -1,108 +0,0 @@
package reflection
import (
"log"
"reflect"
"github.com/fatih/structtag"
"github.com/pkg/errors"
)
// TypeInfo ...
type TypeInfo struct {
rt reflect.Type
fields []TypeInfoField
}
// Type ...
func (ti *TypeInfo) Type() reflect.Type {
return ti.rt
}
// Kind ...
func (ti *TypeInfo) Kind() reflect.Kind {
return ti.rt.Kind()
}
// TypeInfoField ...
type TypeInfoField struct {
sf reflect.StructField
tags *structtag.Tags
Name string
}
// Type ...
func (tif *TypeInfoField) Type() reflect.Type {
return tif.sf.Type
}
// Kind ...
func (tif *TypeInfoField) Kind() reflect.Kind {
return tif.sf.Type.Kind()
}
// Tag ...
func (tif *TypeInfoField) Tag(key string) (*structtag.Tag, bool) {
if tif.tags != nil {
tag, _ := tif.tags.Get(key)
if tag != nil {
return tag, true
}
}
return nil, false
}
// NewTypeInfo ...
func NewTypeInfo(rt reflect.Type) (TypeInfo, error) {
log.Printf("%s.%s: %s (%s)", "reflection", "NewTypeInfo", rt, rt.Kind())
ti := TypeInfo{
rt: rt,
}
err := ti.init()
return ti, err
}
func (ti *TypeInfo) init() error {
switch ti.Kind() {
case reflect.Struct:
return ti.initStruct()
default:
return nil
}
}
func (ti *TypeInfo) initStruct() error {
// load fields
n := ti.rt.NumField()
fields := make([]TypeInfoField, 0, n)
for i := 0; i < n; i++ {
sf := ti.rt.Field(i)
tags, err := structtag.Parse(string(sf.Tag))
if err != nil {
// tag parse error
err = &UnmarshalTypeError{
Type: ti.rt,
Err: errors.Wrap(err, structFieldName(sf)),
}
return err
}
if sf.IsExported() {
log.Printf("%s.%s: [%v]: %q %s (%s)",
"reflection", "NewTypeInfo",
i, structFieldName(sf), sf.Type, sf.Type.Kind())
fields = append(fields, TypeInfoField{
sf: sf,
tags: tags,
})
}
}
ti.fields = fields
return nil
}
+2
View File
@@ -0,0 +1,2 @@
// Package reflective assists reflection-driven unmarshalling
package reflective
+1 -1
View File
@@ -1,4 +1,4 @@
package reflection
package reflective
import (
"bytes"
+15
View File
@@ -0,0 +1,15 @@
package reflective
import "reflect"
// IsZero tells if a value is zero either via
// v.IsZero() or reflection.
func IsZero(iv any) bool {
if p, ok := iv.(interface {
IsZero() bool
}); ok {
return p.IsZero()
}
return reflect.ValueOf(iv).IsZero()
}
+27
View File
@@ -0,0 +1,27 @@
package reflective
import "reflect"
// Reflection provides Marshalling/Unmarshalling oriented view
// of a value
type Reflection struct {
v reflect.Value
}
// New creates a Reflection of the given pointer
func New(v any) (*Reflection, error) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Pointer || rv.IsNil() {
err := &InvalidUnmarshalError{
Type: rv.Type(),
}
return nil, err
}
r := &Reflection{
v: rv,
}
return r, nil
}