func TestReduceExprForSolveableExpr(t *testing.T) { expr := ast.NewBinaryExpr(ast.NewOp(ast.T_PLUS), ast.NewNumber(10, nil, nil), ast.NewNumber(3, nil, nil), false) expr2 := ast.NewBinaryExpr(ast.NewOp(ast.T_PLUS), expr, ast.NewNumber(3, nil, nil), false) val, ok := ReduceExpr(expr2, nil) assert.True(t, ok) assert.NotNil(t, val) num, ok := val.(*ast.Number) assert.True(t, ok) assert.NotNil(t, num) assert.Equal(t, 16.0, num.Value) }
/** We here treat the property values as expressions: padding: {expression} {expression} {expression}; margin: {expression}; */ func (parser *Parser) ParseExpr(inParenthesis bool) ast.Expr { var pos = parser.Pos // plus or minus. This creates an unary expression that holds the later term. // this is for: +3 or -4 var expr ast.Expr = nil if tok := parser.acceptAnyOf2(ast.T_PLUS, ast.T_MINUS); tok != nil { if term := parser.ParseTerm(); term != nil { expr = ast.NewUnaryExpr(ast.NewOpWithToken(tok), term) if uexpr, ok := expr.(*ast.UnaryExpr); ok { // if it's evaluatable just return the evaluated value. if val, ok := runtime.ReduceExpr(uexpr, parser.GlobalContext); ok { expr = ast.Expr(val) } } } else { parser.restore(pos) return nil } } else { expr = parser.ParseTerm() } if expr == nil { debug("ParseExpr failed, got %+v, restoring to %d", expr, pos) parser.restore(pos) return nil } var rightTok = parser.peek() for rightTok.Type == ast.T_PLUS || rightTok.Type == ast.T_MINUS || rightTok.Type == ast.T_LITERAL_CONCAT { // accept plus or minus parser.advance() if rightTerm := parser.ParseTerm(); rightTerm != nil { // XXX: check parenthesis var bexpr = ast.NewBinaryExpr(ast.NewOpWithToken(rightTok), expr, rightTerm, inParenthesis) if val, ok := runtime.ReduceExpr(bexpr, parser.GlobalContext); ok { expr = ast.Expr(val) } else { // wrap the existing expression with the new binary expression object expr = ast.Expr(bexpr) } } else { panic(SyntaxError{ Reason: "Expecting term on the right side", ActualToken: parser.peek(), File: parser.File, }) } rightTok = parser.peek() } return expr }
/* This method parses media type first, then expecting more that on media expressions. media_query: [[only | not]? <media_type> [ and <expression> ]*] | <expression> [ and <expression> ]* expression: ( <media_feature> [: <value>]? ) Specification: http://dev.w3.org/csswg/mediaqueries-3 */ func (parser *Parser) ParseMediaQuery() *ast.MediaQuery { // the leading media type is optional var mediaType = parser.ParseMediaType() if mediaType != nil { // Check if there is an expression after the media type. var tok = parser.peek() if tok.Type != ast.T_LOGICAL_AND { return ast.NewMediaQuery(mediaType, nil) } parser.advance() // skip the and operator token } // parse the media expression after the media type. var mediaExpr = parser.ParseMediaQueryExpr() if mediaExpr == nil { if mediaType == nil { return nil } return ast.NewMediaQuery(mediaType, mediaExpr) } // @media query only allows AND operator here.. for tok := parser.accept(ast.T_LOGICAL_AND); tok != nil; tok = parser.accept(ast.T_LOGICAL_AND) { // parse another mediq query expression var expr2 = parser.ParseMediaQueryExpr() mediaExpr = ast.NewBinaryExpr(ast.NewOpWithToken(tok), mediaExpr, expr2, false) } return ast.NewMediaQuery(mediaType, mediaExpr) }
func (parser *Parser) ParseLogicExpr() ast.Expr { debug("ParseLogicExpr") var expr = parser.ParseLogicANDExpr() for tok := parser.accept(ast.T_LOGICAL_OR); tok != nil; tok = parser.accept(ast.T_LOGICAL_OR) { if subexpr := parser.ParseLogicANDExpr(); subexpr != nil { expr = ast.NewBinaryExpr(ast.NewOpWithToken(tok), expr, subexpr, false) } } return expr }
func (parser *Parser) ParseComparisonExpr() ast.Expr { debug("ParseComparisonExpr") var expr ast.Expr = nil if parser.accept(ast.T_PAREN_OPEN) != nil { expr = parser.ParseLogicExpr() parser.expect(ast.T_PAREN_CLOSE) } else { expr = parser.ParseExpr(false) } var tok = parser.peek() for tok != nil && tok.IsComparisonOperator() { parser.advance() if subexpr := parser.ParseExpr(false); subexpr != nil { expr = ast.NewBinaryExpr(ast.NewOpWithToken(tok), expr, subexpr, false) } tok = parser.peek() } return expr }
func (parser *Parser) ParseTerm() ast.Expr { var pos = parser.Pos var factor = parser.ParseFactor() if factor == nil { parser.restore(pos) return nil } // see if the next token is '*' or '/' if tok := parser.acceptAnyOf2(ast.T_MUL, ast.T_DIV); tok != nil { if term := parser.ParseTerm(); term != nil { return ast.NewBinaryExpr(ast.NewOpWithToken(tok), factor, term, false) } else { panic(SyntaxError{ Reason: "Expecting term after '*' or '/'", ActualToken: parser.peek(), File: parser.File, }) } } return factor }
func TestReduceCSSSlashExpr(t *testing.T) { expr := ast.NewBinaryExpr(ast.NewOp(ast.T_DIV), ast.NewNumber(10, ast.NewUnit(ast.T_UNIT_PX, nil), nil), ast.NewNumber(3, ast.NewUnit(ast.T_UNIT_PX, nil), nil), false) ok := CanReduceExpr(expr) assert.False(t, ok) }