5991a21818
* First batch of changes for the refactor Added the support of gin in routes and other services/utils Begining implementation of JetHTML * Remove os folder * Move scrapers to own repo * Second batch of changes All .jet.html are the working templates. You can now test this PR, the index Page and upload works. If you want to complete the other html templates, you're welcome * Move captcha to util * Move uploadService to utils * Use govalidator instead of regex * Third batch of changes All the front end should as previously. I also fixed some minor things unrelated to the refactor (mostly style issues on static pages) Now errors can be accessed by importing the "errors" helpers and using the `yield errors(name="xxx")` command in templates. Same for infos. Templates are now more hierarchized with a base template "base.jet.html" which is extended depending on the context in "index_site" or "index_admin" layouts. Those layouts are extended than in every pages. Other helpers are captcha to render a captcha `yield captcha(captchaid="xxx")` And also csrf, with the command `yield csrf_field()` To translate, you don't have anymore to do `call $.T "xxx"`, you just have to do `T("xxx")`. Pages for the website part are in folders in the folder "templates/site". Pages for the admin part are in "templates/admin". Layouts are separated in "templates/layouts". Helpers and menu are in "templates/layouts/helpers" and "templates/layouts/menu". Error pages should be put in "templates/errors" * Added test on templates When adding a new template, you have to tell to template_test.go, the context of the new template (if it doesn't use the common context) * Panel admin works Now the templating part should work. The PR can now be fully tested. I think we should push the templating PR and do the routes/controllers/removal of services in another branch. So we know that this one is functional * Updated dependencies * Fixed test for modelhelper * Fix testing for commentlist * Fix travis :') * Just renamed router and removed network * Applying same SEO fix * Update form_validator.go * Added back regexp package
1612 lignes
40 Kio
Go
1612 lignes
40 Kio
Go
// Copyright 2016 José Santos <henrique_1609@me.com>
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package jet
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/CloudyKit/fastprinter"
|
|
"io"
|
|
"reflect"
|
|
"runtime"
|
|
"strconv"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
funcType = reflect.TypeOf(Func(nil))
|
|
stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
|
rangerType = reflect.TypeOf((*Ranger)(nil)).Elem()
|
|
rendererType = reflect.TypeOf((*Renderer)(nil)).Elem()
|
|
safeWriterType = reflect.TypeOf(SafeWriter(nil))
|
|
pool_State = sync.Pool{
|
|
New: func() interface{} {
|
|
return &Runtime{scope: &scope{}, escapeeWriter: new(escapeeWriter)}
|
|
},
|
|
}
|
|
)
|
|
|
|
// Renderer any resulting value from an expression in an action that implements this
|
|
// interface will not be printed, instead, we will invoke his Render() method which will be responsible
|
|
// to render his self
|
|
type Renderer interface {
|
|
Render(*Runtime)
|
|
}
|
|
|
|
// RendererFunc func implementing interface Renderer
|
|
type RendererFunc func(*Runtime)
|
|
|
|
func (renderer RendererFunc) Render(r *Runtime) {
|
|
renderer(r)
|
|
}
|
|
|
|
// Ranger a value implementing a ranger interface is able to iterate on his value
|
|
// and can be used directly in a range statement
|
|
type Ranger interface {
|
|
Range() (reflect.Value, reflect.Value, bool)
|
|
}
|
|
|
|
type escapeeWriter struct {
|
|
Writer io.Writer
|
|
escapee SafeWriter
|
|
set *Set
|
|
}
|
|
|
|
func (w *escapeeWriter) Write(b []byte) (int, error) {
|
|
if w.set.escapee == nil {
|
|
w.Writer.Write(b)
|
|
} else {
|
|
w.set.escapee(w.Writer, b)
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
// Runtime this type holds the state of the execution of an template
|
|
type Runtime struct {
|
|
*escapeeWriter
|
|
*scope
|
|
content func(*Runtime, Expression)
|
|
|
|
translator Translator
|
|
context reflect.Value
|
|
}
|
|
|
|
// Context returns the current context value
|
|
func (r *Runtime) Context() reflect.Value {
|
|
return r.context
|
|
}
|
|
|
|
func (st *Runtime) newScope() {
|
|
st.scope = &scope{parent: st.scope, variables: make(VarMap), blocks: st.blocks}
|
|
}
|
|
|
|
func (st *Runtime) releaseScope() {
|
|
st.scope = st.scope.parent
|
|
}
|
|
|
|
type scope struct {
|
|
parent *scope
|
|
variables VarMap
|
|
blocks map[string]*BlockNode
|
|
}
|
|
|
|
// YieldBlock yields a block in the current context, will panic if the context is not available
|
|
func (st *Runtime) YieldBlock(name string, context interface{}) {
|
|
block, has := st.getBlock(name)
|
|
|
|
if has == false {
|
|
panic(fmt.Errorf("Block %q was not found!!", name))
|
|
}
|
|
|
|
if context != nil {
|
|
current := st.context
|
|
st.context = reflect.ValueOf(context)
|
|
st.executeList(block.List)
|
|
st.context = current
|
|
}
|
|
|
|
st.executeList(block.List)
|
|
}
|
|
|
|
func (st *scope) getBlock(name string) (block *BlockNode, has bool) {
|
|
block, has = st.blocks[name]
|
|
for !has && st.parent != nil {
|
|
st = st.parent
|
|
block, has = st.blocks[name]
|
|
}
|
|
return
|
|
}
|
|
|
|
// YieldTemplate yields a template same as include
|
|
func (st *Runtime) YieldTemplate(name string, context interface{}) {
|
|
|
|
t, err := st.set.GetTemplate(name)
|
|
if err != nil {
|
|
panic(fmt.Errorf("include: template %q was not found", name))
|
|
}
|
|
|
|
st.newScope()
|
|
st.blocks = t.processedBlocks
|
|
|
|
Root := t.Root
|
|
if t.extends != nil {
|
|
Root = t.extends.Root
|
|
}
|
|
|
|
if context != nil {
|
|
c := st.context
|
|
st.context = reflect.ValueOf(context)
|
|
st.executeList(Root)
|
|
st.context = c
|
|
} else {
|
|
st.executeList(Root)
|
|
}
|
|
|
|
st.releaseScope()
|
|
}
|
|
|
|
// Set sets variable ${name} in the current template scope
|
|
func (state *Runtime) Set(name string, val interface{}) {
|
|
state.setValue(name, reflect.ValueOf(val))
|
|
}
|
|
|
|
func (state *Runtime) setValue(name string, val reflect.Value) bool {
|
|
sc := state.scope
|
|
initial := sc
|
|
|
|
// try to resolve variables in the current scope
|
|
_, ok := sc.variables[name]
|
|
|
|
// if not found walks parent scopes
|
|
for !ok && sc.parent != nil {
|
|
sc = sc.parent
|
|
_, ok = sc.variables[name]
|
|
}
|
|
|
|
if ok {
|
|
sc.variables[name] = val
|
|
return false
|
|
}
|
|
|
|
for initial.variables == nil && initial.parent != nil {
|
|
initial = initial.parent
|
|
}
|
|
|
|
if initial.variables != nil {
|
|
sc.variables[name] = val
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Resolve resolves a value from the execution context
|
|
func (state *Runtime) Resolve(name string) reflect.Value {
|
|
|
|
if name == "." {
|
|
return state.context
|
|
}
|
|
|
|
sc := state.scope
|
|
// try to resolve variables in the current scope
|
|
vl, ok := sc.variables[name]
|
|
// if not found walks parent scopes
|
|
for !ok && sc.parent != nil {
|
|
sc = sc.parent
|
|
vl, ok = sc.variables[name]
|
|
}
|
|
|
|
// if not found check globals
|
|
if !ok {
|
|
state.set.gmx.RLock()
|
|
vl, ok = state.set.globals[name]
|
|
state.set.gmx.RUnlock()
|
|
// not found check defaultVariables
|
|
if !ok {
|
|
vl, ok = defaultVariables[name]
|
|
}
|
|
}
|
|
return vl
|
|
}
|
|
|
|
func (st *Runtime) recover(err *error) {
|
|
pool_State.Put(st)
|
|
if recovered := recover(); recovered != nil {
|
|
var is bool
|
|
if _, is = recovered.(runtime.Error); is {
|
|
panic(recovered)
|
|
}
|
|
*err, is = recovered.(error)
|
|
if !is {
|
|
panic(recovered)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (st *Runtime) executeSet(left Expression, right reflect.Value) {
|
|
typ := left.Type()
|
|
if typ == NodeIdentifier {
|
|
st.setValue(left.(*IdentifierNode).Ident, right)
|
|
return
|
|
}
|
|
var value reflect.Value
|
|
var fields []string
|
|
if typ == NodeChain {
|
|
chain := left.(*ChainNode)
|
|
value = st.evalPrimaryExpressionGroup(chain.Node)
|
|
fields = chain.Field
|
|
} else {
|
|
fields = left.(*FieldNode).Ident
|
|
value = st.context
|
|
}
|
|
lef := len(fields) - 1
|
|
for i := 0; i < lef; i++ {
|
|
value = getFieldOrMethodValue(fields[i], value)
|
|
if !value.IsValid() {
|
|
left.errorf("identifier %q is not available in the current scope", fields[i])
|
|
}
|
|
}
|
|
|
|
RESTART:
|
|
switch value.Kind() {
|
|
case reflect.Ptr:
|
|
value = value.Elem()
|
|
goto RESTART
|
|
case reflect.Struct:
|
|
value = value.FieldByName(fields[lef])
|
|
if !value.IsValid() {
|
|
left.errorf("identifier %q is not available in the current scope", fields[lef])
|
|
}
|
|
value.Set(right)
|
|
case reflect.Map:
|
|
value.SetMapIndex(reflect.ValueOf(&fields[lef]).Elem(), right)
|
|
}
|
|
}
|
|
|
|
func (st *Runtime) executeSetList(set *SetNode) {
|
|
if set.IndexExprGetLookup {
|
|
value := st.evalPrimaryExpressionGroup(set.Right[0])
|
|
st.executeSet(set.Left[0], value)
|
|
if value.IsValid() {
|
|
st.executeSet(set.Left[1], valueBoolTRUE)
|
|
} else {
|
|
st.executeSet(set.Left[1], valueBoolFALSE)
|
|
}
|
|
} else {
|
|
for i := 0; i < len(set.Left); i++ {
|
|
st.executeSet(set.Left[i], st.evalPrimaryExpressionGroup(set.Right[i]))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (st *Runtime) executeLetList(set *SetNode) {
|
|
if set.IndexExprGetLookup {
|
|
value := st.evalPrimaryExpressionGroup(set.Right[0])
|
|
|
|
st.variables[set.Left[0].(*IdentifierNode).Ident] = value
|
|
|
|
if value.IsValid() {
|
|
st.variables[set.Left[1].(*IdentifierNode).Ident] = valueBoolTRUE
|
|
} else {
|
|
st.variables[set.Left[1].(*IdentifierNode).Ident] = valueBoolFALSE
|
|
}
|
|
|
|
} else {
|
|
for i := 0; i < len(set.Left); i++ {
|
|
st.variables[set.Left[i].(*IdentifierNode).Ident] = st.evalPrimaryExpressionGroup(set.Right[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (st *Runtime) executeYieldBlock(block *BlockNode, blockParam, yieldParam *BlockParameterList, expression Expression, content *ListNode) {
|
|
|
|
needNewScope := len(blockParam.List) > 0 || len(yieldParam.List) > 0
|
|
if needNewScope {
|
|
st.newScope()
|
|
for i := 0; i < len(yieldParam.List); i++ {
|
|
p := &yieldParam.List[i]
|
|
st.variables[p.Identifier] = st.evalPrimaryExpressionGroup(p.Expression)
|
|
}
|
|
for i := 0; i < len(blockParam.List); i++ {
|
|
p := &blockParam.List[i]
|
|
if _, found := st.variables[p.Identifier]; !found {
|
|
if p.Expression == nil {
|
|
st.variables[p.Identifier] = valueBoolFALSE
|
|
} else {
|
|
st.variables[p.Identifier] = st.evalPrimaryExpressionGroup(p.Expression)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mycontent := st.content
|
|
if content != nil {
|
|
myscope := st.scope
|
|
st.content = func(st *Runtime, expression Expression) {
|
|
outscope := st.scope
|
|
outcontent := st.content
|
|
|
|
st.scope = myscope
|
|
st.content = mycontent
|
|
|
|
if expression != nil {
|
|
context := st.context
|
|
st.context = st.evalPrimaryExpressionGroup(expression)
|
|
st.executeList(content)
|
|
st.context = context
|
|
} else {
|
|
st.executeList(content)
|
|
}
|
|
|
|
st.scope = outscope
|
|
st.content = outcontent
|
|
}
|
|
}
|
|
|
|
if expression != nil {
|
|
context := st.context
|
|
st.context = st.evalPrimaryExpressionGroup(expression)
|
|
st.executeList(block.List)
|
|
st.context = context
|
|
} else {
|
|
st.executeList(block.List)
|
|
}
|
|
|
|
st.content = mycontent
|
|
if needNewScope {
|
|
st.releaseScope()
|
|
}
|
|
}
|
|
|
|
func (st *Runtime) executeList(list *ListNode) {
|
|
inNewSCOPE := false
|
|
|
|
for i := 0; i < len(list.Nodes); i++ {
|
|
node := list.Nodes[i]
|
|
switch node.Type() {
|
|
|
|
case NodeText:
|
|
node := node.(*TextNode)
|
|
_, err := st.Writer.Write(node.Text)
|
|
if err != nil {
|
|
node.error(err)
|
|
}
|
|
case NodeAction:
|
|
node := node.(*ActionNode)
|
|
if node.Set != nil {
|
|
if node.Set.Let {
|
|
if !inNewSCOPE {
|
|
st.newScope() //creates new scope in the back state
|
|
inNewSCOPE = true
|
|
}
|
|
st.executeLetList(node.Set)
|
|
} else {
|
|
st.executeSetList(node.Set)
|
|
}
|
|
}
|
|
if node.Pipe != nil {
|
|
v, safeWriter := st.evalPipelineExpression(node.Pipe)
|
|
if !safeWriter && v.IsValid() {
|
|
if v.Type().Implements(rendererType) {
|
|
v.Interface().(Renderer).Render(st)
|
|
} else {
|
|
_, err := fastprinter.PrintValue(st.escapeeWriter, v)
|
|
if err != nil {
|
|
node.error(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case NodeIf:
|
|
node := node.(*IfNode)
|
|
var isLet bool
|
|
if node.Set != nil {
|
|
if node.Set.Let {
|
|
isLet = true
|
|
st.newScope()
|
|
st.executeLetList(node.Set)
|
|
} else {
|
|
st.executeSetList(node.Set)
|
|
}
|
|
}
|
|
|
|
if castBoolean(st.evalPrimaryExpressionGroup(node.Expression)) {
|
|
st.executeList(node.List)
|
|
} else if node.ElseList != nil {
|
|
st.executeList(node.ElseList)
|
|
}
|
|
if isLet {
|
|
st.releaseScope()
|
|
}
|
|
case NodeRange:
|
|
node := node.(*RangeNode)
|
|
var expression reflect.Value
|
|
|
|
isSet := node.Set != nil
|
|
isLet := false
|
|
isKeyVal := false
|
|
|
|
context := st.context
|
|
|
|
if isSet {
|
|
isKeyVal = len(node.Set.Left) > 1
|
|
expression = st.evalPrimaryExpressionGroup(node.Set.Right[0])
|
|
if node.Set.Let {
|
|
isLet = true
|
|
st.newScope()
|
|
}
|
|
} else {
|
|
expression = st.evalPrimaryExpressionGroup(node.Expression)
|
|
}
|
|
|
|
ranger := getRanger(expression)
|
|
indexValue, rangeValue, end := ranger.Range()
|
|
if !end {
|
|
for !end {
|
|
if isSet {
|
|
if isLet {
|
|
if isKeyVal {
|
|
st.variables[node.Set.Left[0].String()] = indexValue
|
|
st.variables[node.Set.Left[1].String()] = rangeValue
|
|
} else {
|
|
st.variables[node.Set.Left[0].String()] = rangeValue
|
|
}
|
|
} else {
|
|
if isKeyVal {
|
|
st.executeSet(node.Set.Left[0], indexValue)
|
|
st.executeSet(node.Set.Left[1], rangeValue)
|
|
} else {
|
|
st.executeSet(node.Set.Left[0], rangeValue)
|
|
}
|
|
}
|
|
} else {
|
|
st.context = rangeValue
|
|
}
|
|
st.executeList(node.List)
|
|
indexValue, rangeValue, end = ranger.Range()
|
|
}
|
|
} else if node.ElseList != nil {
|
|
st.executeList(node.ElseList)
|
|
}
|
|
st.context = context
|
|
if isLet {
|
|
st.releaseScope()
|
|
}
|
|
case NodeYield:
|
|
node := node.(*YieldNode)
|
|
if node.IsContent {
|
|
if st.content != nil {
|
|
st.content(st, node.Expression)
|
|
}
|
|
} else {
|
|
block, has := st.getBlock(node.Name)
|
|
if has == false || block == nil {
|
|
node.errorf("unresolved block %q!!", node.Name)
|
|
}
|
|
st.executeYieldBlock(block, block.Parameters, node.Parameters, node.Expression, node.Content)
|
|
}
|
|
case NodeBlock:
|
|
node := node.(*BlockNode)
|
|
block, has := st.getBlock(node.Name)
|
|
if has == false {
|
|
block = node
|
|
}
|
|
st.executeYieldBlock(block, block.Parameters, block.Parameters, block.Expression, block.Content)
|
|
case NodeInclude:
|
|
node := node.(*IncludeNode)
|
|
var Name string
|
|
|
|
name := st.evalPrimaryExpressionGroup(node.Name)
|
|
if name.Type().Implements(stringerType) {
|
|
Name = name.String()
|
|
} else if name.Kind() == reflect.String {
|
|
Name = name.String()
|
|
} else {
|
|
node.errorf("unexpected expression type %q in template yielding", getTypeString(name))
|
|
}
|
|
|
|
t, err := st.set.getTemplate(Name, node.TemplateName)
|
|
if err != nil {
|
|
node.error(err)
|
|
} else {
|
|
st.newScope()
|
|
st.blocks = t.processedBlocks
|
|
var context reflect.Value
|
|
if node.Expression != nil {
|
|
context = st.context
|
|
st.context = st.evalPrimaryExpressionGroup(node.Expression)
|
|
}
|
|
Root := t.Root
|
|
for t.extends != nil {
|
|
t = t.extends
|
|
Root = t.Root
|
|
}
|
|
st.executeList(Root)
|
|
st.releaseScope()
|
|
if node.Expression != nil {
|
|
st.context = context
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if inNewSCOPE {
|
|
st.releaseScope()
|
|
}
|
|
}
|
|
|
|
var (
|
|
valueBoolTRUE = reflect.ValueOf(true)
|
|
valueBoolFALSE = reflect.ValueOf(false)
|
|
)
|
|
|
|
func (st *Runtime) evalPrimaryExpressionGroup(node Expression) reflect.Value {
|
|
switch node.Type() {
|
|
case NodeAdditiveExpr:
|
|
return st.evalAdditiveExpression(node.(*AdditiveExprNode))
|
|
case NodeMultiplicativeExpr:
|
|
return st.evalMultiplicativeExpression(node.(*MultiplicativeExprNode))
|
|
case NodeComparativeExpr:
|
|
return st.evalComparativeExpression(node.(*ComparativeExprNode))
|
|
case NodeNumericComparativeExpr:
|
|
return st.evalNumericComparativeExpression(node.(*NumericComparativeExprNode))
|
|
case NodeLogicalExpr:
|
|
return st.evalLogicalExpression(node.(*LogicalExprNode))
|
|
case NodeNotExpr:
|
|
return boolValue(!castBoolean(st.evalPrimaryExpressionGroup(node.(*NotExprNode).Expr)))
|
|
case NodeTernaryExpr:
|
|
node := node.(*TernaryExprNode)
|
|
if castBoolean(st.evalPrimaryExpressionGroup(node.Boolean)) {
|
|
return st.evalPrimaryExpressionGroup(node.Left)
|
|
}
|
|
return st.evalPrimaryExpressionGroup(node.Right)
|
|
case NodeCallExpr:
|
|
node := node.(*CallExprNode)
|
|
baseExpr := st.evalBaseExpressionGroup(node.BaseExpr)
|
|
if baseExpr.Kind() != reflect.Func {
|
|
node.errorf("node %q is not func kind %q", node.BaseExpr, baseExpr.Type())
|
|
}
|
|
return st.evalCallExpression(baseExpr, node.Args)
|
|
case NodeIndexExpr:
|
|
node := node.(*IndexExprNode)
|
|
|
|
baseExpression := st.evalPrimaryExpressionGroup(node.Base)
|
|
indexExpression := st.evalPrimaryExpressionGroup(node.Index)
|
|
indexType := indexExpression.Type()
|
|
|
|
if baseExpression.Kind() == reflect.Interface {
|
|
baseExpression = baseExpression.Elem()
|
|
}
|
|
|
|
if baseExpression.Kind() == reflect.Ptr {
|
|
baseExpression = baseExpression.Elem()
|
|
}
|
|
|
|
switch baseExpression.Kind() {
|
|
case reflect.Map:
|
|
key := baseExpression.Type().Key()
|
|
if !indexType.AssignableTo(key) {
|
|
if indexType.ConvertibleTo(key) {
|
|
indexExpression = indexExpression.Convert(key)
|
|
} else {
|
|
node.errorf("%s is not assignable|convertible to map key %s", indexType.String(), key.String())
|
|
}
|
|
}
|
|
return baseExpression.MapIndex(indexExpression)
|
|
case reflect.Array, reflect.String, reflect.Slice:
|
|
if canNumber(indexType.Kind()) {
|
|
return baseExpression.Index(int(castInt64(indexExpression)))
|
|
} else {
|
|
node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String())
|
|
}
|
|
case reflect.Struct:
|
|
if canNumber(indexType.Kind()) {
|
|
return baseExpression.Field(int(castInt64(indexExpression)))
|
|
} else if indexType.Kind() == reflect.String {
|
|
return getFieldOrMethodValue(indexExpression.String(), baseExpression)
|
|
} else {
|
|
node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String())
|
|
}
|
|
default:
|
|
node.errorf("indexing is not supported in value type %s", baseExpression.Kind().String())
|
|
}
|
|
case NodeSliceExpr:
|
|
node := node.(*SliceExprNode)
|
|
baseExpression := st.evalPrimaryExpressionGroup(node.Base)
|
|
|
|
var index, length int
|
|
if node.Index != nil {
|
|
indexExpression := st.evalPrimaryExpressionGroup(node.Index)
|
|
if canNumber(indexExpression.Kind()) {
|
|
index = int(castInt64(indexExpression))
|
|
} else {
|
|
node.Index.errorf("non numeric value in index expression kind %s", indexExpression.Kind().String())
|
|
}
|
|
}
|
|
|
|
if node.EndIndex != nil {
|
|
indexExpression := st.evalPrimaryExpressionGroup(node.EndIndex)
|
|
if canNumber(indexExpression.Kind()) {
|
|
length = int(castInt64(indexExpression))
|
|
} else {
|
|
node.EndIndex.errorf("non numeric value in index expression kind %s", indexExpression.Kind().String())
|
|
}
|
|
} else {
|
|
length = baseExpression.Len()
|
|
}
|
|
|
|
return baseExpression.Slice(index, length)
|
|
}
|
|
return st.evalBaseExpressionGroup(node)
|
|
}
|
|
|
|
func (st *Runtime) isSet(node Node) bool {
|
|
nodeType := node.Type()
|
|
|
|
switch nodeType {
|
|
case NodeIndexExpr:
|
|
node := node.(*IndexExprNode)
|
|
if !st.isSet(node.Base) {
|
|
return false
|
|
}
|
|
|
|
if !st.isSet(node.Index) {
|
|
return false
|
|
}
|
|
|
|
baseExpression := st.evalPrimaryExpressionGroup(node.Base)
|
|
indexExpression := st.evalPrimaryExpressionGroup(node.Index)
|
|
|
|
indexType := indexExpression.Type()
|
|
if baseExpression.Kind() == reflect.Ptr {
|
|
baseExpression = baseExpression.Elem()
|
|
}
|
|
|
|
switch baseExpression.Kind() {
|
|
case reflect.Map:
|
|
key := baseExpression.Type().Key()
|
|
if !indexType.AssignableTo(key) {
|
|
if indexType.ConvertibleTo(key) {
|
|
indexExpression = indexExpression.Convert(key)
|
|
} else {
|
|
node.errorf("%s is not assignable|convertible to map key %s", indexType.String(), key.String())
|
|
}
|
|
}
|
|
return baseExpression.MapIndex(indexExpression).IsValid()
|
|
case reflect.Array, reflect.String, reflect.Slice:
|
|
if canNumber(indexType.Kind()) {
|
|
i := int(castInt64(indexExpression))
|
|
return i >= 0 && i < baseExpression.Len()
|
|
} else {
|
|
node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String())
|
|
}
|
|
case reflect.Struct:
|
|
if canNumber(indexType.Kind()) {
|
|
i := int(castInt64(indexExpression))
|
|
return i >= 0 && i < baseExpression.NumField()
|
|
} else if indexType.Kind() == reflect.String {
|
|
return getFieldOrMethodValue(indexExpression.String(), baseExpression).IsValid()
|
|
} else {
|
|
node.errorf("non numeric value in index expression kind %s", baseExpression.Kind().String())
|
|
}
|
|
default:
|
|
node.errorf("indexing is not supported in value type %s", baseExpression.Kind().String())
|
|
}
|
|
case NodeIdentifier:
|
|
if st.Resolve(node.String()).IsValid() == false {
|
|
return false
|
|
}
|
|
case NodeField:
|
|
node := node.(*FieldNode)
|
|
resolved := st.context
|
|
for i := 0; i < len(node.Ident); i++ {
|
|
resolved = getFieldOrMethodValue(node.Ident[i], resolved)
|
|
if !resolved.IsValid() {
|
|
return false
|
|
}
|
|
}
|
|
case NodeChain:
|
|
node := node.(*ChainNode)
|
|
var value = st.evalPrimaryExpressionGroup(node.Node)
|
|
if !value.IsValid() {
|
|
return false
|
|
}
|
|
for i := 0; i < len(node.Field); i++ {
|
|
value := getFieldOrMethodValue(node.Field[i], value)
|
|
if !value.IsValid() {
|
|
return false
|
|
}
|
|
}
|
|
default:
|
|
//todo: maybe work some edge cases
|
|
if !(nodeType > beginExpressions && nodeType < endExpressions) {
|
|
node.errorf("unexpected %q node in isset clause", node)
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (st *Runtime) evalNumericComparativeExpression(node *NumericComparativeExprNode) reflect.Value {
|
|
left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right)
|
|
isTrue := false
|
|
kind := left.Kind()
|
|
|
|
// if the left value is not a float and the right is, we need to promote the left value to a float before the calculation
|
|
// this is necessary for expressions like 4*1.23
|
|
needFloatPromotion := !isFloat(kind) && isFloat(right.Kind())
|
|
|
|
switch node.Operator.typ {
|
|
case itemGreat:
|
|
if isInt(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Int()) > right.Float()
|
|
} else {
|
|
isTrue = left.Int() > toInt(right)
|
|
}
|
|
} else if isFloat(kind) {
|
|
isTrue = left.Float() > toFloat(right)
|
|
} else if isUint(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Uint()) > right.Float()
|
|
} else {
|
|
isTrue = left.Uint() > toUint(right)
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in numeric comparative expression")
|
|
}
|
|
case itemGreatEquals:
|
|
if isInt(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Int()) >= right.Float()
|
|
} else {
|
|
isTrue = left.Int() >= toInt(right)
|
|
}
|
|
} else if isFloat(kind) {
|
|
isTrue = left.Float() >= toFloat(right)
|
|
} else if isUint(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Uint()) >= right.Float()
|
|
} else {
|
|
isTrue = left.Uint() >= toUint(right)
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in numeric comparative expression")
|
|
}
|
|
case itemLess:
|
|
if isInt(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Int()) < right.Float()
|
|
} else {
|
|
isTrue = left.Int() < toInt(right)
|
|
}
|
|
} else if isFloat(kind) {
|
|
isTrue = left.Float() < toFloat(right)
|
|
} else if isUint(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Uint()) < right.Float()
|
|
} else {
|
|
isTrue = left.Uint() < toUint(right)
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in numeric comparative expression")
|
|
}
|
|
case itemLessEquals:
|
|
if isInt(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Int()) <= right.Float()
|
|
} else {
|
|
isTrue = left.Int() <= toInt(right)
|
|
}
|
|
} else if isFloat(kind) {
|
|
isTrue = left.Float() <= toFloat(right)
|
|
} else if isUint(kind) {
|
|
if needFloatPromotion {
|
|
isTrue = float64(left.Uint()) <= right.Float()
|
|
} else {
|
|
isTrue = left.Uint() <= toUint(right)
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in numeric comparative expression")
|
|
}
|
|
}
|
|
return boolValue(isTrue)
|
|
}
|
|
|
|
func (st *Runtime) evalLogicalExpression(node *LogicalExprNode) reflect.Value {
|
|
isTrue := castBoolean(st.evalPrimaryExpressionGroup(node.Left))
|
|
if node.Operator.typ == itemAnd {
|
|
isTrue = isTrue && castBoolean(st.evalPrimaryExpressionGroup(node.Right))
|
|
} else {
|
|
isTrue = isTrue || castBoolean(st.evalPrimaryExpressionGroup(node.Right))
|
|
}
|
|
return boolValue(isTrue)
|
|
}
|
|
|
|
func boolValue(isTrue bool) reflect.Value {
|
|
if isTrue {
|
|
return valueBoolTRUE
|
|
}
|
|
return valueBoolFALSE
|
|
}
|
|
|
|
func (st *Runtime) evalComparativeExpression(node *ComparativeExprNode) reflect.Value {
|
|
left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right)
|
|
if node.Operator.typ == itemNotEquals {
|
|
return boolValue(!checkEquality(left, right))
|
|
}
|
|
return boolValue(checkEquality(left, right))
|
|
}
|
|
|
|
func toInt(v reflect.Value) int64 {
|
|
kind := v.Kind()
|
|
if isInt(kind) {
|
|
return v.Int()
|
|
} else if isFloat(kind) {
|
|
return int64(v.Float())
|
|
} else if isUint(kind) {
|
|
return int64(v.Uint())
|
|
} else if kind == reflect.String {
|
|
n, e := strconv.ParseInt(v.String(), 10, 0)
|
|
if e != nil {
|
|
panic(e)
|
|
}
|
|
return n
|
|
} else if kind == reflect.Bool {
|
|
if v.Bool() {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
panic(fmt.Errorf("type: %q can't be converted to int64", v.Type()))
|
|
}
|
|
|
|
func toUint(v reflect.Value) uint64 {
|
|
kind := v.Kind()
|
|
if isUint(kind) {
|
|
return v.Uint()
|
|
} else if isInt(kind) {
|
|
return uint64(v.Int())
|
|
} else if isFloat(kind) {
|
|
return uint64(v.Float())
|
|
} else if kind == reflect.String {
|
|
n, e := strconv.ParseUint(v.String(), 10, 0)
|
|
if e != nil {
|
|
panic(e)
|
|
}
|
|
return n
|
|
} else if kind == reflect.Bool {
|
|
if v.Bool() {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
panic(fmt.Errorf("type: %q can't be converted to uint64", v.Type()))
|
|
}
|
|
|
|
func toFloat(v reflect.Value) float64 {
|
|
kind := v.Kind()
|
|
if isFloat(kind) {
|
|
return v.Float()
|
|
} else if isInt(kind) {
|
|
return float64(v.Int())
|
|
} else if isUint(kind) {
|
|
return float64(v.Uint())
|
|
} else if kind == reflect.String {
|
|
n, e := strconv.ParseFloat(v.String(), 0)
|
|
if e != nil {
|
|
panic(e)
|
|
}
|
|
return n
|
|
} else if kind == reflect.Bool {
|
|
if v.Bool() {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
panic(fmt.Errorf("type: %q can't be converted to float64", v.Type()))
|
|
}
|
|
|
|
func (st *Runtime) evalMultiplicativeExpression(node *MultiplicativeExprNode) reflect.Value {
|
|
left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right)
|
|
kind := left.Kind()
|
|
// if the left value is not a float and the right is, we need to promote the left value to a float before the calculation
|
|
// this is necessary for expressions like 4*1.23
|
|
needFloatPromotion := !isFloat(kind) && isFloat(right.Kind())
|
|
switch node.Operator.typ {
|
|
case itemMul:
|
|
if isInt(kind) {
|
|
if needFloatPromotion {
|
|
// do the promotion and calculates
|
|
left = reflect.ValueOf(float64(left.Int()) * right.Float())
|
|
} else {
|
|
// do not need float promotion
|
|
left = reflect.ValueOf(left.Int() * toInt(right))
|
|
}
|
|
} else if isFloat(kind) {
|
|
left = reflect.ValueOf(left.Float() * toFloat(right))
|
|
} else if isUint(kind) {
|
|
if needFloatPromotion {
|
|
left = reflect.ValueOf(float64(left.Uint()) * right.Float())
|
|
} else {
|
|
left = reflect.ValueOf(left.Uint() * toUint(right))
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in multiplicative expression")
|
|
}
|
|
case itemDiv:
|
|
if isInt(kind) {
|
|
if needFloatPromotion {
|
|
left = reflect.ValueOf(float64(left.Int()) / right.Float())
|
|
} else {
|
|
left = reflect.ValueOf(left.Int() / toInt(right))
|
|
}
|
|
} else if isFloat(kind) {
|
|
left = reflect.ValueOf(left.Float() / toFloat(right))
|
|
} else if isUint(kind) {
|
|
if needFloatPromotion {
|
|
left = reflect.ValueOf(float64(left.Uint()) / right.Float())
|
|
} else {
|
|
left = reflect.ValueOf(left.Uint() / toUint(right))
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in multiplicative expression")
|
|
}
|
|
case itemMod:
|
|
if isInt(kind) {
|
|
left = reflect.ValueOf(left.Int() % toInt(right))
|
|
} else if isFloat(kind) {
|
|
left = reflect.ValueOf(int64(left.Float()) % toInt(right))
|
|
} else if isUint(kind) {
|
|
left = reflect.ValueOf(left.Uint() % toUint(right))
|
|
} else {
|
|
node.Left.errorf("a non numeric value in multiplicative expression")
|
|
}
|
|
}
|
|
return left
|
|
}
|
|
|
|
func (st *Runtime) evalAdditiveExpression(node *AdditiveExprNode) reflect.Value {
|
|
|
|
isAdditive := node.Operator.typ == itemAdd
|
|
if node.Left == nil {
|
|
right := st.evalPrimaryExpressionGroup(node.Right)
|
|
kind := right.Kind()
|
|
// todo: optimize
|
|
// todo:
|
|
if isInt(kind) {
|
|
if isAdditive {
|
|
return reflect.ValueOf(+right.Int())
|
|
} else {
|
|
return reflect.ValueOf(-right.Int())
|
|
}
|
|
} else if isUint(kind) {
|
|
if isAdditive {
|
|
return right
|
|
} else {
|
|
return reflect.ValueOf(-int64(right.Uint()))
|
|
}
|
|
} else if isFloat(kind) {
|
|
if isAdditive {
|
|
return reflect.ValueOf(+right.Float())
|
|
} else {
|
|
return reflect.ValueOf(-right.Float())
|
|
}
|
|
}
|
|
node.Left.errorf("a non numeric value in additive expression")
|
|
}
|
|
|
|
left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right)
|
|
kind := left.Kind()
|
|
// if the left value is not a float and the right is, we need to promote the left value to a float before the calculation
|
|
// this is necessary for expressions like 4+1.23
|
|
needFloatPromotion := !isFloat(kind) && kind != reflect.String && isFloat(right.Kind())
|
|
if needFloatPromotion {
|
|
if isInt(kind) {
|
|
if isAdditive {
|
|
left = reflect.ValueOf(float64(left.Int()) + right.Float())
|
|
} else {
|
|
left = reflect.ValueOf(float64(left.Int()) - right.Float())
|
|
}
|
|
} else if isUint(kind) {
|
|
if isAdditive {
|
|
left = reflect.ValueOf(float64(left.Uint()) + right.Float())
|
|
} else {
|
|
left = reflect.ValueOf(float64(left.Uint()) - right.Float())
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in additive expression")
|
|
}
|
|
} else {
|
|
if isInt(kind) {
|
|
if isAdditive {
|
|
left = reflect.ValueOf(left.Int() + toInt(right))
|
|
} else {
|
|
left = reflect.ValueOf(left.Int() - toInt(right))
|
|
}
|
|
} else if isFloat(kind) {
|
|
if isAdditive {
|
|
left = reflect.ValueOf(left.Float() + toFloat(right))
|
|
} else {
|
|
left = reflect.ValueOf(left.Float() - toFloat(right))
|
|
}
|
|
} else if isUint(kind) {
|
|
if isAdditive {
|
|
left = reflect.ValueOf(left.Uint() + toUint(right))
|
|
} else {
|
|
left = reflect.ValueOf(left.Uint() - toUint(right))
|
|
}
|
|
} else if kind == reflect.String {
|
|
if isAdditive {
|
|
left = reflect.ValueOf(left.String() + fmt.Sprint(right))
|
|
} else {
|
|
node.Right.errorf("minus signal is not allowed with strings")
|
|
}
|
|
} else {
|
|
node.Left.errorf("a non numeric value in additive expression")
|
|
}
|
|
}
|
|
|
|
return left
|
|
}
|
|
|
|
func getTypeString(value reflect.Value) string {
|
|
if value.IsValid() {
|
|
return value.Type().String()
|
|
}
|
|
return "nil"
|
|
}
|
|
|
|
func (st *Runtime) evalBaseExpressionGroup(node Node) reflect.Value {
|
|
switch node.Type() {
|
|
case NodeNil:
|
|
return reflect.ValueOf(nil)
|
|
case NodeBool:
|
|
if node.(*BoolNode).True {
|
|
return valueBoolTRUE
|
|
}
|
|
return valueBoolFALSE
|
|
case NodeString:
|
|
return reflect.ValueOf(&node.(*StringNode).Text).Elem()
|
|
case NodeIdentifier:
|
|
resolved := st.Resolve(node.(*IdentifierNode).Ident)
|
|
if !resolved.IsValid() {
|
|
node.errorf("identifier %q is not available in the current scope %v", node, st.variables)
|
|
}
|
|
return resolved
|
|
case NodeField:
|
|
node := node.(*FieldNode)
|
|
resolved := st.context
|
|
for i := 0; i < len(node.Ident); i++ {
|
|
fieldResolved := getFieldOrMethodValue(node.Ident[i], resolved)
|
|
if !fieldResolved.IsValid() {
|
|
node.errorf("there is no field or method %q in %s", node.Ident[i], getTypeString(resolved))
|
|
}
|
|
resolved = fieldResolved
|
|
}
|
|
return resolved
|
|
case NodeChain:
|
|
node := node.(*ChainNode)
|
|
var resolved = st.evalPrimaryExpressionGroup(node.Node)
|
|
for i := 0; i < len(node.Field); i++ {
|
|
fieldValue := getFieldOrMethodValue(node.Field[i], resolved)
|
|
if !fieldValue.IsValid() {
|
|
node.errorf("there is no field or method %q in %s", node.Field[i], getTypeString(resolved))
|
|
}
|
|
resolved = fieldValue
|
|
}
|
|
return resolved
|
|
case NodeNumber:
|
|
node := node.(*NumberNode)
|
|
if node.IsFloat {
|
|
return reflect.ValueOf(&node.Float64).Elem()
|
|
}
|
|
|
|
if node.IsInt {
|
|
return reflect.ValueOf(&node.Int64).Elem()
|
|
}
|
|
|
|
if node.IsUint {
|
|
return reflect.ValueOf(&node.Uint64).Elem()
|
|
}
|
|
}
|
|
node.errorf("unexpected node type %s in unary expression evaluating", node)
|
|
return reflect.Value{}
|
|
}
|
|
|
|
func (st *Runtime) evalCallExpression(baseExpr reflect.Value, args []Expression, values ...reflect.Value) reflect.Value {
|
|
|
|
if funcType.AssignableTo(baseExpr.Type()) {
|
|
return baseExpr.Interface().(Func)(Arguments{runtime: st, argExpr: args, argVal: values})
|
|
}
|
|
|
|
i := len(args) + len(values)
|
|
var returns []reflect.Value
|
|
if i <= 10 {
|
|
returns = reflect_Call10(i, st, baseExpr, args, values...)
|
|
} else {
|
|
returns = reflect_Call(make([]reflect.Value, i, i), st, baseExpr, args, values...)
|
|
}
|
|
|
|
if len(returns) == 0 {
|
|
return reflect.Value{}
|
|
}
|
|
|
|
return returns[0]
|
|
}
|
|
|
|
func (st *Runtime) evalCommandExpression(node *CommandNode) (reflect.Value, bool) {
|
|
term := st.evalPrimaryExpressionGroup(node.BaseExpr)
|
|
if node.Call {
|
|
if term.Kind() == reflect.Func {
|
|
if term.Type() == safeWriterType {
|
|
st.evalSafeWriter(term, node)
|
|
return reflect.Value{}, true
|
|
}
|
|
return st.evalCallExpression(term, node.Args), false
|
|
} else {
|
|
node.Args[0].errorf("command %q type %s is not func", node.Args[0], term.Type())
|
|
}
|
|
}
|
|
return term, false
|
|
}
|
|
|
|
type escapeWriter struct {
|
|
rawWriter io.Writer
|
|
safeWriter SafeWriter
|
|
}
|
|
|
|
func (w *escapeWriter) Write(b []byte) (int, error) {
|
|
w.safeWriter(w.rawWriter, b)
|
|
return 0, nil
|
|
}
|
|
|
|
func (st *Runtime) evalSafeWriter(term reflect.Value, node *CommandNode, v ...reflect.Value) {
|
|
|
|
sw := &escapeWriter{rawWriter: st.Writer, safeWriter: term.Interface().(SafeWriter)}
|
|
for i := 0; i < len(v); i++ {
|
|
fastprinter.PrintValue(sw, v[i])
|
|
}
|
|
for i := 0; i < len(node.Args); i++ {
|
|
fastprinter.PrintValue(sw, st.evalPrimaryExpressionGroup(node.Args[i]))
|
|
}
|
|
}
|
|
|
|
func (st *Runtime) evalCommandPipeExpression(node *CommandNode, value reflect.Value) (reflect.Value, bool) {
|
|
term := st.evalPrimaryExpressionGroup(node.BaseExpr)
|
|
if term.Kind() == reflect.Func {
|
|
if term.Type() == safeWriterType {
|
|
st.evalSafeWriter(term, node, value)
|
|
return reflect.Value{}, true
|
|
}
|
|
return st.evalCallExpression(term, node.Args, value), false
|
|
} else {
|
|
node.BaseExpr.errorf("pipe command %q type %s is not func", node.BaseExpr, term.Type())
|
|
}
|
|
return term, false
|
|
}
|
|
|
|
func (st *Runtime) evalPipelineExpression(node *PipeNode) (value reflect.Value, safeWriter bool) {
|
|
value, safeWriter = st.evalCommandExpression(node.Cmds[0])
|
|
for i := 1; i < len(node.Cmds); i++ {
|
|
if safeWriter {
|
|
node.Cmds[i].errorf("unexpected command %s, writer command should be the last command", node.Cmds[i])
|
|
}
|
|
value, safeWriter = st.evalCommandPipeExpression(node.Cmds[i], value)
|
|
}
|
|
return
|
|
}
|
|
|
|
func reflect_Call(arguments []reflect.Value, st *Runtime, fn reflect.Value, args []Expression, values ...reflect.Value) []reflect.Value {
|
|
typ := fn.Type()
|
|
numIn := typ.NumIn()
|
|
|
|
isVariadic := typ.IsVariadic()
|
|
if isVariadic {
|
|
numIn--
|
|
}
|
|
i, j := 0, 0
|
|
|
|
for ; i < numIn && i < len(values); i++ {
|
|
in := typ.In(i)
|
|
term := values[i]
|
|
if !term.Type().AssignableTo(in) {
|
|
term = term.Convert(in)
|
|
}
|
|
arguments[i] = term
|
|
}
|
|
|
|
if isVariadic {
|
|
in := typ.In(numIn).Elem()
|
|
for ; i < len(values); i++ {
|
|
term := values[i]
|
|
if !term.Type().AssignableTo(in) {
|
|
term = term.Convert(in)
|
|
}
|
|
arguments[i] = term
|
|
}
|
|
}
|
|
|
|
for ; i < numIn && j < len(args); i, j = i+1, j+1 {
|
|
in := typ.In(i)
|
|
term := st.evalPrimaryExpressionGroup(args[j])
|
|
if !term.Type().AssignableTo(in) {
|
|
term = term.Convert(in)
|
|
}
|
|
arguments[i] = term
|
|
}
|
|
|
|
if isVariadic {
|
|
in := typ.In(numIn).Elem()
|
|
for ; j < len(args); i, j = i+1, j+1 {
|
|
term := st.evalPrimaryExpressionGroup(args[j])
|
|
if !term.Type().AssignableTo(in) {
|
|
term = term.Convert(in)
|
|
}
|
|
arguments[i] = term
|
|
}
|
|
}
|
|
return fn.Call(arguments[0:i])
|
|
}
|
|
|
|
func reflect_Call10(i int, st *Runtime, fn reflect.Value, args []Expression, values ...reflect.Value) []reflect.Value {
|
|
var arguments [10]reflect.Value
|
|
return reflect_Call(arguments[0:i], st, fn, args, values...)
|
|
}
|
|
|
|
func isUint(kind reflect.Kind) bool {
|
|
return kind >= reflect.Uint && kind <= reflect.Uint64
|
|
}
|
|
func isInt(kind reflect.Kind) bool {
|
|
return kind >= reflect.Int && kind <= reflect.Int64
|
|
}
|
|
func isFloat(kind reflect.Kind) bool {
|
|
return kind == reflect.Float32 || kind == reflect.Float64
|
|
}
|
|
|
|
// checkEquality of two reflect values in the semantic of the jet runtime
|
|
func checkEquality(v1, v2 reflect.Value) bool {
|
|
|
|
if !v1.IsValid() || !v2.IsValid() {
|
|
return v1.IsValid() == v2.IsValid()
|
|
}
|
|
|
|
v1Type := v1.Type()
|
|
v2Type := v2.Type()
|
|
|
|
// fast path
|
|
if v1Type != v2.Type() && !v2Type.AssignableTo(v1Type) && !v2Type.ConvertibleTo(v1Type) {
|
|
return false
|
|
}
|
|
|
|
kind := v1.Kind()
|
|
if isInt(kind) {
|
|
return v1.Int() == toInt(v2)
|
|
}
|
|
if isFloat(kind) {
|
|
return v1.Float() == toFloat(v2)
|
|
}
|
|
if isUint(kind) {
|
|
return v1.Uint() == toUint(v2)
|
|
}
|
|
|
|
switch kind {
|
|
case reflect.Bool:
|
|
return v1.Bool() == castBoolean(v2)
|
|
case reflect.String:
|
|
return v1.String() == v2.String()
|
|
case reflect.Array:
|
|
vlen := v1.Len()
|
|
if vlen == v2.Len() {
|
|
return false
|
|
}
|
|
for i := 0; i < vlen; i++ {
|
|
if !checkEquality(v1.Index(i), v2.Index(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Slice:
|
|
|
|
if v1.IsNil() != v2.IsNil() {
|
|
return false
|
|
}
|
|
|
|
vlen := v1.Len()
|
|
if vlen != v2.Len() {
|
|
return false
|
|
}
|
|
|
|
if v1.CanAddr() && v2.CanAddr() && v1.Pointer() == v2.Pointer() {
|
|
return true
|
|
}
|
|
|
|
for i := 0; i < vlen; i++ {
|
|
if !checkEquality(v1.Index(i), v2.Index(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Interface:
|
|
if v1.IsNil() || v2.IsNil() {
|
|
return v1.IsNil() == v2.IsNil()
|
|
}
|
|
return checkEquality(v1.Elem(), v2.Elem())
|
|
case reflect.Ptr:
|
|
return v1.Pointer() == v2.Pointer()
|
|
case reflect.Struct:
|
|
numField := v1.NumField()
|
|
for i, n := 0, numField; i < n; i++ {
|
|
if !checkEquality(v1.Field(i), v2.Field(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Map:
|
|
if v1.IsNil() != v2.IsNil() {
|
|
return false
|
|
}
|
|
if v1.Len() != v2.Len() {
|
|
return false
|
|
}
|
|
if v1.Pointer() == v2.Pointer() {
|
|
return true
|
|
}
|
|
for _, k := range v1.MapKeys() {
|
|
val1 := v1.MapIndex(k)
|
|
val2 := v2.MapIndex(k)
|
|
if !val1.IsValid() || !val2.IsValid() || !checkEquality(v1.MapIndex(k), v2.MapIndex(k)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Func:
|
|
return v1.IsNil() && v2.IsNil()
|
|
default:
|
|
// Normal equality suffices
|
|
return v1.Interface() == v2.Interface()
|
|
}
|
|
}
|
|
|
|
func castBoolean(v reflect.Value) bool {
|
|
kind := v.Kind()
|
|
switch kind {
|
|
case reflect.Ptr:
|
|
return v.IsNil() == false
|
|
case reflect.Bool:
|
|
return v.Bool()
|
|
case reflect.Array:
|
|
numItems := v.Len()
|
|
for i, n := 0, numItems; i < n; i++ {
|
|
if !castBoolean(v.Index(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Struct:
|
|
numField := v.NumField()
|
|
for i, n := 0, numField; i < n; i++ {
|
|
if !castBoolean(v.Field(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case reflect.Map, reflect.Slice, reflect.String:
|
|
return v.Len() > 0
|
|
default:
|
|
if isInt(kind) {
|
|
return v.Int() > 0
|
|
}
|
|
if isUint(kind) {
|
|
return v.Uint() > 0
|
|
}
|
|
if isFloat(kind) {
|
|
return v.Float() > 0
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func canNumber(kind reflect.Kind) bool {
|
|
return isInt(kind) || isUint(kind) || isFloat(kind)
|
|
}
|
|
|
|
func castInt64(v reflect.Value) int64 {
|
|
kind := v.Kind()
|
|
switch {
|
|
case isInt(kind):
|
|
return v.Int()
|
|
case isUint(kind):
|
|
return int64(v.Uint())
|
|
case isFloat(kind):
|
|
return int64(v.Float())
|
|
}
|
|
return 0
|
|
}
|
|
|
|
var cachedStructsMutex = sync.RWMutex{}
|
|
var cachedStructsFieldIndex = map[reflect.Type]map[string][]int{}
|
|
|
|
func getFieldOrMethodValue(key string, v reflect.Value) reflect.Value {
|
|
value := getValue(key, v)
|
|
if value.Kind() == reflect.Interface {
|
|
value = value.Elem()
|
|
}
|
|
return value
|
|
}
|
|
|
|
func getValue(key string, v reflect.Value) reflect.Value {
|
|
|
|
if !v.IsValid() {
|
|
return reflect.Value{}
|
|
}
|
|
|
|
value := v.MethodByName(key)
|
|
|
|
if value.IsValid() {
|
|
return value
|
|
}
|
|
|
|
k := v.Kind()
|
|
if k == reflect.Ptr || k == reflect.Interface {
|
|
v = v.Elem()
|
|
k = v.Kind()
|
|
value = v.MethodByName(key)
|
|
if value.IsValid() {
|
|
return value
|
|
}
|
|
} else if v.CanAddr() {
|
|
value = v.Addr().MethodByName(key)
|
|
if value.IsValid() {
|
|
return value
|
|
}
|
|
}
|
|
|
|
if k == reflect.Struct {
|
|
typ := v.Type()
|
|
cachedStructsMutex.RLock()
|
|
cache, ok := cachedStructsFieldIndex[typ]
|
|
cachedStructsMutex.RUnlock()
|
|
if !ok {
|
|
cachedStructsMutex.Lock()
|
|
if cache, ok = cachedStructsFieldIndex[typ]; !ok {
|
|
cache = make(map[string][]int)
|
|
buildCache(typ, cache, nil)
|
|
cachedStructsFieldIndex[typ] = cache
|
|
}
|
|
cachedStructsMutex.Unlock()
|
|
}
|
|
if id, ok := cache[key]; ok {
|
|
return v.FieldByIndex(id)
|
|
}
|
|
return reflect.Value{}
|
|
} else if k == reflect.Map {
|
|
return v.MapIndex(reflect.ValueOf(key))
|
|
}
|
|
return reflect.Value{}
|
|
}
|
|
|
|
func buildCache(typ reflect.Type, cache map[string][]int, parent []int) {
|
|
numFields := typ.NumField()
|
|
max := len(parent) + 1
|
|
|
|
for i := 0; i < numFields; i++ {
|
|
|
|
index := make([]int, max)
|
|
copy(index, parent)
|
|
index[len(parent)] = i
|
|
|
|
field := typ.Field(i)
|
|
if field.Anonymous {
|
|
typ := field.Type
|
|
if typ.Kind() == reflect.Struct {
|
|
buildCache(typ, cache, index)
|
|
}
|
|
}
|
|
cache[field.Name] = index
|
|
}
|
|
}
|
|
|
|
func getRanger(v reflect.Value) Ranger {
|
|
tuP := v.Type()
|
|
if tuP.Implements(rangerType) {
|
|
return v.Interface().(Ranger)
|
|
}
|
|
k := tuP.Kind()
|
|
switch k {
|
|
case reflect.Ptr, reflect.Interface:
|
|
v = v.Elem()
|
|
k = v.Kind()
|
|
fallthrough
|
|
case reflect.Slice, reflect.Array:
|
|
sliceranger := pool_sliceRanger.Get().(*sliceRanger)
|
|
sliceranger.i = -1
|
|
sliceranger.len = v.Len()
|
|
sliceranger.v = v
|
|
return sliceranger
|
|
case reflect.Map:
|
|
mapranger := pool_mapRanger.Get().(*mapRanger)
|
|
*mapranger = mapRanger{v: v, keys: v.MapKeys(), len: v.Len()}
|
|
return mapranger
|
|
case reflect.Chan:
|
|
chanranger := pool_chanRanger.Get().(*chanRanger)
|
|
*chanranger = chanRanger{v: v}
|
|
return chanranger
|
|
}
|
|
panic(fmt.Errorf("type %s is not rangeable", tuP))
|
|
}
|
|
|
|
var (
|
|
pool_sliceRanger = sync.Pool{
|
|
New: func() interface{} {
|
|
return new(sliceRanger)
|
|
},
|
|
}
|
|
pool_mapRanger = sync.Pool{
|
|
New: func() interface{} {
|
|
return new(mapRanger)
|
|
},
|
|
}
|
|
pool_chanRanger = sync.Pool{
|
|
New: func() interface{} {
|
|
return new(chanRanger)
|
|
},
|
|
}
|
|
)
|
|
|
|
type sliceRanger struct {
|
|
v reflect.Value
|
|
len int
|
|
i int
|
|
}
|
|
|
|
func (s *sliceRanger) Range() (index, value reflect.Value, end bool) {
|
|
s.i++
|
|
index = reflect.ValueOf(&s.i).Elem()
|
|
if s.i < s.len {
|
|
value = s.v.Index(s.i)
|
|
return
|
|
}
|
|
pool_sliceRanger.Put(s)
|
|
end = true
|
|
return
|
|
}
|
|
|
|
type chanRanger struct {
|
|
v reflect.Value
|
|
}
|
|
|
|
func (s *chanRanger) Range() (_, value reflect.Value, end bool) {
|
|
value, end = s.v.Recv()
|
|
if end {
|
|
pool_chanRanger.Put(s)
|
|
}
|
|
return
|
|
}
|
|
|
|
type mapRanger struct {
|
|
v reflect.Value
|
|
keys []reflect.Value
|
|
len int
|
|
i int
|
|
}
|
|
|
|
func (s *mapRanger) Range() (index, value reflect.Value, end bool) {
|
|
if s.i < s.len {
|
|
index = s.keys[s.i]
|
|
value = s.v.MapIndex(index)
|
|
s.i++
|
|
return
|
|
}
|
|
end = true
|
|
pool_mapRanger.Put(s)
|
|
return
|
|
}
|