Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0
Ce dépôt a été archivé le 2022-05-07. Vous pouvez voir ses fichiers ou le cloner, mais pas ouvrir de ticket ou de demandes d'ajout, ni soumettre de changements.
nyaa-pantsu/vendor/github.com/CloudyKit/jet/eval.go
akuma06 5991a21818 First batch of changes for the refactor (#1078)
* 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
2017-06-28 21:42:38 +10:00

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
}