func (parser *Parser) ParseNumber() ast.Expr { var pos = parser.Pos debug("ParseNumber at %d", parser.Pos) // the number token var tok = parser.next() debug("ParseNumber => next: %s", tok) var negative = false if tok.Type == ast.T_MINUS { tok = parser.next() negative = true } else if tok.Type == ast.T_PLUS { tok = parser.next() negative = false } var val float64 var tok2 = parser.peek() if tok.Type == ast.T_INTEGER { i, err := strconv.ParseInt(tok.Str, 10, 64) if err != nil { panic(err) } if negative { i = -i } val = float64(i) } else if tok.Type == ast.T_FLOAT { f, err := strconv.ParseFloat(tok.Str, 64) if err != nil { panic(err) } if negative { f = -f } val = f } else { // unknown token parser.restore(pos) return nil } if tok2.IsUnit() { // consume the unit token parser.next() return ast.NewNumber(val, ast.NewUnitWithToken(tok2), tok) } return ast.NewNumber(val, nil, tok) }
/* 10px / 3, 10 / 3, 10px / 10px is allowed here */ func NumberDivNumber(a *ast.Number, b *ast.Number) *ast.Number { // for 10/2 and 10px/2px if (a.Unit == nil && b.Unit == nil) || (a.Unit != nil && b.Unit != nil && a.Unit.Type == b.Unit.Type) { return ast.NewNumber(a.Value/b.Value, nil, nil) } if a.Unit != nil && b.Unit == nil { return ast.NewNumber(a.Value/b.Value, a.Unit, nil) } return nil }
func TestComputeNumberDivWithUnit(t *testing.T) { val := Compute(ast.NewOp(ast.T_DIV), ast.NewNumber(10, ast.NewUnit(ast.T_UNIT_PX, nil), nil), ast.NewNumber(2, nil, nil)) num, ok := val.(*ast.Number) assert.True(t, ok) assert.NotNil(t, num.Unit) assert.Equal(t, ast.T_UNIT_PX, num.Unit.Type) assert.Equal(t, 5.0, num.Value) }
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) }
func TestStack(t *testing.T) { var stack = &Stack{} stack.Push(ast.NewNumber(10, nil, nil)) stack.Push(ast.NewNumber(12, nil, nil)) val1 := stack.Pop() num1, ok := val1.(*ast.Number) assert.True(t, ok) assert.Equal(t, 12.0, num1.Value) val2 := stack.Pop() num2, ok := val2.(*ast.Number) assert.True(t, ok) assert.Equal(t, 10.0, num2.Value) }
func TestComputeNumberMulWithUnit(t *testing.T) { val := Compute(ast.NewOp(ast.T_MUL), ast.NewNumber(10, ast.NewUnit(ast.T_UNIT_PX, nil), nil), ast.NewNumber(3, nil, nil)) num, ok := val.(*ast.Number) assert.True(t, ok) assert.Equal(t, ast.T_UNIT_PX, num.Unit.Type) assert.Equal(t, 30.0, num.Value) }
func NumberAddNumber(a *ast.Number, b *ast.Number) *ast.Number { if (a.Unit == nil && b.Unit == nil) || (a.Unit != nil && b.Unit != nil && a.Unit.Type == b.Unit.Type) { return ast.NewNumber(a.Value+b.Value, a.Unit, nil) } fmt.Printf("Incompatible unit %s != %s. %v + %v \n", a.Unit, b.Unit, a, b) return nil }
func NumberSubNumber(a *ast.Number, b *ast.Number) *ast.Number { if a.Unit != nil && b.Unit != nil && a.Unit.Type != b.Unit.Type { fmt.Printf("Incompatible unit %s != %s. %v - %v \n", a.Unit, b.Unit, a, b) return nil } var result = a.Value - b.Value return ast.NewNumber(result, a.Unit, nil) }
/* 3 * 10px, 10px * 3, 10px * 10px is allowed here */ func NumberMulNumber(a *ast.Number, b *ast.Number) *ast.Number { if a.Unit == nil && b.Unit == nil { return ast.NewNumber(a.Value*b.Value, nil, nil) } if a.Unit == nil || b.Unit == nil || a.Unit.Type == b.Unit.Type { var result = a.Value * b.Value var unit *ast.Unit = nil if a.Unit != nil { unit = a.Unit } if b.Unit != nil { unit = b.Unit } return ast.NewNumber(result, unit, nil) } return nil }
func TestComputeRGBAColorWithNumber(t *testing.T) { val := Compute(ast.NewOp(ast.T_PLUS), ast.NewRGBAColor(10, 10, 10, 0.2, nil), ast.NewNumber(3, nil, nil)) c, ok := val.(*ast.RGBAColor) assert.True(t, ok) assert.Equal(t, "rgba(13, 13, 13, 0.2)", c.String()) }
func TestComputeNumberAddNumberIncompatibleUnit(t *testing.T) { val := Compute(ast.NewOp(ast.T_PLUS), ast.NewNumber(10, ast.NewUnit(ast.T_UNIT_PX, nil), nil), ast.NewNumber(3, ast.NewUnit(ast.T_UNIT_PT, nil), nil)) assert.Nil(t, val) }
func TestComputeNumberAddNumber(t *testing.T) { val := Compute(ast.NewOp(ast.T_PLUS), ast.NewNumber(10, nil, nil), ast.NewNumber(3, nil, nil)) num, ok := val.(*ast.Number) assert.True(t, ok) assert.Equal(t, 13.0, num.Value) }
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) }