1package yqlib
2
3import (
4	"errors"
5
6	logging "gopkg.in/op/go-logging.v1"
7)
8
9type expressionPostFixer interface {
10	ConvertToPostfix([]*token) ([]*Operation, error)
11}
12
13type expressionPostFixerImpl struct {
14}
15
16func newExpressionPostFixer() expressionPostFixer {
17	return &expressionPostFixerImpl{}
18}
19
20func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) {
21	var newOp *token
22	opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
23	log.Debugf("popped %v from opstack to results", newOp.toString(true))
24	return opStack, append(result, newOp.Operation)
25}
26
27func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) {
28	var result []*Operation
29	// surround the whole thing with brackets
30	var opStack = []*token{{TokenType: openBracket}}
31	var tokens = append(infixTokens, &token{TokenType: closeBracket})
32
33	for _, currentToken := range tokens {
34		log.Debugf("postfix processing currentToken %v", currentToken.toString(true))
35		switch currentToken.TokenType {
36		case openBracket, openCollect, openCollectObject:
37			opStack = append(opStack, currentToken)
38			log.Debugf("put %v onto the opstack", currentToken.toString(true))
39		case closeCollect, closeCollectObject:
40			var opener tokenType = openCollect
41			var collectOperator *operationType = collectOpType
42			if currentToken.TokenType == closeCollectObject {
43				opener = openCollectObject
44				collectOperator = collectObjectOpType
45			}
46
47			for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
48				opStack, result = popOpToResult(opStack, result)
49			}
50			if len(opStack) == 0 {
51				return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket")
52			}
53			// now we should have [ as the last element on the opStack, get rid of it
54			opStack = opStack[0 : len(opStack)-1]
55			log.Debugf("deleteing open bracket from opstack")
56
57			//and append a collect to the opStack
58			result = append(result, &Operation{OperationType: collectOperator})
59			log.Debugf("put collect onto the result")
60			result = append(result, &Operation{OperationType: shortPipeOpType})
61			log.Debugf("put shortpipe onto the result")
62
63		case closeBracket:
64			for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
65				opStack, result = popOpToResult(opStack, result)
66			}
67			if len(opStack) == 0 {
68				return nil, errors.New("Bad path expression, got close brackets without matching opening bracket")
69			}
70			// now we should have ( as the last element on the opStack, get rid of it
71			opStack = opStack[0 : len(opStack)-1]
72
73		default:
74			var currentPrecedence = currentToken.Operation.OperationType.Precedence
75			// pop off higher precedent operators onto the result
76			for len(opStack) > 0 &&
77				opStack[len(opStack)-1].TokenType == operationToken &&
78				opStack[len(opStack)-1].Operation.OperationType.Precedence > currentPrecedence {
79				opStack, result = popOpToResult(opStack, result)
80			}
81			// add this operator to the opStack
82			opStack = append(opStack, currentToken)
83			log.Debugf("put %v onto the opstack", currentToken.toString(true))
84		}
85	}
86
87	if log.IsEnabledFor(logging.DEBUG) {
88		log.Debugf("PostFix Result:")
89		for _, currentToken := range result {
90			log.Debugf("> %v", currentToken.toString())
91		}
92	}
93
94	return result, nil
95}
96