Merge pull request #181 from Nan-Zhang/support-integer

Support parsing int64 number in Blueprint file
diff --git a/parser/ast.go b/parser/ast.go
index 52462f7..7f94efb 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -161,6 +161,7 @@
 const (
 	BoolType Type = iota + 1
 	StringType
+	Int64Type
 	ListType
 	MapType
 )
@@ -171,6 +172,8 @@
 		return "bool"
 	case StringType:
 		return "string"
+	case Int64Type:
+		return "int64"
 	case ListType:
 		return "list"
 	case MapType:
@@ -350,6 +353,31 @@
 	return StringType
 }
 
+type Int64 struct {
+	LiteralPos scanner.Position
+	Value      int64
+}
+
+func (x *Int64) Pos() scanner.Position { return x.LiteralPos }
+func (x *Int64) End() scanner.Position { return x.LiteralPos }
+
+func (x *Int64) Copy() Expression {
+	ret := *x
+	return &ret
+}
+
+func (x *Int64) Eval() Expression {
+	return x
+}
+
+func (x *Int64) String() string {
+	return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
+}
+
+func (x *Int64) Type() Type {
+	return Int64Type
+}
+
 type Bool struct {
 	LiteralPos scanner.Position
 	Value      bool
diff --git a/parser/parser.go b/parser/parser.go
index de4e8e2..728afbe 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -114,7 +114,7 @@
 	p.scanner.Error = func(sc *scanner.Scanner, msg string) {
 		p.errorf(msg)
 	}
-	p.scanner.Mode = scanner.ScanIdents | scanner.ScanStrings |
+	p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings |
 		scanner.ScanRawStrings | scanner.ScanComments
 	p.next()
 	return p
@@ -330,6 +330,9 @@
 	switch p.tok {
 	case '+':
 		return p.parseOperator(value)
+	case '-':
+		p.errorf("subtraction not supported: %s", p.scanner.String())
+		return value
 	default:
 		return value
 	}
@@ -355,6 +358,8 @@
 			switch v := value.(type) {
 			case *String:
 				v.Value += e2.(*String).Value
+			case *Int64:
+				v.Value += e2.(*Int64).Value
 			case *List:
 				v.Values = append(v.Values, e2.(*List).Values...)
 			case *Map:
@@ -441,6 +446,8 @@
 	switch p.tok {
 	case scanner.Ident:
 		return p.parseVariable()
+	case '-', scanner.Int: // Integer might have '-' sign ahead ('+' is only treated as operator now)
+		return p.parseIntValue()
 	case scanner.String:
 		return p.parseStringValue()
 	case '[':
@@ -500,6 +507,32 @@
 	return value
 }
 
+func (p *parser) parseIntValue() *Int64 {
+	var str string
+	literalPos := p.scanner.Position
+	if p.tok == '-' {
+		str += string(p.tok)
+		p.accept(p.tok)
+		if p.tok != scanner.Int {
+			p.errorf("expected int; found %s", scanner.TokenString(p.tok))
+			return nil
+		}
+	}
+	str += p.scanner.TokenText()
+	i, err := strconv.ParseInt(str, 10, 64)
+	if err != nil {
+		p.errorf("couldn't parse int: %s", err)
+		return nil
+	}
+
+	value := &Int64{
+		LiteralPos: literalPos,
+		Value:      i,
+	}
+	p.accept(scanner.Int)
+	return value
+}
+
 func (p *parser) parseListValue() *List {
 	lBracePos := p.scanner.Position
 	if !p.accept('[') {
diff --git a/parser/parser_test.go b/parser/parser_test.go
index bde67e5..d740184 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -110,6 +110,35 @@
 
 	{`
 		foo {
+			num: 4,
+		}
+		`,
+		[]Definition{
+			&Module{
+				Type:    "foo",
+				TypePos: mkpos(3, 2, 3),
+				Map: Map{
+					LBracePos: mkpos(7, 2, 7),
+					RBracePos: mkpos(22, 4, 3),
+					Properties: []*Property{
+						{
+							Name:     "num",
+							NamePos:  mkpos(12, 3, 4),
+							ColonPos: mkpos(15, 3, 7),
+							Value: &Int64{
+								LiteralPos: mkpos(17, 3, 9),
+								Value:      4,
+							},
+						},
+					},
+				},
+			},
+		},
+		nil,
+	},
+
+	{`
+		foo {
 			stuff: ["asdf", "jkl;", "qwert",
 				"uiop", "bnm,"]
 		}
@@ -164,7 +193,8 @@
 		foo {
 			stuff: {
 				isGood: true,
-				name: "bar"
+				name: "bar",
+				num: 36,
 			}
 		}
 		`,
@@ -174,7 +204,7 @@
 				TypePos: mkpos(3, 2, 3),
 				Map: Map{
 					LBracePos: mkpos(7, 2, 7),
-					RBracePos: mkpos(62, 7, 3),
+					RBracePos: mkpos(76, 8, 3),
 					Properties: []*Property{
 						{
 							Name:     "stuff",
@@ -182,7 +212,7 @@
 							ColonPos: mkpos(17, 3, 9),
 							Value: &Map{
 								LBracePos: mkpos(19, 3, 11),
-								RBracePos: mkpos(58, 6, 4),
+								RBracePos: mkpos(72, 7, 4),
 								Properties: []*Property{
 									{
 										Name:     "isGood",
@@ -202,6 +232,15 @@
 											Value:      "bar",
 										},
 									},
+									{
+										Name:     "num",
+										NamePos:  mkpos(60, 6, 5),
+										ColonPos: mkpos(63, 6, 8),
+										Value: &Int64{
+											LiteralPos: mkpos(65, 6, 10),
+											Value:      36,
+										},
+									},
 								},
 							},
 						},
@@ -279,10 +318,12 @@
 	{`
 		foo {
 			name: "abc",
+			num: 4,
 		}
 
 		bar {
 			name: "def",
+			num: -5,
 		}
 		`,
 		[]Definition{
@@ -291,7 +332,7 @@
 				TypePos: mkpos(3, 2, 3),
 				Map: Map{
 					LBracePos: mkpos(7, 2, 7),
-					RBracePos: mkpos(27, 4, 3),
+					RBracePos: mkpos(38, 5, 3),
 					Properties: []*Property{
 						{
 							Name:     "name",
@@ -302,31 +343,50 @@
 								Value:      "abc",
 							},
 						},
+						{
+							Name:     "num",
+							NamePos:  mkpos(28, 4, 4),
+							ColonPos: mkpos(31, 4, 7),
+							Value: &Int64{
+								LiteralPos: mkpos(33, 4, 9),
+								Value:      4,
+							},
+						},
 					},
 				},
 			},
 			&Module{
 				Type:    "bar",
-				TypePos: mkpos(32, 6, 3),
+				TypePos: mkpos(43, 7, 3),
 				Map: Map{
-					LBracePos: mkpos(36, 6, 7),
-					RBracePos: mkpos(56, 8, 3),
+					LBracePos: mkpos(47, 7, 7),
+					RBracePos: mkpos(79, 10, 3),
 					Properties: []*Property{
 						{
 							Name:     "name",
-							NamePos:  mkpos(41, 7, 4),
-							ColonPos: mkpos(45, 7, 8),
+							NamePos:  mkpos(52, 8, 4),
+							ColonPos: mkpos(56, 8, 8),
 							Value: &String{
-								LiteralPos: mkpos(47, 7, 10),
+								LiteralPos: mkpos(58, 8, 10),
 								Value:      "def",
 							},
 						},
+						{
+							Name:     "num",
+							NamePos:  mkpos(68, 9, 4),
+							ColonPos: mkpos(71, 9, 7),
+							Value: &Int64{
+								LiteralPos: mkpos(73, 9, 9),
+								Value:      -5,
+							},
+						},
 					},
 				},
 			},
 		},
 		nil,
 	},
+
 	{`
 		foo = "stuff"
 		bar = foo
@@ -557,6 +617,317 @@
 		},
 		nil,
 	},
+
+	{`
+		baz = -4 + -5 + 6
+		`,
+		[]Definition{
+			&Assignment{
+				Name:      "baz",
+				NamePos:   mkpos(3, 2, 3),
+				EqualsPos: mkpos(7, 2, 7),
+				Value: &Operator{
+					OperatorPos: mkpos(12, 2, 12),
+					Operator:    '+',
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      -3,
+					},
+					Args: [2]Expression{
+						&Int64{
+							LiteralPos: mkpos(9, 2, 9),
+							Value:      -4,
+						},
+						&Operator{
+							OperatorPos: mkpos(17, 2, 17),
+							Operator:    '+',
+							Value: &Int64{
+								LiteralPos: mkpos(14, 2, 14),
+								Value:      1,
+							},
+							Args: [2]Expression{
+								&Int64{
+									LiteralPos: mkpos(14, 2, 14),
+									Value:      -5,
+								},
+								&Int64{
+									LiteralPos: mkpos(19, 2, 19),
+									Value:      6,
+								},
+							},
+						},
+					},
+				},
+				OrigValue: &Operator{
+					OperatorPos: mkpos(12, 2, 12),
+					Operator:    '+',
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      -3,
+					},
+					Args: [2]Expression{
+						&Int64{
+							LiteralPos: mkpos(9, 2, 9),
+							Value:      -4,
+						},
+						&Operator{
+							OperatorPos: mkpos(17, 2, 17),
+							Operator:    '+',
+							Value: &Int64{
+								LiteralPos: mkpos(14, 2, 14),
+								Value:      1,
+							},
+							Args: [2]Expression{
+								&Int64{
+									LiteralPos: mkpos(14, 2, 14),
+									Value:      -5,
+								},
+								&Int64{
+									LiteralPos: mkpos(19, 2, 19),
+									Value:      6,
+								},
+							},
+						},
+					},
+				},
+				Assigner:   "=",
+				Referenced: false,
+			},
+		},
+		nil,
+	},
+
+	{`
+		foo = 1000000
+		bar = foo
+		baz = foo + bar
+		boo = baz
+		boo += foo
+		`,
+		[]Definition{
+			&Assignment{
+				Name:      "foo",
+				NamePos:   mkpos(3, 2, 3),
+				EqualsPos: mkpos(7, 2, 7),
+				Value: &Int64{
+					LiteralPos: mkpos(9, 2, 9),
+					Value:      1000000,
+				},
+				OrigValue: &Int64{
+					LiteralPos: mkpos(9, 2, 9),
+					Value:      1000000,
+				},
+				Assigner:   "=",
+				Referenced: true,
+			},
+			&Assignment{
+				Name:      "bar",
+				NamePos:   mkpos(19, 3, 3),
+				EqualsPos: mkpos(23, 3, 7),
+				Value: &Variable{
+					Name:    "foo",
+					NamePos: mkpos(25, 3, 9),
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      1000000,
+					},
+				},
+				OrigValue: &Variable{
+					Name:    "foo",
+					NamePos: mkpos(25, 3, 9),
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      1000000,
+					},
+				},
+				Assigner:   "=",
+				Referenced: true,
+			},
+			&Assignment{
+				Name:      "baz",
+				NamePos:   mkpos(31, 4, 3),
+				EqualsPos: mkpos(35, 4, 7),
+				Value: &Operator{
+					OperatorPos: mkpos(41, 4, 13),
+					Operator:    '+',
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      2000000,
+					},
+					Args: [2]Expression{
+						&Variable{
+							Name:    "foo",
+							NamePos: mkpos(37, 4, 9),
+							Value: &Int64{
+								LiteralPos: mkpos(9, 2, 9),
+								Value:      1000000,
+							},
+						},
+						&Variable{
+							Name:    "bar",
+							NamePos: mkpos(43, 4, 15),
+							Value: &Variable{
+								Name:    "foo",
+								NamePos: mkpos(25, 3, 9),
+								Value: &Int64{
+									LiteralPos: mkpos(9, 2, 9),
+									Value:      1000000,
+								},
+							},
+						},
+					},
+				},
+				OrigValue: &Operator{
+					OperatorPos: mkpos(41, 4, 13),
+					Operator:    '+',
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      2000000,
+					},
+					Args: [2]Expression{
+						&Variable{
+							Name:    "foo",
+							NamePos: mkpos(37, 4, 9),
+							Value: &Int64{
+								LiteralPos: mkpos(9, 2, 9),
+								Value:      1000000,
+							},
+						},
+						&Variable{
+							Name:    "bar",
+							NamePos: mkpos(43, 4, 15),
+							Value: &Variable{
+								Name:    "foo",
+								NamePos: mkpos(25, 3, 9),
+								Value: &Int64{
+									LiteralPos: mkpos(9, 2, 9),
+									Value:      1000000,
+								},
+							},
+						},
+					},
+				},
+				Assigner:   "=",
+				Referenced: true,
+			},
+			&Assignment{
+				Name:      "boo",
+				NamePos:   mkpos(49, 5, 3),
+				EqualsPos: mkpos(53, 5, 7),
+				Value: &Operator{
+					Args: [2]Expression{
+						&Variable{
+							Name:    "baz",
+							NamePos: mkpos(55, 5, 9),
+							Value: &Operator{
+								OperatorPos: mkpos(41, 4, 13),
+								Operator:    '+',
+								Value: &Int64{
+									LiteralPos: mkpos(9, 2, 9),
+									Value:      2000000,
+								},
+								Args: [2]Expression{
+									&Variable{
+										Name:    "foo",
+										NamePos: mkpos(37, 4, 9),
+										Value: &Int64{
+											LiteralPos: mkpos(9, 2, 9),
+											Value:      1000000,
+										},
+									},
+									&Variable{
+										Name:    "bar",
+										NamePos: mkpos(43, 4, 15),
+										Value: &Variable{
+											Name:    "foo",
+											NamePos: mkpos(25, 3, 9),
+											Value: &Int64{
+												LiteralPos: mkpos(9, 2, 9),
+												Value:      1000000,
+											},
+										},
+									},
+								},
+							},
+						},
+						&Variable{
+							Name:    "foo",
+							NamePos: mkpos(68, 6, 10),
+							Value: &Int64{
+								LiteralPos: mkpos(9, 2, 9),
+								Value:      1000000,
+							},
+						},
+					},
+					OperatorPos: mkpos(66, 6, 8),
+					Operator:    '+',
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      3000000,
+					},
+				},
+				OrigValue: &Variable{
+					Name:    "baz",
+					NamePos: mkpos(55, 5, 9),
+					Value: &Operator{
+						OperatorPos: mkpos(41, 4, 13),
+						Operator:    '+',
+						Value: &Int64{
+							LiteralPos: mkpos(9, 2, 9),
+							Value:      2000000,
+						},
+						Args: [2]Expression{
+							&Variable{
+								Name:    "foo",
+								NamePos: mkpos(37, 4, 9),
+								Value: &Int64{
+									LiteralPos: mkpos(9, 2, 9),
+									Value:      1000000,
+								},
+							},
+							&Variable{
+								Name:    "bar",
+								NamePos: mkpos(43, 4, 15),
+								Value: &Variable{
+									Name:    "foo",
+									NamePos: mkpos(25, 3, 9),
+									Value: &Int64{
+										LiteralPos: mkpos(9, 2, 9),
+										Value:      1000000,
+									},
+								},
+							},
+						},
+					},
+				},
+				Assigner: "=",
+			},
+			&Assignment{
+				Name:      "boo",
+				NamePos:   mkpos(61, 6, 3),
+				EqualsPos: mkpos(66, 6, 8),
+				Value: &Variable{
+					Name:    "foo",
+					NamePos: mkpos(68, 6, 10),
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      1000000,
+					},
+				},
+				OrigValue: &Variable{
+					Name:    "foo",
+					NamePos: mkpos(68, 6, 10),
+					Value: &Int64{
+						LiteralPos: mkpos(9, 2, 9),
+						Value:      1000000,
+					},
+				},
+				Assigner: "+=",
+			},
+		},
+		nil,
+	},
+
 	{`
 		// comment1
 		// comment2
diff --git a/parser/printer.go b/parser/printer.go
index 56349fa..d3aad4a 100644
--- a/parser/printer.go
+++ b/parser/printer.go
@@ -123,6 +123,8 @@
 			s = "false"
 		}
 		p.printToken(s, v.LiteralPos)
+	case *Int64:
+		p.printToken(strconv.FormatInt(v.Value, 10), v.LiteralPos)
 	case *String:
 		p.printToken(strconv.Quote(v.Value), v.LiteralPos)
 	case *List:
diff --git a/parser/printer_test.go b/parser/printer_test.go
index 6c5b646..a223fab 100644
--- a/parser/printer_test.go
+++ b/parser/printer_test.go
@@ -33,11 +33,12 @@
 	},
 	{
 		input: `
-foo{name= "abc",}
+foo{name= "abc",num= 4,}
 `,
 		output: `
 foo {
     name: "abc",
+    num: 4,
 }
 `,
 	},
@@ -108,7 +109,8 @@
 		foo {
 			stuff: {
 				isGood: true,
-				name: "bar"
+				name: "bar",
+				num: 4,
 			}
 		}
 		`,
@@ -117,6 +119,7 @@
     stuff: {
         isGood: true,
         name: "bar",
+        num: 4,
     },
 }
 `,
@@ -141,19 +144,23 @@
 		input: `
 foo {
 	name: "abc",
+	num: 4,
 }
 
 bar  {
 	name: "def",
+	num: 5,
 }
 		`,
 		output: `
 foo {
     name: "abc",
+    num: 4,
 }
 
 bar {
     name: "def",
+    num: 5,
 }
 `,
 	},
@@ -173,6 +180,20 @@
 	},
 	{
 		input: `
+foo = 100
+bar = foo
+baz = foo + bar
+baz += foo
+`,
+		output: `
+foo = 100
+bar = foo
+baz = foo + bar
+baz += foo
+`,
+	},
+	{
+		input: `
 //test
 test /* test */ {
     srcs: [
diff --git a/proptools/clone.go b/proptools/clone.go
index 4cc1103..9948b9a 100644
--- a/proptools/clone.go
+++ b/proptools/clone.go
@@ -114,7 +114,7 @@
 						origDstFieldValue.Set(newValue)
 					}
 				}
-			case reflect.Bool, reflect.String:
+			case reflect.Bool, reflect.Int64, reflect.String:
 				newValue := reflect.New(srcFieldValue.Type())
 				newValue.Elem().Set(srcFieldValue)
 				origDstFieldValue.Set(newValue)
@@ -167,7 +167,7 @@
 					break
 				}
 				ZeroProperties(fieldValue.Elem())
-			case reflect.Bool, reflect.String:
+			case reflect.Bool, reflect.Int64, reflect.String:
 				fieldValue.Set(reflect.Zero(fieldValue.Type()))
 			default:
 				panic(fmt.Errorf("can't zero field %q: points to a %s",
@@ -237,7 +237,7 @@
 				} else {
 					dstFieldValue.Set(newValue)
 				}
-			case reflect.Bool, reflect.String:
+			case reflect.Bool, reflect.Int64, reflect.String:
 				// Nothing
 			default:
 				panic(fmt.Errorf("can't clone empty field %q: points to a %s",
diff --git a/proptools/clone_test.go b/proptools/clone_test.go
index 0679c12..b6f1bf6 100644
--- a/proptools/clone_test.go
+++ b/proptools/clone_test.go
@@ -91,6 +91,15 @@
 		},
 	},
 	{
+		// Clone pointer to int64
+		in: &struct{ S *int64 }{
+			S: Int64Ptr(5),
+		},
+		out: &struct{ S *int64 }{
+			S: Int64Ptr(5),
+		},
+	},
+	{
 		// Clone struct
 		in: &struct{ S struct{ S string } }{
 			S: struct{ S string }{
@@ -189,10 +198,12 @@
 		}{
 			EmbeddedStruct: EmbeddedStruct{
 				S: "string1",
+				I: Int64Ptr(55),
 			},
 			Nested: struct{ EmbeddedStruct }{
 				EmbeddedStruct: EmbeddedStruct{
 					S: "string2",
+					I: Int64Ptr(5),
 				},
 			},
 		},
@@ -202,10 +213,12 @@
 		}{
 			EmbeddedStruct: EmbeddedStruct{
 				S: "string1",
+				I: Int64Ptr(55),
 			},
 			Nested: struct{ EmbeddedStruct }{
 				EmbeddedStruct: EmbeddedStruct{
 					S: "string2",
+					I: Int64Ptr(5),
 				},
 			},
 		},
@@ -241,7 +254,10 @@
 	},
 }
 
-type EmbeddedStruct struct{ S string }
+type EmbeddedStruct struct {
+	S string
+	I *int64
+}
 type EmbeddedInterface interface{}
 
 func TestCloneProperties(t *testing.T) {
@@ -309,6 +325,14 @@
 		out: &struct{ B1, B2 *bool }{},
 	},
 	{
+		// Clone pointer to int64
+		in: &struct{ B1, B2 *int64 }{
+			B1: Int64Ptr(5),
+			B2: Int64Ptr(4),
+		},
+		out: &struct{ B1, B2 *int64 }{},
+	},
+	{
 		// Clone pointer to string
 		in: &struct{ S *string }{
 			S: StringPtr("string1"),
diff --git a/proptools/extend.go b/proptools/extend.go
index 16f48c3..1f323cf 100644
--- a/proptools/extend.go
+++ b/proptools/extend.go
@@ -346,7 +346,7 @@
 						dstFieldValue.Type(), srcFieldValue.Type())
 				}
 				switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind {
-				case reflect.Bool, reflect.String, reflect.Struct:
+				case reflect.Bool, reflect.Int64, reflect.String, reflect.Struct:
 				// Nothing
 				default:
 					return extendPropertyErrorf(propertyName, "pointer is a %s", ptrKind)
@@ -448,6 +448,17 @@
 				// For append, replace the original value.
 				dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool())))
 			}
+		case reflect.Int64:
+			if prepend {
+				if dstFieldValue.IsNil() {
+					// Int() returns Int64
+					dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int())))
+				}
+			} else {
+				// For append, replace the original value.
+				// Int() returns Int64
+				dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int())))
+			}
 		case reflect.String:
 			if prepend {
 				if dstFieldValue.IsNil() {
diff --git a/proptools/extend_test.go b/proptools/extend_test.go
index 0070d7e..66adabb 100644
--- a/proptools/extend_test.go
+++ b/proptools/extend_test.go
@@ -177,6 +177,58 @@
 			prepend: true,
 		},
 		{
+			// Append pointer to integer
+			in1: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
+				I1: Int64Ptr(55),
+				I2: Int64Ptr(-3),
+				I3: nil,
+				I4: Int64Ptr(100),
+				I5: Int64Ptr(33),
+				I6: nil,
+				I7: Int64Ptr(77),
+				I8: Int64Ptr(0),
+				I9: nil,
+			},
+			in2: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
+				I1: nil,
+				I2: nil,
+				I3: nil,
+				I4: Int64Ptr(1),
+				I5: Int64Ptr(-2),
+				I6: Int64Ptr(8),
+				I7: Int64Ptr(9),
+				I8: Int64Ptr(10),
+				I9: Int64Ptr(11),
+			},
+			out: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
+				I1: Int64Ptr(55),
+				I2: Int64Ptr(-3),
+				I3: nil,
+				I4: Int64Ptr(1),
+				I5: Int64Ptr(-2),
+				I6: Int64Ptr(8),
+				I7: Int64Ptr(9),
+				I8: Int64Ptr(10),
+				I9: Int64Ptr(11),
+			},
+		},
+		{
+			// Prepend pointer to integer
+			in1: &struct{ I1, I2, I3 *int64 }{
+				I1: Int64Ptr(55),
+				I3: nil,
+			},
+			in2: &struct{ I1, I2, I3 *int64 }{
+				I2: Int64Ptr(33),
+			},
+			out: &struct{ I1, I2, I3 *int64 }{
+				I1: Int64Ptr(55),
+				I2: Int64Ptr(33),
+				I3: nil,
+			},
+			prepend: true,
+		},
+		{
 			// Append pointer to strings
 			in1: &struct{ S1, S2, S3, S4 *string }{
 				S1: StringPtr("string1"),
@@ -383,6 +435,18 @@
 			},
 		},
 		{
+			// Unexported field
+			in1: &struct{ i *int64 }{
+				i: Int64Ptr(33),
+			},
+			in2: &struct{ i *int64 }{
+				i: Int64Ptr(5),
+			},
+			out: &struct{ i *int64 }{
+				i: Int64Ptr(33),
+			},
+		},
+		{
 			// Empty struct
 			in1: &struct{}{},
 			in2: &struct{}{},
@@ -420,10 +484,12 @@
 			}{
 				EmbeddedStruct: EmbeddedStruct{
 					S: "string1",
+					I: Int64Ptr(55),
 				},
 				Nested: struct{ EmbeddedStruct }{
 					EmbeddedStruct: EmbeddedStruct{
 						S: "string2",
+						I: Int64Ptr(-4),
 					},
 				},
 			},
@@ -433,10 +499,12 @@
 			}{
 				EmbeddedStruct: EmbeddedStruct{
 					S: "string3",
+					I: Int64Ptr(66),
 				},
 				Nested: struct{ EmbeddedStruct }{
 					EmbeddedStruct: EmbeddedStruct{
 						S: "string4",
+						I: Int64Ptr(-8),
 					},
 				},
 			},
@@ -446,10 +514,12 @@
 			}{
 				EmbeddedStruct: EmbeddedStruct{
 					S: "string1string3",
+					I: Int64Ptr(66),
 				},
 				Nested: struct{ EmbeddedStruct }{
 					EmbeddedStruct: EmbeddedStruct{
 						S: "string2string4",
+						I: Int64Ptr(-8),
 					},
 				},
 			},
@@ -460,12 +530,20 @@
 				EmbeddedInterface
 				Nested struct{ EmbeddedInterface }
 			}{
-				EmbeddedInterface: &struct{ S string }{
+				EmbeddedInterface: &struct {
+					S string
+					I *int64
+				}{
 					S: "string1",
+					I: Int64Ptr(-8),
 				},
 				Nested: struct{ EmbeddedInterface }{
-					EmbeddedInterface: &struct{ S string }{
+					EmbeddedInterface: &struct {
+						S string
+						I *int64
+					}{
 						S: "string2",
+						I: Int64Ptr(55),
 					},
 				},
 			},
@@ -473,12 +551,20 @@
 				EmbeddedInterface
 				Nested struct{ EmbeddedInterface }
 			}{
-				EmbeddedInterface: &struct{ S string }{
+				EmbeddedInterface: &struct {
+					S string
+					I *int64
+				}{
 					S: "string3",
+					I: Int64Ptr(6),
 				},
 				Nested: struct{ EmbeddedInterface }{
-					EmbeddedInterface: &struct{ S string }{
+					EmbeddedInterface: &struct {
+						S string
+						I *int64
+					}{
 						S: "string4",
+						I: Int64Ptr(6),
 					},
 				},
 			},
@@ -486,12 +572,20 @@
 				EmbeddedInterface
 				Nested struct{ EmbeddedInterface }
 			}{
-				EmbeddedInterface: &struct{ S string }{
+				EmbeddedInterface: &struct {
+					S string
+					I *int64
+				}{
 					S: "string1string3",
+					I: Int64Ptr(6),
 				},
 				Nested: struct{ EmbeddedInterface }{
-					EmbeddedInterface: &struct{ S string }{
+					EmbeddedInterface: &struct {
+						S string
+						I *int64
+					}{
 						S: "string2string4",
+						I: Int64Ptr(6),
 					},
 				},
 			},
@@ -626,6 +720,19 @@
 			err: extendPropertyErrorf("i", "unsupported kind int"),
 		},
 		{
+			// Unsupported kind
+			in1: &struct{ I int64 }{
+				I: 1,
+			},
+			in2: &struct{ I int64 }{
+				I: 2,
+			},
+			out: &struct{ I int64 }{
+				I: 1,
+			},
+			err: extendPropertyErrorf("i", "unsupported kind int64"),
+		},
+		{
 			// Interface nilitude mismatch
 			in1: &struct{ S interface{} }{
 				S: nil,
@@ -785,6 +892,24 @@
 			},
 		},
 		{
+			// Filter mutated
+			in1: &struct {
+				S *int64 `blueprint:"mutated"`
+			}{
+				S: Int64Ptr(4),
+			},
+			in2: &struct {
+				S *int64 `blueprint:"mutated"`
+			}{
+				S: Int64Ptr(5),
+			},
+			out: &struct {
+				S *int64 `blueprint:"mutated"`
+			}{
+				S: Int64Ptr(4),
+			},
+		},
+		{
 			// Filter error
 			in1: &struct{ S string }{
 				S: "string1",
diff --git a/proptools/proptools.go b/proptools/proptools.go
index 690d384..e071b3c 100644
--- a/proptools/proptools.go
+++ b/proptools/proptools.go
@@ -55,6 +55,12 @@
 	return &b
 }
 
+// Int64Ptr returns a pointer to a new int64 containing the given value.
+func Int64Ptr(i int64) *int64 {
+	b := int64(i)
+	return &(b)
+}
+
 // StringPtr returns a pointer to a new string containing the given value.
 func StringPtr(s string) *string {
 	return &s
diff --git a/unpack.go b/unpack.go
index 4591f1a..cd165da 100644
--- a/unpack.go
+++ b/unpack.go
@@ -186,7 +186,7 @@
 					origFieldValue.Set(fieldValue)
 				}
 				fieldValue = fieldValue.Elem()
-			case reflect.Bool, reflect.String:
+			case reflect.Bool, reflect.Int64, reflect.String:
 				// Nothing
 			default:
 				panic(fmt.Errorf("field %s contains a pointer to %s", propertyName, ptrKind))
@@ -303,6 +303,14 @@
 		}
 		value = reflect.ValueOf(b.Value)
 
+	case reflect.Int64:
+		b, ok := property.Value.Eval().(*parser.Int64)
+		if !ok {
+			return value, fmt.Errorf("%s: can't assign %s value to int64 property %q",
+				property.Value.Pos(), property.Value.Type(), property.Name)
+		}
+		value = reflect.ValueOf(b.Value)
+
 	case reflect.String:
 		s, ok := property.Value.Eval().(*parser.String)
 		if !ok {