diff --git a/demo/dql-yaml/yq.xgo b/demo/dql-yaml/yq.xgo new file mode 100644 index 000000000..084ebc96a --- /dev/null +++ b/demo/dql-yaml/yq.xgo @@ -0,0 +1,12 @@ +config := yaml`server: localhost +port: 8080 +features: + - auth + - logging +`! + +echo config.port + +for feature in config.features.* { + echo feature._value +} diff --git a/dql/html/html.go b/dql/html/html.go index 0542d6062..269f552e4 100644 --- a/dql/html/html.go +++ b/dql/html/html.go @@ -478,6 +478,29 @@ func (p NodeSet) Collect() ([]*Node, error) { return dql.Collect(p.Data), nil } +// Name returns the name of the first node in the NodeSet. +// empty string is returned if the NodeSet is empty or the first node is +// not an element node. +func (p NodeSet) Name__0() string { + val, _ := p.Name__1() + return val +} + +// Name returns the name of the first node in the NodeSet. +// If the NodeSet is empty or the first node is not an element node, it +// returns ErrNotFound. +func (p NodeSet) Name__1() (ret string, err error) { + node, err := p.XGo_first() + if err == nil { + if node.Type == html.ElementNode { + ret = node.Data + } else { + err = dql.ErrNotFound // not an element node + } + } + return +} + // Value returns the data content of the first node in the NodeSet. func (p NodeSet) Value__0() string { val, _ := p.Value__1() diff --git a/dql/maps/maps.go b/dql/maps/maps.go index f2122c235..56e47d874 100644 --- a/dql/maps/maps.go +++ b/dql/maps/maps.go @@ -28,10 +28,10 @@ const ( // ----------------------------------------------------------------------------- -// Node represents a map[string]any or []any node. +// Node represents a named value in a DQL query tree. type Node struct { - Name string - Children any // map[string]any or []any + Name string + Value any } // NodeSet represents a set of nodes. @@ -78,7 +78,7 @@ func New(doc any) NodeSet { } return NodeSet{ Data: func(yield func(Node) bool) { - yield(Node{Name: "", Children: doc}) + yield(Node{Name: "", Value: doc}) }, } } @@ -156,23 +156,14 @@ func (p NodeSet) XGo_Elem(name string) NodeSet { // yieldElem yields the child node with the specified name if it exists. func yieldElem(node Node, name string, yield func(Node) bool) bool { - if children, ok := node.Children.(map[string]any); ok { + if children, ok := node.Value.(map[string]any); ok { if v, ok := children[name]; ok { - return yieldNode(name, v, yield) + return yield(Node{Name: name, Value: v}) } } return true } -// yieldNode yields a node if the value is a map[string]any or []any. -func yieldNode(name string, v any, yield func(Node) bool) bool { - switch v.(type) { - case map[string]any, []any: - return yield(Node{Name: name, Children: v}) - } - return true -} - // XGo_Child returns a NodeSet containing all child nodes of the nodes in the NodeSet. func (p NodeSet) XGo_Child() NodeSet { if p.Err != nil { @@ -189,16 +180,16 @@ func (p NodeSet) XGo_Child() NodeSet { // yieldChildNodes yields all child nodes of the given node. func yieldChildNodes(node Node, yield func(Node) bool) bool { - switch children := node.Children.(type) { + switch children := node.Value.(type) { case map[string]any: for k, v := range children { - if !yieldNode(k, v, yield) { + if !yield(Node{Name: k, Value: v}) { return false } } case []any: for _, v := range children { - if !yieldNode("", v, yield) { + if !yield(Node{Name: "", Value: v}) { return false } } @@ -233,7 +224,7 @@ func yieldAnyNodes(name string, node Node, yield func(Node) bool) bool { return false } } - switch children := node.Children.(type) { + switch children := node.Value.(type) { case map[string]any: for k, v := range children { if !yieldAnyNode(name, k, v, yield) { @@ -255,7 +246,7 @@ func yieldAnyNodes(name string, node Node, yield func(Node) bool) bool { func yieldAnyNode(name, k string, v any, yield func(Node) bool) bool { switch v.(type) { case map[string]any, []any: - return yieldAnyNodes(name, Node{Name: k, Children: v}, yield) + return yieldAnyNodes(name, Node{Name: k, Value: v}, yield) } return true } @@ -315,12 +306,46 @@ func (p NodeSet) XGo_first() (Node, error) { return dql.First(p.Data) } +// _name returns the name of the first node in the NodeSet. +// empty string is returned if the NodeSet is empty or error occurs. +func (p NodeSet) XGo_name__0() string { + val, _ := p.XGo_name__1() + return val +} + +// _name returns the name of the first node in the NodeSet. +// If the NodeSet is empty, it returns ErrNotFound. +func (p NodeSet) XGo_name__1() (ret string, err error) { + node, err := p.XGo_first() + if err == nil { + ret = node.Name + } + return +} + +// _value returns the value of the first node in the NodeSet. +// nil is returned if the NodeSet is empty or error occurs. +func (p NodeSet) XGo_value__0() any { + val, _ := p.XGo_value__1() + return val +} + +// _value returns the value of the first node in the NodeSet. +// If the NodeSet is empty, it returns ErrNotFound. +func (p NodeSet) XGo_value__1() (ret any, err error) { + node, err := p.XGo_first() + if err == nil { + ret = node.Value + } + return +} + // _hasAttr returns true if the first node in the NodeSet has the specified attribute. // It returns false otherwise. func (p NodeSet) XGo_hasAttr(name string) bool { node, err := p.XGo_first() if err == nil { - switch children := node.Children.(type) { + switch children := node.Value.(type) { case map[string]any: _, ok := children[name] return ok @@ -345,7 +370,7 @@ func (p NodeSet) XGo_Attr__0(name string) any { func (p NodeSet) XGo_Attr__1(name string) (val any, err error) { node, err := p.XGo_first() if err == nil { - switch children := node.Children.(type) { + switch children := node.Value.(type) { case map[string]any: if v, ok := children[name]; ok { return v, nil diff --git a/dql/reflects/reflects.go b/dql/reflects/reflects.go index b9e92e55f..f091831ed 100644 --- a/dql/reflects/reflects.go +++ b/dql/reflects/reflects.go @@ -49,13 +49,13 @@ func uncapitalize(name string) string { // ----------------------------------------------------------------------------- -// Node represents a reflect.Value node. +// Node represents a named value in a DQL query tree. type Node struct { - Name string - Children reflect.Value + Name string + Value reflect.Value } -// NodeSet represents a set of reflect.Value nodes. +// NodeSet represents a set of nodes. type NodeSet struct { Data iter.Seq[Node] Err error @@ -92,7 +92,7 @@ func Nodes(nodes ...Node) NodeSet { func New(doc reflect.Value) NodeSet { return NodeSet{ Data: func(yield func(Node) bool) { - yield(Node{Name: "", Children: doc}) + yield(Node{Name: "", Value: doc}) }, } } @@ -167,8 +167,8 @@ func (p NodeSet) XGo_Elem(name string) NodeSet { // yieldElem yields the child node with the specified name if it exists. func yieldElem(node Node, name string, yield func(Node) bool) bool { - if v := lookup(node.Children, name); isNode(v) { - return yield(Node{Name: name, Children: v}) + if v := lookup(node.Value, name); v.IsValid() { + return yield(Node{Name: name, Value: v}) } return true } @@ -197,65 +197,32 @@ func deref(v reflect.Value) (reflect.Kind, reflect.Value) { return kind, v } -func isNodeType(v reflect.Type) bool { - kind := v.Kind() - if kind == reflect.Pointer { - v = v.Elem() - kind = v.Kind() - } - switch kind { - case reflect.Struct, reflect.Map, reflect.Interface: - return true - case reflect.Slice: - ekind := v.Elem().Kind() - return ekind > reflect.Complex128 && ekind != reflect.String - } - return false -} - -func isNode(v reflect.Value) bool { - kind, v := deref(v) - switch kind { - case reflect.Struct, reflect.Map: - return true - case reflect.Slice: - ekind := v.Type().Elem().Kind() - return ekind > reflect.Complex128 && ekind != reflect.String - } - return false -} - func yieldChildNodes(node reflect.Value, yield func(Node) bool) bool { kind, node := deref(node) switch kind { case reflect.Struct: typ := node.Type() for i, n := 0, typ.NumField(); i < n; i++ { - v := node.Field(i) - if isNode(v) { - if !yield(Node{Name: uncapitalize(typ.Field(i).Name), Children: v}) { + if v := node.Field(i); v.CanInterface() { // only yield exported fields + if !yield(Node{Name: uncapitalize(typ.Field(i).Name), Value: v}) { return false } } } case reflect.Map: typ := node.Type() - if typ.Key().Kind() != reflect.String || !isNodeType(typ.Elem()) { + if typ.Key().Kind() != reflect.String { // Only support map[string]T break } it := node.MapRange() for it.Next() { - if !yield(Node{Name: it.Key().String(), Children: it.Value()}) { + if !yield(Node{Name: it.Key().String(), Value: it.Value()}) { return false } } case reflect.Slice: - elemType := node.Type().Elem() - if !isNodeType(elemType) { - break - } for i := 0; i < node.Len(); i++ { - if !yield(Node{Name: "", Children: node.Index(i)}) { + if !yield(Node{Name: "", Value: node.Index(i)}) { return false } } @@ -271,7 +238,7 @@ func yieldAnyNodes(name string, node Node, yield func(Node) bool) bool { return false } } - return yieldChildNodes(node.Children, func(n Node) bool { + return yieldChildNodes(node.Value, func(n Node) bool { return yieldAnyNodes(name, n, yield) }) } @@ -284,7 +251,7 @@ func (p NodeSet) XGo_Child() NodeSet { return NodeSet{ Data: func(yield func(Node) bool) { p.Data(func(node Node) bool { - return yieldChildNodes(node.Children, yield) + return yieldChildNodes(node.Value, yield) }) }, } @@ -364,12 +331,59 @@ func (p NodeSet) XGo_first() (Node, error) { return dql.First(p.Data) } +// _name returns the name of the first node in the NodeSet. +// empty string is returned if the NodeSet is empty or error occurs. +func (p NodeSet) XGo_name__0() string { + val, _ := p.XGo_name__1() + return val +} + +// _name returns the name of the first node in the NodeSet. +// If the NodeSet is empty, it returns ErrNotFound. +func (p NodeSet) XGo_name__1() (ret string, err error) { + node, err := p.XGo_first() + if err == nil { + ret = node.Name + } + return +} + +// _value returns the value of the first node in the NodeSet. +// nil is returned if the NodeSet is empty or error occurs. +func (p NodeSet) XGo_value__0() any { + val, _ := p.XGo_value__1() + return val +} + +// _value returns the value of the first node in the NodeSet. +// If the NodeSet is empty, it returns ErrNotFound. +func (p NodeSet) XGo_value__1() (ret any, err error) { + node, err := p.XGo_first() + if err == nil { + ret = node.Value.Interface() + } + return +} + +// XGo_class returns the class name of the first node in the NodeSet. +func (p NodeSet) XGo_class() (class string) { + node, err := p.XGo_first() + if err != nil { + return + } + _, v := deref(node.Value) + if v.IsValid() { + return v.Type().Name() + } + return "" +} + // _hasAttr returns true if the first node in the NodeSet has the specified attribute. // It returns false otherwise. func (p NodeSet) XGo_hasAttr(name string) bool { node, err := p.XGo_first() if err == nil { - return lookup(node.Children, name).IsValid() + return lookup(node.Value, name).IsValid() } return false } @@ -390,7 +404,7 @@ func (p NodeSet) XGo_Attr__0(name string) any { func (p NodeSet) XGo_Attr__1(name string) (val any, err error) { node, err := p.XGo_first() if err == nil { - if v := lookup(node.Children, name); v.IsValid() { + if v := lookup(node.Value, name); v.IsValid() { return v.Interface(), nil } err = dql.ErrNotFound @@ -398,17 +412,4 @@ func (p NodeSet) XGo_Attr__1(name string) (val any, err error) { return } -// XGo_class returns the class name of the first node in the NodeSet. -func (p NodeSet) XGo_class() (class string) { - node, err := p.XGo_first() - if err != nil { - return - } - _, v := deref(node.Children) - if v.IsValid() { - return v.Type().Name() - } - return "" -} - // -----------------------------------------------------------------------------