x/debug: add an expression evaluator.

The evaluator parses the expression with go/parser, then walks through
the resulting syntax tree, evaluating the subexpression at each node.

The expression can include local and global variables.  The program
counter and stack pointer of the stopped program are used to determine
what variables are available, and their location in memory.

The returned result uses the same types as the program.Value function.

The math/big package is used to represent untyped numeric constants.

A go generate rule using the m4 preprocessor is used to produce eval.go.

Change-Id: I5b668308b3dd8361fc16974b3dfe9e1ab423c974
Reviewed-on: https://go-review.googlesource.com/15678
Reviewed-by: Dave Day <djd@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go
index 7a69445..2affb00 100644
--- a/ogle/demo/ogler/ogler_test.go
+++ b/ogle/demo/ogler/ogler_test.go
@@ -12,6 +12,7 @@
 	"log"
 	"os"
 	"os/exec"
+	"reflect"
 	"testing"
 
 	"golang.org/x/debug/ogle/program"
@@ -36,6 +37,7 @@
 	`main.Z_uint8`:      uint8(231),
 }
 
+// TODO: the string forms of some types we're testing aren't stable
 var expectedVars = map[string]string{
 	`main.Z_array`:               `[5]int8{-121, 121, 3, 2, 1}`,
 	`main.Z_array_empty`:         `[0]int8{}`,
@@ -52,13 +54,14 @@
 	`main.Z_float64`:             `1.987654321`,
 	`main.Z_func_int8_r_int8`:    `func(int8, *int8) void @0xX `,
 	`main.Z_func_int8_r_pint8`:   `func(int8, **int8) void @0xX `,
-	`main.Z_func_bar`:            `func(*main.FooStruct) void @0xX `,
+	`main.Z_func_bar`:            `func(*struct main.FooStruct) void @0xX `,
 	`main.Z_func_nil`:            `func(int8, *int8) void @0xX `,
 	`main.Z_int`:                 `-21`,
 	`main.Z_int16`:               `-32321`,
 	`main.Z_int32`:               `-1987654321`,
 	`main.Z_int64`:               `-9012345678987654321`,
 	`main.Z_int8`:                `-121`,
+	`main.Z_int_typedef`:         `88`,
 	`main.Z_interface`:           `("*main.FooStruct", 0xX)`,
 	`main.Z_interface_nil`:       `(<nil>, <nil>)`,
 	`main.Z_interface_typed_nil`: `("*main.FooStruct", <nil>)`,
@@ -84,6 +87,115 @@
 	`main.Z_unsafe_pointer_nil`:  `0x0`,
 }
 
+// expectedEvaluate contains expected results of the program.Evaluate function.
+// A nil value indicates that an error is expected.
+var expectedEvaluate = map[string]program.Value{
+	`x`:                         int16(42),
+	`local_array`:               program.Array{42, 42, 5, 8},
+	`local_bool_false`:          false,
+	`local_bool_true`:           true,
+	`local_channel`:             program.Channel{42, 42, 42, 0, 0, 2, 0},
+	`local_channel_buffered`:    program.Channel{42, 42, 42, 6, 10, 2, 8},
+	`local_channel_nil`:         program.Channel{42, 0, 0, 0, 0, 2, 0},
+	`local_complex128`:          complex128(1.987654321 - 2.987654321i),
+	`local_complex64`:           complex64(1.54321 + 2.54321i),
+	`local_float32`:             float32(1.54321),
+	`local_float64`:             float64(1.987654321),
+	`local_func_int8_r_int8`:    program.Func{42},
+	`local_func_int8_r_pint8`:   program.Func{42},
+	`local_func_bar`:            program.Func{42},
+	`local_func_nil`:            program.Func{0},
+	`local_int`:                 -21,
+	`local_int16`:               int16(-32321),
+	`local_int32`:               int32(-1987654321),
+	`local_int64`:               int64(-9012345678987654321),
+	`local_int8`:                int8(-121),
+	`local_int_typedef`:         int16(88),
+	`local_interface`:           program.Interface{},
+	`local_interface_nil`:       program.Interface{},
+	`local_interface_typed_nil`: program.Interface{},
+	`local_map`:                 program.Map{42, 42, 1},
+	`local_map_2`:               program.Map{42, 42, 1},
+	`local_map_3`:               program.Map{42, 42, 2},
+	`local_map_empty`:           program.Map{42, 42, 0},
+	`local_map_nil`:             program.Map{42, 42, 0},
+	`local_pointer`:             program.Pointer{42, 42},
+	`local_pointer_nil`:         program.Pointer{42, 0},
+	`local_slice`:               program.Slice{program.Array{42, 42, 5, 8}, 5},
+	`local_slice_2`:             program.Slice{program.Array{42, 42, 2, 8}, 5},
+	`local_slice_nil`:           program.Slice{program.Array{42, 0, 0, 8}, 0},
+	`local_string`:              program.String{12, `I'm a string`},
+	`local_struct`:              program.Struct{[]program.StructField{{"a", program.Var{}}, {"b", program.Var{}}}},
+	`local_uint`:                uint(21),
+	`local_uint16`:              uint16(54321),
+	`local_uint32`:              uint32(3217654321),
+	`local_uint64`:              uint64(12345678900987654321),
+	`local_uint8`:               uint8(231),
+	`local_uintptr`:             uint(21),
+	`local_unsafe_pointer`:      program.Pointer{0, 42},
+	`local_unsafe_pointer_nil`:  program.Pointer{0, 0},
+	`x + 5`:                     int16(47),
+	`x - 5`:                     int16(37),
+	`x / 5`:                     int16(8),
+	`x % 5`:                     int16(2),
+	`x & 2`:                     int16(2),
+	`x | 1`:                     int16(43),
+	`x ^ 3`:                     int16(41),
+	`5 + x`:                     int16(47),
+	`5 - x`:                     int16(-37),
+	`100 / x`:                   int16(2),
+	`100 % x`:                   int16(16),
+	`2 & x`:                     int16(2),
+	`1 | x`:                     int16(43),
+	`3 ^ x`:                     int16(41),
+	`12`:                        12,
+	`+42`:                       42,
+	`23i`:                       23i,
+	`34.0`:                      34.0,
+	`34.5`:                      34.5,
+	`1e5`:                       100000.0,
+	`0x42`:                      66,
+	`'c'`:                       'c',
+	`"de"`:                      program.String{2, `de`},
+	"`ef`":                      program.String{2, `ef`},
+	`"de" + "fg"`:               program.String{4, `defg`},
+	`/* comment */ -5`:          -5,
+	`false`:                     false,
+	`true`:                      true,
+	`!false`:                    true,
+	`!true`:                     false,
+	`5 + 5`:                     10,
+	`true || false`:             true,
+	`false || false`:            false,
+	`true && false`:             false,
+	`true && true`:              true,
+	`!(5 > 8)`:                  true,
+	`10 + 'a'`:                  'k',
+	`10 + 10.5`:                 20.5,
+	`10 + 10.5i`:                10 + 10.5i,
+	`'a' + 10.5`:                107.5,
+	`'a' + 10.5i`:               97 + 10.5i,
+	`10.5 + 20.5i`:              10.5 + 20.5i,
+	`10 * 20`:                   200,
+	`10.0 - 20.5`:               -10.5,
+	`(6 + 8i) * 4`:              24 + 32i,
+	`(6 + 8i) * (1 + 1i)`:       -2 + 14i,
+	`(6 + 8i) * (6 - 8i)`:       complex128(100),
+	`(6 + 8i) / (3 + 4i)`:       complex128(2),
+	`local_string + "!"`:        program.String{13, `I'm a string!`},
+	`5 + false`:                 nil,
+	``:                          nil,
+	`x + ""`:                    nil,
+	`x / 0`:                     nil,
+	`0 / 0`:                     nil,
+	`'a' / ('a'-'a')`:           nil,
+	`0.0 / 0.0`:                 nil,
+	`3i / 0.0`:                  nil,
+	`x % 0`:                     nil,
+	`0 % 0`:                     nil,
+	`'a' % ('a'-'a')`:           nil,
+}
+
 func isHex(r uint8) bool {
 	switch {
 	case '0' <= r && r <= '9':
@@ -269,8 +381,8 @@
 		log.Fatalf("DeleteBreakpoints: %v", err)
 	}
 
-	// Set a breakpoint at line 80, resume, and check we stopped there.
-	pcsLine80, err := prog.BreakpointAtLine("tracee/main.go", 80)
+	// Set a breakpoint at line 125, resume, and check we stopped there.
+	pcsLine125, err := prog.BreakpointAtLine("tracee/main.go", 125)
 	if err != nil {
 		t.Fatal("BreakpointAtLine:", err)
 	}
@@ -286,14 +398,192 @@
 		}
 		return false
 	}
-	if !stoppedAt(pcsLine80) {
-		t.Errorf("stopped at %X; expected one of %X.", status.PC, pcsLine80)
+	if !stoppedAt(pcsLine125) {
+		t.Errorf("stopped at %X; expected one of %X.", status.PC, pcsLine125)
 	}
 
-	// Remove the breakpoint at line 80, set a breakpoint at main.f1 and main.f2,
+	for k, v := range expectedEvaluate {
+		val, err := prog.Evaluate(k)
+		if v == nil {
+			if err == nil {
+				t.Errorf("got Evaluate(%s) = %v, expected error", k, val)
+			}
+			continue
+		}
+		if err != nil {
+			t.Errorf("Evaluate(%s): got error %s, expected %v", k, err, v)
+			continue
+		}
+		typ := reflect.TypeOf(v)
+		if typ != reflect.TypeOf(val) && typ != reflect.TypeOf(int(0)) && typ != reflect.TypeOf(uint(0)) {
+			t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v)
+			continue
+		}
+
+		// For types with fields like Address, TypeID, etc., we can't know the exact
+		// value, so we only test whether those fields are zero or not.
+		switch v := v.(type) {
+		default:
+			if v != val {
+				t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v)
+			}
+		case program.Array:
+			val := val.(program.Array)
+			if v.ElementTypeID == 0 && val.ElementTypeID != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val)
+			}
+			if v.ElementTypeID != 0 && val.ElementTypeID == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val)
+			}
+			if v.Address == 0 && val.Address != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val)
+			}
+			if v.Address != 0 && val.Address == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val)
+			}
+		case program.Slice:
+			val := val.(program.Slice)
+			if v.ElementTypeID == 0 && val.ElementTypeID != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val)
+			}
+			if v.ElementTypeID != 0 && val.ElementTypeID == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val)
+			}
+			if v.Address == 0 && val.Address != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val)
+			}
+			if v.Address != 0 && val.Address == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val)
+			}
+		case program.Map:
+			val := val.(program.Map)
+			if v.TypeID == 0 && val.TypeID != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero TypeID", k, val)
+			}
+			if v.TypeID != 0 && val.TypeID == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero TypeID", k, val)
+			}
+			if v.Address == 0 && val.Address != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val)
+			}
+			if v.Address != 0 && val.Address == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val)
+			}
+		case program.Pointer:
+			val := val.(program.Pointer)
+			if v.TypeID == 0 && val.TypeID != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero TypeID", k, val)
+			}
+			if v.TypeID != 0 && val.TypeID == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero TypeID", k, val)
+			}
+			if v.Address == 0 && val.Address != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val)
+			}
+			if v.Address != 0 && val.Address == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val)
+			}
+		case program.Channel:
+			val := val.(program.Channel)
+			if v.ElementTypeID == 0 && val.ElementTypeID != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero ElementTypeID", k, val)
+			}
+			if v.ElementTypeID != 0 && val.ElementTypeID == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero ElementTypeID", k, val)
+			}
+			if v.Address == 0 && val.Address != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val)
+			}
+			if v.Address != 0 && val.Address == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val)
+			}
+			if v.Buffer == 0 && val.Buffer != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero Buffer", k, val)
+			}
+			if v.Buffer != 0 && val.Buffer == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero Buffer", k, val)
+			}
+		case program.Struct:
+			val := val.(program.Struct)
+			if len(v.Fields) != len(val.Fields) {
+				t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v)
+				break
+			}
+			for i := range v.Fields {
+				a := v.Fields[i].Name
+				b := val.Fields[i].Name
+				if a != b {
+					t.Errorf("Evaluate(%s): field name mismatch: %s vs %s", k, a, b)
+					break
+				}
+			}
+		case program.Func:
+			val := val.(program.Func)
+			if v.Address == 0 && val.Address != 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected zero Address", k, val)
+			}
+			if v.Address != 0 && val.Address == 0 {
+				t.Errorf("got Evaluate(%s) = %+v, expected non-zero Address", k, val)
+			}
+		case int:
+			// ints in a remote program can be returned as int32 or int64
+			switch val := val.(type) {
+			case int32:
+				if val != int32(v) {
+					t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v)
+				}
+			case int64:
+				if val != int64(v) {
+					t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v)
+				}
+			default:
+				t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v)
+			}
+		case uint:
+			// uints in a remote program can be returned as uint32 or uint64
+			switch val := val.(type) {
+			case uint32:
+				if val != uint32(v) {
+					t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v)
+				}
+			case uint64:
+				if val != uint64(v) {
+					t.Errorf("got Evaluate(%s) = %T(%v), expected %v", k, val, val, v)
+				}
+			default:
+				t.Errorf("got Evaluate(%s) = %T(%v), expected %T(%v)", k, val, val, v, v)
+			}
+		}
+	}
+
+	// Evaluate a struct.
+	val, err := prog.Evaluate(`local_struct`)
+	if err != nil {
+		t.Fatalf("Evaluate: %s", err)
+	}
+	s, ok := val.(program.Struct)
+	if !ok {
+		t.Fatalf("got Evaluate(`local_struct`) = %T(%v), expected program.Struct", val, val)
+	}
+	// Check the values of its fields.
+	if len(s.Fields) != 2 {
+		t.Fatalf("got Evaluate(`local_struct`) = %+v, expected 2 fields", s)
+	}
+	if v0, err := prog.Value(s.Fields[0].Var); err != nil {
+		t.Errorf("Value: %s", err)
+	} else if v0 != int32(21) && v0 != int64(21) {
+		t.Errorf("Value: got %T(%v), expected 21", v0, v0)
+	}
+	if v1, err := prog.Value(s.Fields[1].Var); err != nil {
+		t.Errorf("Value: %s", err)
+	} else if v1 != (program.String{2, "hi"}) {
+		t.Errorf("Value: got %T(%v), expected `hi`", v1, v1)
+	}
+
+	// Remove the breakpoint at line 125, set a breakpoint at main.f1 and main.f2,
 	// then delete the breakpoint at main.f1.  Resume, then check we stopped at
 	// main.f2.
-	err = prog.DeleteBreakpoints(pcsLine80)
+	err = prog.DeleteBreakpoints(pcsLine125)
 	if err != nil {
 		log.Fatalf("DeleteBreakpoints: %v", err)
 	}
diff --git a/ogle/demo/tracee/main.go b/ogle/demo/tracee/main.go
index 1293ba2..91b942e 100644
--- a/ogle/demo/tracee/main.go
+++ b/ogle/demo/tracee/main.go
@@ -25,6 +25,8 @@
 
 func (f *FooStruct) Bar() {}
 
+type myInt int16
+
 var (
 	Z_bool_false          bool        = false
 	Z_bool_true           bool        = true
@@ -33,6 +35,7 @@
 	Z_int16               int16       = -32321
 	Z_int32               int32       = -1987654321
 	Z_int64               int64       = -9012345678987654321
+	Z_int_typedef         myInt       = 88
 	Z_uint                uint        = 21
 	Z_uint8               uint8       = 231
 	Z_uint16              uint16      = 54321
@@ -74,8 +77,53 @@
 )
 
 func foo(x int16, y float32) {
+	var (
+		local_array               [5]int8    = [5]int8{-121, 121, 3, 2, 1}
+		local_bool_false          bool       = false
+		local_bool_true           bool       = true
+		local_channel             chan int16 = Z_channel
+		local_channel_buffered    chan int16 = Z_channel_buffered
+		local_channel_nil         chan int16
+		local_complex128          complex128        = 1.987654321 - 2.987654321i
+		local_complex64           complex64         = 1.54321 + 2.54321i
+		local_float32             float32           = 1.54321
+		local_float64             float64           = 1.987654321
+		local_func_bar                              = (*FooStruct).Bar
+		local_func_int8_r_int8                      = func(x int8) int8 { return x + 1 }
+		local_func_int8_r_pint8                     = func(x int8) *int8 { y := x + 1; return &y }
+		local_func_nil            func(x int8) int8 = nil
+		local_int                 int               = -21
+		local_int16               int16             = -32321
+		local_int32               int32             = -1987654321
+		local_int64               int64             = -9012345678987654321
+		local_int8                int8              = -121
+		local_int_typedef         myInt             = 88
+		local_interface           FooInterface      = &Z_struct
+		local_interface_nil       FooInterface
+		local_interface_typed_nil FooInterface     = Z_pointer_nil
+		local_map                 map[int8]float32 = map[int8]float32{-21: 3.54321}
+		local_map_2               map[int16]int8   = map[int16]int8{1024: 1}
+		local_map_3               map[int16]int8   = map[int16]int8{1024: 1, 512: -1}
+		local_map_empty           map[int8]float32 = map[int8]float32{}
+		local_map_nil             map[int8]float32
+		local_pointer             *FooStruct = &Z_struct
+		local_pointer_nil         *FooStruct
+		local_slice               []byte = []byte{'s', 'l', 'i', 'c', 'e'}
+		local_slice_2             []int8 = Z_array[0:2]
+		local_slice_nil           []byte
+		local_string              string         = "I'm a string"
+		local_struct              FooStruct      = FooStruct{a: 21, b: "hi"}
+		local_uint                uint           = 21
+		local_uint16              uint16         = 54321
+		local_uint32              uint32         = 3217654321
+		local_uint64              uint64         = 12345678900987654321
+		local_uint8               uint8          = 231
+		local_uintptr             uintptr        = 21
+		local_unsafe_pointer      unsafe.Pointer = unsafe.Pointer(&Z_uint)
+		local_unsafe_pointer_nil  unsafe.Pointer
+	)
 	fmt.Println(Z_bool_false, Z_bool_true)
-	fmt.Println(Z_int, Z_int8, Z_int16, Z_int32, Z_int64)
+	fmt.Println(Z_int, Z_int8, Z_int16, Z_int32, Z_int64, Z_int_typedef)
 	fmt.Println(Z_uint, Z_uint8, Z_uint16, Z_uint32, Z_uint64, Z_uintptr)
 	fmt.Println(Z_float32, Z_float64, Z_complex64, Z_complex128)
 	fmt.Println(Z_array, Z_array_empty, Z_array_of_empties)
@@ -87,6 +135,18 @@
 	fmt.Println(Z_slice, Z_slice_2, Z_slice_nil)
 	fmt.Println(Z_string, Z_struct)
 	fmt.Println(Z_unsafe_pointer, Z_unsafe_pointer_nil)
+	fmt.Println(local_bool_false, local_bool_true)
+	fmt.Println(local_int, local_int8, local_int16, local_int32, local_int64, local_int_typedef)
+	fmt.Println(local_uint, local_uint8, local_uint16, local_uint32, local_uint64, local_uintptr)
+	fmt.Println(local_float32, local_float64, local_complex64, local_complex128, local_array)
+	fmt.Println(local_channel, local_channel_buffered, local_channel_nil)
+	fmt.Println(local_func_bar, local_func_int8_r_int8, local_func_int8_r_pint8, local_func_nil)
+	fmt.Println(local_interface, local_interface_nil, local_interface_typed_nil)
+	fmt.Println(local_map, local_map_2, local_map_3, local_map_empty, local_map_nil)
+	fmt.Println(local_pointer, local_pointer_nil)
+	fmt.Println(local_slice, local_slice_2, local_slice_nil)
+	fmt.Println(local_string, local_struct)
+	fmt.Println(local_unsafe_pointer, local_unsafe_pointer_nil)
 	f1()
 	f2()
 }
diff --git a/ogle/program/client/client.go b/ogle/program/client/client.go
index 6841767..6083d04 100644
--- a/ogle/program/client/client.go
+++ b/ogle/program/client/client.go
@@ -223,6 +223,15 @@
 	return resp.Result, err
 }
 
+func (p *Program) Evaluate(e string) (program.Value, error) {
+	req := proxyrpc.EvaluateRequest{
+		Expression: e,
+	}
+	var resp proxyrpc.EvaluateResponse
+	err := p.client.Call("Server.Evaluate", &req, &resp)
+	return resp.Result, err
+}
+
 func (p *Program) Frames(count int) ([]program.Frame, error) {
 	req := proxyrpc.FramesRequest{
 		Count: count,
diff --git a/ogle/program/local/local.go b/ogle/program/local/local.go
index cbbb681..55f8c12 100644
--- a/ogle/program/local/local.go
+++ b/ogle/program/local/local.go
@@ -108,6 +108,15 @@
 	return resp.Result, err
 }
 
+func (l *Local) Evaluate(e string) (program.Value, error) {
+	req := proxyrpc.EvaluateRequest{
+		Expression: e,
+	}
+	var resp proxyrpc.EvaluateResponse
+	err := l.s.Evaluate(&req, &resp)
+	return resp.Result, err
+}
+
 func (l *Local) Frames(count int) ([]program.Frame, error) {
 	req := proxyrpc.FramesRequest{
 		Count: count,
diff --git a/ogle/program/program.go b/ogle/program/program.go
index 4409781..b7feb20 100644
--- a/ogle/program/program.go
+++ b/ogle/program/program.go
@@ -63,6 +63,7 @@
 	// Eval evaluates the expression (typically an address) and returns
 	// its string representation(s). Multivalued expressions such as
 	// matches for regular expressions return multiple values.
+	// TODO: change this to multiple functions with more specific names.
 	// Syntax:
 	//	re:regexp
 	//		Returns a list of symbol names that match the expression
@@ -77,6 +78,19 @@
 	//		symbol ("main.foo") at that address (hex, octal, decimal).
 	Eval(expr string) ([]string, error)
 
+	// Evaluate evaluates an expression.  Accepts a subset of Go expression syntax:
+	// basic literals, identifiers, parenthesized expressions, and most operators.
+	// Only the len function call is available.
+	//
+	// The expression can refer to local variables and function parameters of the
+	// function where the program is stopped.
+	//
+	// On success, the type of the value returned will be one of:
+	// int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64,
+	// complex64, complex128, bool, Pointer, Array, Slice, String, Map, Struct,
+	// Channel, Func, or Interface.
+	Evaluate(e string) (Value, error)
+
 	// Frames returns up to count stack frames from where the program
 	// is currently stopped.
 	Frames(count int) ([]Frame, error)
diff --git a/ogle/program/proxyrpc/proxyrpc.go b/ogle/program/proxyrpc/proxyrpc.go
index 80031c3..8ed02b4 100644
--- a/ogle/program/proxyrpc/proxyrpc.go
+++ b/ogle/program/proxyrpc/proxyrpc.go
@@ -115,6 +115,14 @@
 	Result []string
 }
 
+type EvaluateRequest struct {
+	Expression string
+}
+
+type EvaluateResponse struct {
+	Result program.Value
+}
+
 type FramesRequest struct {
 	Count int
 }
diff --git a/ogle/program/server/eval.go b/ogle/program/server/eval.go
new file mode 100644
index 0000000..53e4e71
--- /dev/null
+++ b/ogle/program/server/eval.go
@@ -0,0 +1,1493 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// Evaluates Go expressions, using the current values of variables in a program
+// being debugged.
+//
+// TODOs:
+// More overflow checking.
+// Stricter type checking.
+// More expression types.
+
+package server
+
+import (
+	"errors"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"math"
+	"math/big"
+
+	"golang.org/x/debug/dwarf"
+	"golang.org/x/debug/ogle/program"
+)
+
+const prec = 256 // precision for untyped float and complex constants.
+
+var (
+	// Some big.Ints to use in overflow checks.
+	bigIntMaxInt32 = new(big.Int).SetInt64(math.MaxInt32)
+	bigIntMinInt32 = new(big.Int).SetInt64(math.MinInt32)
+	bigIntMaxInt64 = new(big.Int).SetInt64(math.MaxInt64)
+	bigIntMinInt64 = new(big.Int).SetInt64(math.MinInt64)
+)
+
+// result stores an intermediate value produced during evaluation of an expression.
+//
+// d contains the DWARF type of the value.  For untyped values, d will be nil.
+//
+// v contains the value itself.  For numeric and bool types, v will have the
+// corresponding predeclared Go type.
+// For untyped integer, rune, float, complex, string, and bool constants, v will
+// have type untInt, untRune, untFloat, untComplex, untString, or bool,
+// respectively.
+// For values of type int, uint and uintptr, v will be an int32, int64, uint32
+// or uint64 as appropriate.
+// Other types are represented using the corresponding implementation of
+// program.Value in program.go.
+//
+// If an evaluation results in an error, the zero value of result is used.
+type result struct {
+	d dwarf.Type
+	v interface{}
+}
+
+// untInt is an untyped integer constant
+type untInt struct {
+	*big.Int
+}
+
+// untRune is an untyped rune constant
+type untRune struct {
+	*big.Int
+}
+
+// untFloat is an untyped floating-point constant
+type untFloat struct {
+	*big.Float
+}
+
+// untComplex is an untyped complex constant
+type untComplex struct {
+	r *big.Float
+	i *big.Float
+}
+
+// untString is an untyped string constant
+type untString string
+
+// evalExpression evaluates a Go expression.
+// If the program counter and stack pointer are nonzero, they are used to determine
+// what local variables are available and where in memory they are.
+func (s *Server) evalExpression(expression string, pc, sp uint64) (program.Value, error) {
+	e := evaluator{server: s, expression: expression, pc: pc, sp: sp}
+	node, err := parser.ParseExpr(expression)
+	if err != nil {
+		return nil, err
+	}
+	val := e.evalNode(node)
+	if e.evalError != nil {
+		return nil, e.evalError
+	}
+
+	// Convert untyped constants to their default types.
+	switch v := val.v.(type) {
+	case untInt:
+		return e.intFromInteger(v)
+	case untRune:
+		if v.Cmp(bigIntMaxInt32) == +1 {
+			return nil, errors.New("constant overflows rune")
+		}
+		if v.Cmp(bigIntMinInt32) == -1 {
+			return nil, errors.New("constant overflows rune")
+		}
+		return int32(v.Int64()), nil
+	case untFloat:
+		f, _ := v.Float64()
+		if math.IsInf(f, 0) {
+			return nil, errors.New("constant overflows float64")
+		}
+		if math.IsNaN(f) {
+			return nil, errors.New("constant is NaN")
+		}
+		return f, nil
+	case untComplex:
+		r, _ := v.r.Float64()
+		i, _ := v.i.Float64()
+		if math.IsInf(r, 0) || math.IsInf(i, 0) {
+			return nil, errors.New("constant overflows complex128")
+		}
+		if math.IsNaN(r) || math.IsNaN(i) {
+			return nil, errors.New("constant is NaN")
+		}
+		return complex(r, i), nil
+	case untString:
+		return program.String{Length: uint64(len(v)), String: string(v)}, nil
+	}
+	return val.v, nil
+}
+
+type evaluator struct {
+	// expression is the expression being evaluated.
+	expression string
+	// server interacts with the program being debugged.
+	server *Server
+	// curNode is the current parse tree node.  This is set so that error messages
+	// can quote the part of the expression that caused an error.
+	curNode ast.Node
+	// evalError is the first error that occurred while evaluating the expression,
+	// or nil if no error has occurred.
+	evalError error
+	// pc and sp are the current program counter and stack pointer, used for
+	// finding local variables.  If either are zero, the expression is evaluated
+	// without using local variables.
+	pc uint64
+	sp uint64
+}
+
+// setNode sets curNode, and returns curNode's previous value.
+func (e *evaluator) setNode(node ast.Node) (old ast.Node) {
+	old, e.curNode = e.curNode, node
+	return old
+}
+
+// err saves an error that occurred during evaluation.
+// It returns a zero result, so that functions can exit and set an error with
+//	return e.err(...)
+func (e *evaluator) err(s string) result {
+	if e.evalError != nil {
+		return result{}
+	}
+	// Append the substring of the expression that corresponds to the current AST node.
+	start := int(e.curNode.Pos() - 1)
+	end := int(e.curNode.End() - 1)
+	if start < 0 {
+		start = 0
+	}
+	if end > len(e.expression) {
+		end = len(e.expression)
+	}
+	if start > end {
+		start, end = 0, 0
+	}
+	e.evalError = errors.New(s + `: "` + e.expression[start:end] + `"`)
+	return result{}
+}
+
+// evalNode computes the value of a node in the expression tree.
+func (e *evaluator) evalNode(node ast.Node) result {
+	// Set the current node in the evaluator, so that error messages can refer to
+	// it.  Defer a function call that changes it back.
+	defer e.setNode(e.setNode(node))
+
+	switch n := node.(type) {
+	case *ast.Ident:
+		if e.pc != 0 && e.sp != 0 {
+			a, t := e.server.findLocalVar(n.Name, e.pc, e.sp)
+			if t != nil {
+				return e.resultFrom(a, t)
+			}
+		}
+		a, t := e.server.findGlobalVar(n.Name)
+		if t != nil {
+			return e.resultFrom(a, t)
+		}
+		switch n.Name {
+		// Note: these could have been redefined as constants in the code, but we
+		// don't have a way to detect that.
+		case "true":
+			return result{nil, true}
+		case "false":
+			return result{nil, false}
+		}
+		return e.err("unknown identifier")
+
+	case *ast.BasicLit:
+		switch n.Kind {
+		case token.INT:
+			i := new(big.Int)
+			if _, ok := i.SetString(n.Value, 0); !ok {
+				return e.err("invalid integer constant")
+			}
+			return result{nil, untInt{i}}
+		case token.FLOAT:
+			r, _, err := big.ParseFloat(n.Value, 10, prec, big.ToNearestEven)
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return result{nil, untFloat{r}}
+		case token.IMAG:
+			if len(n.Value) <= 1 || n.Value[len(n.Value)-1] != 'i' {
+				return e.err("invalid imaginary constant")
+			}
+			r, _, err := big.ParseFloat(n.Value[:len(n.Value)-1], 10, prec, big.ToNearestEven)
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return result{nil, untComplex{new(big.Float), r}}
+		case token.CHAR:
+			// TODO: unescaping
+			return result{nil, untRune{new(big.Int).SetInt64(int64(n.Value[1]))}}
+		case token.STRING:
+			// TODO: unescaping
+			if len(n.Value) <= 1 {
+				return e.err("invalid string constant")
+			}
+			return result{nil, untString(n.Value[1 : len(n.Value)-1])}
+		}
+
+	case *ast.ParenExpr:
+		return e.evalNode(n.X)
+
+	case *ast.UnaryExpr:
+		x := e.evalNode(n.X)
+		if x.v == nil {
+			return x
+		}
+		switch v := x.v.(type) {
+
+		case int8:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case int16:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case int32:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case int64:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case uint8:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case uint16:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case uint32:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case uint64:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case float32:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case float64:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case complex64:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case complex128:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case untInt:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.Int.Neg(v.Int)
+			case token.XOR:
+				v.Int.Not(v.Int)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case untRune:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.Int.Neg(v.Int)
+			case token.XOR:
+				v.Int.Not(v.Int)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case untFloat:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.Float.Neg(v.Float)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case untComplex:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.r.Neg(v.r)
+				v.i.Neg(v.i)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case bool:
+			switch n.Op {
+			case token.NOT:
+				v = !v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+		}
+
+	case *ast.BinaryExpr:
+		x := e.evalNode(n.X)
+		if x.v == nil {
+			return x
+		}
+		y := e.evalNode(n.Y)
+		if y.v == nil {
+			return y
+		}
+		return e.evalBinaryOp(n.Op, x, y)
+	}
+	return e.err("invalid expression")
+}
+
+// evalBinaryOp evaluates a binary operator op applied to x and y.
+func (e *evaluator) evalBinaryOp(op token.Token, x, y result) result {
+	if op == token.NEQ {
+		tmp := e.evalBinaryOp(token.EQL, x, y)
+		b, ok := tmp.v.(bool)
+		if !ok {
+			return tmp
+		}
+		return result{nil, !b}
+	}
+	if op == token.GTR {
+		return e.evalBinaryOp(token.LSS, y, x)
+	}
+	if op == token.GEQ {
+		return e.evalBinaryOp(token.LEQ, x, y)
+	}
+
+	x = convertUntyped(x, y)
+	y = convertUntyped(y, x)
+
+	switch a := x.v.(type) {
+
+	case int8:
+		b, ok := y.v.(int8)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c int8
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case int16:
+		b, ok := y.v.(int16)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c int16
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case int32:
+		b, ok := y.v.(int32)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c int32
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case int64:
+		b, ok := y.v.(int64)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c int64
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case uint8:
+		b, ok := y.v.(uint8)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c uint8
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case uint16:
+		b, ok := y.v.(uint16)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c uint16
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case uint32:
+		b, ok := y.v.(uint32)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c uint32
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case uint64:
+		b, ok := y.v.(uint64)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c uint64
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case float32:
+		b, ok := y.v.(float32)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c float32
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			c = a / b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case float64:
+		b, ok := y.v.(float64)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c float64
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			c = a / b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case complex64:
+		b, ok := y.v.(complex64)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c complex64
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			c = a / b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case complex128:
+		b, ok := y.v.(complex128)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c complex128
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			c = a / b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case bool:
+		b, ok := y.v.(bool)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c bool
+		switch op {
+		case token.LOR:
+			c = a || b
+		case token.LAND:
+			c = a && b
+		case token.EQL:
+			c = a == b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case program.String:
+		b, ok := y.v.(program.String)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c program.String
+		switch op {
+		// TODO: these comparison operators only use the part of the string that
+		// was read.  Very large strings do not have their entire contents read by
+		// server.value.
+		case token.EQL:
+			return result{nil, a.Length == b.Length && a.String == b.String}
+		case token.LSS:
+			return result{nil, a.String < b.String}
+		case token.LEQ:
+			return result{nil, a.String <= b.String}
+		case token.ADD:
+			c.Length = a.Length + b.Length
+			if a.Length == uint64(len(a.String)) {
+				c.String = a.String + b.String
+			} else {
+				// The first string was truncated at a.Length characters, so the sum
+				// must be truncated there too.
+				c.String = a.String
+			}
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case untString:
+		b, ok := y.v.(untString)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c untString
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case untInt:
+		i := a.Int
+		b, ok := y.v.(untInt)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		switch op {
+		case token.EQL:
+			return result{nil, i.Cmp(b.Int) == 0}
+		case token.LSS:
+			return result{nil, i.Cmp(b.Int) < 0}
+		case token.LEQ:
+			return result{nil, i.Cmp(b.Int) <= 0}
+		}
+		c := new(big.Int)
+		switch op {
+		case token.ADD:
+			c.Add(i, b.Int)
+		case token.SUB:
+			c.Sub(i, b.Int)
+		case token.OR:
+			c.Or(i, b.Int)
+		case token.XOR:
+			c.Xor(i, b.Int)
+		case token.MUL:
+			c.Mul(i, b.Int)
+		case token.QUO:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Quo(i, b.Int)
+		case token.REM:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Mod(i, b.Int)
+		case token.AND:
+			c.And(i, b.Int)
+		case token.AND_NOT:
+			c.AndNot(i, b.Int)
+		default:
+			return e.err("invalid operation")
+		}
+		return result{nil, untInt{c}}
+
+	case untRune:
+		i := a.Int
+		b, ok := y.v.(untRune)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		switch op {
+		case token.EQL:
+			return result{nil, i.Cmp(b.Int) == 0}
+		case token.LSS:
+			return result{nil, i.Cmp(b.Int) < 0}
+		case token.LEQ:
+			return result{nil, i.Cmp(b.Int) <= 0}
+		}
+		c := new(big.Int)
+		switch op {
+		case token.ADD:
+			c.Add(i, b.Int)
+		case token.SUB:
+			c.Sub(i, b.Int)
+		case token.OR:
+			c.Or(i, b.Int)
+		case token.XOR:
+			c.Xor(i, b.Int)
+		case token.MUL:
+			c.Mul(i, b.Int)
+		case token.QUO:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Quo(i, b.Int)
+		case token.REM:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Mod(i, b.Int)
+		case token.AND:
+			c.And(i, b.Int)
+		case token.AND_NOT:
+			c.AndNot(i, b.Int)
+		default:
+			return e.err("invalid operation")
+		}
+		return result{nil, untRune{c}}
+
+	case untFloat:
+		r := a.Float
+		b, ok := y.v.(untFloat)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		switch op {
+		case token.EQL:
+			return result{nil, r.Cmp(b.Float) == 0}
+		case token.LSS:
+			return result{nil, r.Cmp(b.Float) < 0}
+		case token.LEQ:
+			return result{nil, r.Cmp(b.Float) <= 0}
+		}
+		c := new(big.Float)
+		switch op {
+		case token.ADD:
+			c.Add(r, b.Float)
+		case token.SUB:
+			c.Sub(r, b.Float)
+		case token.MUL:
+			c.Mul(r, b.Float)
+		case token.QUO:
+			if b.Sign() == 0 {
+				return e.err("divide by zero")
+			}
+			c.Quo(r, b.Float)
+		default:
+			return e.err("invalid operation")
+		}
+		return result{nil, untFloat{c}}
+
+	case untComplex:
+		b, ok := y.v.(untComplex)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var (
+			ar = a.r
+			br = b.r
+			ai = a.i
+			bi = b.i
+		)
+		if op == token.EQL {
+			return result{nil, ar.Cmp(br) == 0 && ai.Cmp(bi) == 0}
+		}
+		var (
+			cr = new(big.Float)
+			ci = new(big.Float)
+		)
+		switch op {
+		case token.ADD:
+			cr.Add(ar, br)
+			ci.Add(ai, bi)
+		case token.SUB:
+			cr.Sub(ar, br)
+			ci.Sub(ai, bi)
+		case token.MUL:
+			var t0, t1 big.Float
+			t0.Mul(ar, br)
+			t1.Mul(ai, bi)
+			cr.Sub(&t0, &t1)
+			t0.Mul(ar, bi)
+			t1.Mul(ai, br)
+			ci.Add(&t0, &t1)
+		case token.QUO:
+			// a/b = a*conj(b)/|b|^2
+			var t0, t1 big.Float
+			cr.Mul(ar, br)
+			t0.Mul(ai, bi)
+			cr.Add(cr, &t0) // cr = Re(a*conj(b))
+			ci.Mul(ai, br)
+			t0.Mul(ar, bi)
+			ci.Sub(ci, &t0) // ci = Im(a*conj(b))
+			t0.Mul(br, br)
+			t1.Mul(bi, bi)
+			t0.Add(&t0, &t1) // t0 = |b|^2
+			if t0.Sign() == 0 {
+				return e.err("divide by zero")
+			}
+			cr.Quo(cr, &t0) // cr = Re(a*conj(b))/|b|^2 = Re(a/b)
+			ci.Quo(ci, &t0) // ci = Im(a*conj(b))/|b|^2 = Im(a/b)
+		}
+		return result{nil, untComplex{cr, ci}}
+	}
+
+	return e.err("invalid operation")
+}
+
+// findLocalVar finds a local variable (or function parameter) by name, and
+// returns its address and DWARF type.  It returns a nil type on failure.
+// The PC and SP are used to determine the current function and stack frame.
+func (s *Server) findLocalVar(name string, pc, sp uint64) (uint64, dwarf.Type) {
+	// Find the DWARF entry for the function at pc.
+	funcEntry, _, err := s.entryForPC(uint64(pc))
+	if err != nil {
+		return 0, nil
+	}
+
+	// Compute the stack frame pointer.
+	fpOffset, err := s.dwarfData.PCToSPOffset(uint64(pc))
+	if err != nil {
+		return 0, nil
+	}
+	framePointer := sp + uint64(fpOffset)
+
+	// Check each child of the function's DWARF entry to see if it is a parameter
+	// or local variable with the right name.  If so, return its address and type.
+	r := s.dwarfData.Reader()
+	r.Seek(funcEntry.Offset)
+	for {
+		varEntry, err := r.Next()
+		if err != nil {
+			break
+		}
+		if varEntry.Tag == 0 {
+			// This tag marks the end of the function's DWARF entry's children.
+			break
+		}
+
+		// Check this entry corresponds to a local variable or function parameter,
+		// that it has the correct name, and that we can get its type and location.
+		// If so, return them.
+		if varEntry.Tag != dwarf.TagFormalParameter && varEntry.Tag != dwarf.TagVariable {
+			continue
+		}
+		varName, ok := varEntry.Val(dwarf.AttrName).(string)
+		if !ok {
+			continue
+		}
+		if varName != name {
+			continue
+		}
+		varTypeOffset, ok := varEntry.Val(dwarf.AttrType).(dwarf.Offset)
+		if !ok {
+			continue
+		}
+		varType, err := s.dwarfData.Type(varTypeOffset)
+		if err != nil {
+			continue
+		}
+		locationAttribute := varEntry.Val(dwarf.AttrLocation)
+		if locationAttribute == nil {
+			continue
+		}
+		locationDescription, ok := locationAttribute.([]uint8)
+		if !ok {
+			continue
+		}
+		frameOffset, err := evalLocation(locationDescription)
+		if err != nil {
+			continue
+		}
+		return framePointer + uint64(frameOffset), varType
+	}
+
+	return 0, nil
+}
+
+// findGlobalVar finds a global variable by name, and returns its address and
+// DWARF type.  It returns a nil type on failure.
+func (s *Server) findGlobalVar(name string) (uint64, dwarf.Type) {
+	entry, err := s.dwarfData.LookupEntry(name)
+	if err != nil {
+		return 0, nil
+	}
+	loc, err := s.dwarfData.EntryLocation(entry)
+	if err != nil {
+		return 0, nil
+	}
+	ofs, err := s.dwarfData.EntryTypeOffset(entry)
+	if err != nil {
+		return 0, nil
+	}
+	typ, err := s.dwarfData.Type(ofs)
+	if err != nil {
+		return 0, nil
+	}
+	return loc, typ
+}
+
+// intFromInteger converts an untyped integer constant to an int32 or int64,
+// depending on the int size of the debugged program.
+// It returns an error on overflow, or if it can't determine the int size.
+func (e *evaluator) intFromInteger(v untInt) (interface{}, error) {
+	t, ok := e.getBaseType("int")
+	if !ok {
+		return nil, errors.New("couldn't get int size from DWARF info")
+	}
+	switch t.Common().ByteSize {
+	case 4:
+		if v.Cmp(bigIntMaxInt32) == +1 || v.Cmp(bigIntMinInt32) == -1 {
+			return nil, errors.New("constant overflows int")
+		}
+		return int32(v.Int64()), nil
+	case 8:
+		if v.Cmp(bigIntMaxInt64) == +1 || v.Cmp(bigIntMinInt64) == -1 {
+			return nil, errors.New("constant overflows int")
+		}
+		return v.Int64(), nil
+	}
+	return nil, errors.New("invalid int size in DWARF info")
+}
+
+// uint8Result constructs a result for a uint8 value.
+func (e *evaluator) uint8Result(v uint8) result {
+	t, ok := e.getBaseType("uint8")
+	if !ok {
+		e.err("couldn't construct uint8")
+	}
+	return result{t, uint8(v)}
+}
+
+// getBaseType returns the *dwarf.Type with a given name.
+// TODO: cache this.
+func (e *evaluator) getBaseType(name string) (dwarf.Type, bool) {
+	entry, err := e.server.dwarfData.LookupEntry(name)
+	if err != nil {
+		return nil, false
+	}
+	t, err := e.server.dwarfData.Type(entry.Offset)
+	if err != nil {
+		return nil, false
+	}
+	return t, true
+}
+
+// resultFrom constructs a result corresponding to a value in the program with
+// the given address and DWARF type.
+func (e *evaluator) resultFrom(a uint64, t dwarf.Type) result {
+	if a == 0 {
+		return e.err("nil pointer dereference")
+	}
+	v, err := e.server.value(t, a)
+	if err != nil {
+		return e.err(err.Error())
+	}
+	return result{t, v}
+}
+
+// convertUntyped converts x to be the same type as y, if x is untyped and the
+// conversion is possible.
+//
+// An untyped bool can be converted to a boolean type.
+// An untyped string can be converted to a string type.
+// An untyped integer, rune, float or complex value can be converted to a
+// numeric type, or to an untyped value later in that list.
+//
+// x is returned unchanged if none of these cases apply.
+func convertUntyped(x, y result) result {
+	switch a := x.v.(type) {
+	case untInt:
+		i := a.Int
+		switch y.v.(type) {
+		case int8:
+			return result{y.d, int8(i.Int64())}
+		case int16:
+			return result{y.d, int16(i.Int64())}
+		case int32:
+			return result{y.d, int32(i.Int64())}
+		case int64:
+			return result{y.d, int64(i.Int64())}
+		case uint8:
+			return result{y.d, uint8(i.Uint64())}
+		case uint16:
+			return result{y.d, uint16(i.Uint64())}
+		case uint32:
+			return result{y.d, uint32(i.Uint64())}
+		case uint64:
+			return result{y.d, uint64(i.Uint64())}
+		case float32:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, f}
+		case float64:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, f}
+		case complex64:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, complex(f, 0)}
+		case complex128:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, complex(f, 0)}
+		case untRune:
+			return result{nil, untRune{i}}
+		case untFloat:
+			return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}}
+		case untComplex:
+			return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}}
+		}
+	case untRune:
+		i := a.Int
+		switch y.v.(type) {
+		case int8:
+			return result{y.d, int8(i.Int64())}
+		case int16:
+			return result{y.d, int16(i.Int64())}
+		case int32:
+			return result{y.d, int32(i.Int64())}
+		case int64:
+			return result{y.d, int64(i.Int64())}
+		case uint8:
+			return result{y.d, uint8(i.Uint64())}
+		case uint16:
+			return result{y.d, uint16(i.Uint64())}
+		case uint32:
+			return result{y.d, uint32(i.Uint64())}
+		case uint64:
+			return result{y.d, uint64(i.Uint64())}
+		case float32:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, f}
+		case float64:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, f}
+		case complex64:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, complex(f, 0)}
+		case complex128:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, complex(f, 0)}
+		case untRune:
+			return result{nil, untRune{i}}
+		case untFloat:
+			return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}}
+		case untComplex:
+			return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}}
+		}
+	case untFloat:
+		if a.IsInt() {
+			i, _ := a.Int(nil)
+			switch y.v.(type) {
+			case int8:
+				return result{y.d, int8(i.Int64())}
+			case int16:
+				return result{y.d, int16(i.Int64())}
+			case int32:
+				return result{y.d, int32(i.Int64())}
+			case int64:
+				return result{y.d, int64(i.Int64())}
+			case uint8:
+				return result{y.d, uint8(i.Uint64())}
+			case uint16:
+				return result{y.d, uint16(i.Uint64())}
+			case uint32:
+				return result{y.d, uint32(i.Uint64())}
+			case uint64:
+				return result{y.d, uint64(i.Uint64())}
+			}
+		}
+		switch y.v.(type) {
+		case float32:
+			f, _ := a.Float32()
+			return result{y.d, float32(f)}
+		case float64:
+			f, _ := a.Float64()
+			return result{y.d, float64(f)}
+		case complex64:
+			f, _ := a.Float32()
+			return result{y.d, complex(f, 0)}
+		case complex128:
+			f, _ := a.Float64()
+			return result{y.d, complex(f, 0)}
+		case untComplex:
+			return result{nil, untComplex{a.Float, new(big.Float)}}
+		}
+	case untComplex:
+		if a.i.Sign() == 0 {
+			// a is a real number.
+			if a.r.IsInt() {
+				// a is an integer.
+				i, _ := a.r.Int(nil)
+				switch y.v.(type) {
+				case int8:
+					return result{y.d, int8(i.Int64())}
+				case int16:
+					return result{y.d, int16(i.Int64())}
+				case int32:
+					return result{y.d, int32(i.Int64())}
+				case int64:
+					return result{y.d, int64(i.Int64())}
+				case uint8:
+					return result{y.d, uint8(i.Uint64())}
+				case uint16:
+					return result{y.d, uint16(i.Uint64())}
+				case uint32:
+					return result{y.d, uint32(i.Uint64())}
+				case uint64:
+					return result{y.d, uint64(i.Uint64())}
+				}
+			}
+			switch y.v.(type) {
+			case float32:
+				f, _ := a.r.Float32()
+				return result{y.d, float32(f)}
+			case float64:
+				f, _ := a.r.Float64()
+				return result{y.d, float64(f)}
+			}
+		}
+		switch y.v.(type) {
+		case complex64:
+			r, _ := a.r.Float32()
+			i, _ := a.i.Float32()
+			return result{y.d, complex(r, i)}
+		case complex128:
+			r, _ := a.r.Float64()
+			i, _ := a.i.Float64()
+			return result{y.d, complex(r, i)}
+		}
+	case bool:
+		if x.d != nil {
+			// x is a typed bool, not an untyped bool.
+			break
+		}
+		switch y.v.(type) {
+		case bool:
+			return result{y.d, bool(a)}
+		}
+	case untString:
+		switch y.v.(type) {
+		case program.String:
+			return result{y.d, program.String{Length: uint64(len(a)), String: string(a)}}
+		}
+	}
+	return x
+}
diff --git a/ogle/program/server/eval.m4 b/ogle/program/server/eval.m4
new file mode 100644
index 0000000..16b8bad
--- /dev/null
+++ b/ogle/program/server/eval.m4
@@ -0,0 +1,1101 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//m4_changequote(`@',`@')
+// Evaluates Go expressions, using the current values of variables in a program
+// being debugged.
+//
+// TODOs:
+// More overflow checking.
+// Stricter type checking.
+// More expression types.
+
+package server
+
+import (
+	"errors"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"math"
+	"math/big"
+
+	"golang.org/x/debug/dwarf"
+	"golang.org/x/debug/ogle/program"
+)
+
+const prec = 256 // precision for untyped float and complex constants.
+
+var (
+	// Some big.Ints to use in overflow checks.
+	bigIntMaxInt32 = new(big.Int).SetInt64(math.MaxInt32)
+	bigIntMinInt32 = new(big.Int).SetInt64(math.MinInt32)
+	bigIntMaxInt64 = new(big.Int).SetInt64(math.MaxInt64)
+	bigIntMinInt64 = new(big.Int).SetInt64(math.MinInt64)
+)
+
+// result stores an intermediate value produced during evaluation of an expression.
+//
+// d contains the DWARF type of the value.  For untyped values, d will be nil.
+//
+// v contains the value itself.  For numeric and bool types, v will have the
+// corresponding predeclared Go type.
+// For untyped integer, rune, float, complex, string, and bool constants, v will
+// have type untInt, untRune, untFloat, untComplex, untString, or bool,
+// respectively.
+// For values of type int, uint and uintptr, v will be an int32, int64, uint32
+// or uint64 as appropriate.
+// Other types are represented using the corresponding implementation of
+// program.Value in program.go.
+//
+// If an evaluation results in an error, the zero value of result is used.
+type result struct {
+	d dwarf.Type
+	v interface{}
+}
+
+// untInt is an untyped integer constant
+type untInt struct {
+	*big.Int
+}
+
+// untRune is an untyped rune constant
+type untRune struct {
+	*big.Int
+}
+
+// untFloat is an untyped floating-point constant
+type untFloat struct {
+	*big.Float
+}
+
+// untComplex is an untyped complex constant
+type untComplex struct {
+	r *big.Float
+	i *big.Float
+}
+
+// untString is an untyped string constant
+type untString string
+
+// evalExpression evaluates a Go expression.
+// If the program counter and stack pointer are nonzero, they are used to determine
+// what local variables are available and where in memory they are.
+func (s *Server) evalExpression(expression string, pc, sp uint64) (program.Value, error) {
+	e := evaluator{server: s, expression: expression, pc: pc, sp: sp}
+	node, err := parser.ParseExpr(expression)
+	if err != nil {
+		return nil, err
+	}
+	val := e.evalNode(node)
+	if e.evalError != nil {
+		return nil, e.evalError
+	}
+
+	// Convert untyped constants to their default types.
+	switch v := val.v.(type) {
+	case untInt:
+		return e.intFromInteger(v)
+	case untRune:
+		if v.Cmp(bigIntMaxInt32) == +1 {
+			return nil, errors.New("constant overflows rune")
+		}
+		if v.Cmp(bigIntMinInt32) == -1 {
+			return nil, errors.New("constant overflows rune")
+		}
+		return int32(v.Int64()), nil
+	case untFloat:
+		f, _ := v.Float64()
+		if math.IsInf(f, 0) {
+			return nil, errors.New("constant overflows float64")
+		}
+		if math.IsNaN(f) {
+			return nil, errors.New("constant is NaN")
+		}
+		return f, nil
+	case untComplex:
+		r, _ := v.r.Float64()
+		i, _ := v.i.Float64()
+		if math.IsInf(r, 0) || math.IsInf(i, 0) {
+			return nil, errors.New("constant overflows complex128")
+		}
+		if math.IsNaN(r) || math.IsNaN(i) {
+			return nil, errors.New("constant is NaN")
+		}
+		return complex(r, i), nil
+	case untString:
+		return program.String{Length: uint64(len(v)), String: string(v)}, nil
+	}
+	return val.v, nil
+}
+
+type evaluator struct {
+	// expression is the expression being evaluated.
+	expression string
+	// server interacts with the program being debugged.
+	server *Server
+	// curNode is the current parse tree node.  This is set so that error messages
+	// can quote the part of the expression that caused an error.
+	curNode ast.Node
+	// evalError is the first error that occurred while evaluating the expression,
+	// or nil if no error has occurred.
+	evalError error
+	// pc and sp are the current program counter and stack pointer, used for
+	// finding local variables.  If either are zero, the expression is evaluated
+	// without using local variables.
+	pc uint64
+	sp uint64
+}
+
+// setNode sets curNode, and returns curNode's previous value.
+func (e *evaluator) setNode(node ast.Node) (old ast.Node) {
+	old, e.curNode = e.curNode, node
+	return old
+}
+
+// err saves an error that occurred during evaluation.
+// It returns a zero result, so that functions can exit and set an error with
+//	return e.err(...)
+func (e *evaluator) err(s string) result {
+	if e.evalError != nil {
+		return result{}
+	}
+	// Append the substring of the expression that corresponds to the current AST node.
+	start := int(e.curNode.Pos() - 1)
+	end := int(e.curNode.End() - 1)
+	if start < 0 {
+		start = 0
+	}
+	if end > len(e.expression) {
+		end = len(e.expression)
+	}
+	if start > end {
+		start, end = 0, 0
+	}
+	e.evalError = errors.New(s + `: "` + e.expression[start:end] + `"`)
+	return result{}
+}
+
+// evalNode computes the value of a node in the expression tree.
+func (e *evaluator) evalNode(node ast.Node) result {
+	// Set the current node in the evaluator, so that error messages can refer to
+	// it.  Defer a function call that changes it back.
+	defer e.setNode(e.setNode(node))
+
+	switch n := node.(type) {
+	case *ast.Ident:
+		if e.pc != 0 && e.sp != 0 {
+			a, t := e.server.findLocalVar(n.Name, e.pc, e.sp)
+			if t != nil {
+				return e.resultFrom(a, t)
+			}
+		}
+		a, t := e.server.findGlobalVar(n.Name)
+		if t != nil {
+			return e.resultFrom(a, t)
+		}
+		switch n.Name {
+		// Note: these could have been redefined as constants in the code, but we
+		// don't have a way to detect that.
+		case "true":
+			return result{nil, true}
+		case "false":
+			return result{nil, false}
+		}
+		return e.err("unknown identifier")
+
+	case *ast.BasicLit:
+		switch n.Kind {
+		case token.INT:
+			i := new(big.Int)
+			if _, ok := i.SetString(n.Value, 0); !ok {
+				return e.err("invalid integer constant")
+			}
+			return result{nil, untInt{i}}
+		case token.FLOAT:
+			r, _, err := big.ParseFloat(n.Value, 10, prec, big.ToNearestEven)
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return result{nil, untFloat{r}}
+		case token.IMAG:
+			if len(n.Value) <= 1 || n.Value[len(n.Value)-1] != 'i' {
+				return e.err("invalid imaginary constant")
+			}
+			r, _, err := big.ParseFloat(n.Value[:len(n.Value)-1], 10, prec, big.ToNearestEven)
+			if err != nil {
+				return e.err(err.Error())
+			}
+			return result{nil, untComplex{new(big.Float), r}}
+		case token.CHAR:
+			// TODO: unescaping
+			return result{nil, untRune{new(big.Int).SetInt64(int64(n.Value[1]))}}
+		case token.STRING:
+			// TODO: unescaping
+			if len(n.Value) <= 1 {
+				return e.err("invalid string constant")
+			}
+			return result{nil, untString(n.Value[1 : len(n.Value)-1])}
+		}
+
+	case *ast.ParenExpr:
+		return e.evalNode(n.X)
+
+	case *ast.UnaryExpr:
+		x := e.evalNode(n.X)
+		if x.v == nil {
+			return x
+		}
+		switch v := x.v.(type) {
+m4_define(UNARY_INT_OPS, @case $1:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			case token.XOR:
+				v = ^v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+@)
+m4_define(UNARY_FLOAT_OPS, @case $1:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v = -v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+@)
+		UNARY_INT_OPS(int8)
+		UNARY_INT_OPS(int16)
+		UNARY_INT_OPS(int32)
+		UNARY_INT_OPS(int64)
+		UNARY_INT_OPS(uint8)
+		UNARY_INT_OPS(uint16)
+		UNARY_INT_OPS(uint32)
+		UNARY_INT_OPS(uint64)
+		UNARY_FLOAT_OPS(float32)
+		UNARY_FLOAT_OPS(float64)
+		UNARY_FLOAT_OPS(complex64)
+		UNARY_FLOAT_OPS(complex128)
+		case untInt:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.Int.Neg(v.Int)
+			case token.XOR:
+				v.Int.Not(v.Int)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case untRune:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.Int.Neg(v.Int)
+			case token.XOR:
+				v.Int.Not(v.Int)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case untFloat:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.Float.Neg(v.Float)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case untComplex:
+			switch n.Op {
+			case token.ADD:
+			case token.SUB:
+				v.r.Neg(v.r)
+				v.i.Neg(v.i)
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+
+		case bool:
+			switch n.Op {
+			case token.NOT:
+				v = !v
+			default:
+				return e.err("invalid operation")
+			}
+			return result{x.d, v}
+		}
+
+	case *ast.BinaryExpr:
+		x := e.evalNode(n.X)
+		if x.v == nil {
+			return x
+		}
+		y := e.evalNode(n.Y)
+		if y.v == nil {
+			return y
+		}
+		return e.evalBinaryOp(n.Op, x, y)
+	}
+	return e.err("invalid expression")
+}
+
+// evalBinaryOp evaluates a binary operator op applied to x and y.
+func (e *evaluator) evalBinaryOp(op token.Token, x, y result) result {
+	if op == token.NEQ {
+		tmp := e.evalBinaryOp(token.EQL, x, y)
+		b, ok := tmp.v.(bool)
+		if !ok {
+			return tmp
+		}
+		return result{nil, !b}
+	}
+	if op == token.GTR {
+		return e.evalBinaryOp(token.LSS, y, x)
+	}
+	if op == token.GEQ {
+		return e.evalBinaryOp(token.LEQ, x, y)
+	}
+
+	x = convertUntyped(x, y)
+	y = convertUntyped(y, x)
+
+	switch a := x.v.(type) {
+m4_define(INT_OPS, @case $1:
+		b, ok := y.v.($1)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c $1
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+@)
+m4_define(UINT_OPS, @case $1:
+		b, ok := y.v.($1)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c $1
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.OR:
+			c = a | b
+		case token.XOR:
+			c = a ^ b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a / b
+		case token.REM:
+			if b == 0 {
+				return e.err("integer divide by zero")
+			}
+			c = a % b
+		case token.AND:
+			c = a & b
+		case token.AND_NOT:
+			c = a &^ b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+@)
+m4_define(FLOAT_OPS, @case $1:
+		b, ok := y.v.($1)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c $1
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			c = a / b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+@)
+m4_define(COMPLEX_OPS, @case $1:
+		b, ok := y.v.($1)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c $1
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.ADD:
+			c = a + b
+		case token.SUB:
+			c = a - b
+		case token.MUL:
+			c = a * b
+		case token.QUO:
+			c = a / b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+@)
+	INT_OPS(int8)
+	INT_OPS(int16)
+	INT_OPS(int32)
+	INT_OPS(int64)
+	UINT_OPS(uint8)
+	UINT_OPS(uint16)
+	UINT_OPS(uint32)
+	UINT_OPS(uint64)
+	FLOAT_OPS(float32)
+	FLOAT_OPS(float64)
+	COMPLEX_OPS(complex64)
+	COMPLEX_OPS(complex128)
+	case bool:
+		b, ok := y.v.(bool)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c bool
+		switch op {
+		case token.LOR:
+			c = a || b
+		case token.LAND:
+			c = a && b
+		case token.EQL:
+			c = a == b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case program.String:
+		b, ok := y.v.(program.String)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c program.String
+		switch op {
+		// TODO: these comparison operators only use the part of the string that
+		// was read.  Very large strings do not have their entire contents read by
+		// server.value.
+		case token.EQL:
+			return result{nil, a.Length == b.Length && a.String == b.String}
+		case token.LSS:
+			return result{nil, a.String < b.String}
+		case token.LEQ:
+			return result{nil, a.String <= b.String}
+		case token.ADD:
+			c.Length = a.Length + b.Length
+			if a.Length == uint64(len(a.String)) {
+				c.String = a.String + b.String
+			} else {
+				// The first string was truncated at a.Length characters, so the sum
+				// must be truncated there too.
+				c.String = a.String
+			}
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case untString:
+		b, ok := y.v.(untString)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var c untString
+		switch op {
+		case token.EQL:
+			return result{nil, a == b}
+		case token.LSS:
+			return result{nil, a < b}
+		case token.LEQ:
+			return result{nil, a <= b}
+		case token.ADD:
+			c = a + b
+		default:
+			return e.err("invalid operation")
+		}
+		return result{x.d, c}
+
+	case untInt:
+		i := a.Int
+		b, ok := y.v.(untInt)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		switch op {
+		case token.EQL:
+			return result{nil, i.Cmp(b.Int) == 0}
+		case token.LSS:
+			return result{nil, i.Cmp(b.Int) < 0}
+		case token.LEQ:
+			return result{nil, i.Cmp(b.Int) <= 0}
+		}
+		c := new(big.Int)
+		switch op {
+		case token.ADD:
+			c.Add(i, b.Int)
+		case token.SUB:
+			c.Sub(i, b.Int)
+		case token.OR:
+			c.Or(i, b.Int)
+		case token.XOR:
+			c.Xor(i, b.Int)
+		case token.MUL:
+			c.Mul(i, b.Int)
+		case token.QUO:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Quo(i, b.Int)
+		case token.REM:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Mod(i, b.Int)
+		case token.AND:
+			c.And(i, b.Int)
+		case token.AND_NOT:
+			c.AndNot(i, b.Int)
+		default:
+			return e.err("invalid operation")
+		}
+		return result{nil, untInt{c}}
+
+	case untRune:
+		i := a.Int
+		b, ok := y.v.(untRune)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		switch op {
+		case token.EQL:
+			return result{nil, i.Cmp(b.Int) == 0}
+		case token.LSS:
+			return result{nil, i.Cmp(b.Int) < 0}
+		case token.LEQ:
+			return result{nil, i.Cmp(b.Int) <= 0}
+		}
+		c := new(big.Int)
+		switch op {
+		case token.ADD:
+			c.Add(i, b.Int)
+		case token.SUB:
+			c.Sub(i, b.Int)
+		case token.OR:
+			c.Or(i, b.Int)
+		case token.XOR:
+			c.Xor(i, b.Int)
+		case token.MUL:
+			c.Mul(i, b.Int)
+		case token.QUO:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Quo(i, b.Int)
+		case token.REM:
+			if b.Sign() == 0 {
+				return e.err("integer divide by zero")
+			}
+			c.Mod(i, b.Int)
+		case token.AND:
+			c.And(i, b.Int)
+		case token.AND_NOT:
+			c.AndNot(i, b.Int)
+		default:
+			return e.err("invalid operation")
+		}
+		return result{nil, untRune{c}}
+
+	case untFloat:
+		r := a.Float
+		b, ok := y.v.(untFloat)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		switch op {
+		case token.EQL:
+			return result{nil, r.Cmp(b.Float) == 0}
+		case token.LSS:
+			return result{nil, r.Cmp(b.Float) < 0}
+		case token.LEQ:
+			return result{nil, r.Cmp(b.Float) <= 0}
+		}
+		c := new(big.Float)
+		switch op {
+		case token.ADD:
+			c.Add(r, b.Float)
+		case token.SUB:
+			c.Sub(r, b.Float)
+		case token.MUL:
+			c.Mul(r, b.Float)
+		case token.QUO:
+			if b.Sign() == 0 {
+				return e.err("divide by zero")
+			}
+			c.Quo(r, b.Float)
+		default:
+			return e.err("invalid operation")
+		}
+		return result{nil, untFloat{c}}
+
+	case untComplex:
+		b, ok := y.v.(untComplex)
+		if !ok {
+			return e.err("type mismatch")
+		}
+		var (
+			ar = a.r
+			br = b.r
+			ai = a.i
+			bi = b.i
+		)
+		if op == token.EQL {
+			return result{nil, ar.Cmp(br) == 0 && ai.Cmp(bi) == 0}
+		}
+		var (
+			cr = new(big.Float)
+			ci = new(big.Float)
+		)
+		switch op {
+		case token.ADD:
+			cr.Add(ar, br)
+			ci.Add(ai, bi)
+		case token.SUB:
+			cr.Sub(ar, br)
+			ci.Sub(ai, bi)
+		case token.MUL:
+			var t0, t1 big.Float
+			t0.Mul(ar, br)
+			t1.Mul(ai, bi)
+			cr.Sub(&t0, &t1)
+			t0.Mul(ar, bi)
+			t1.Mul(ai, br)
+			ci.Add(&t0, &t1)
+		case token.QUO:
+			// a/b = a*conj(b)/|b|^2
+			var t0, t1 big.Float
+			cr.Mul(ar, br)
+			t0.Mul(ai, bi)
+			cr.Add(cr, &t0) // cr = Re(a*conj(b))
+			ci.Mul(ai, br)
+			t0.Mul(ar, bi)
+			ci.Sub(ci, &t0) // ci = Im(a*conj(b))
+			t0.Mul(br, br)
+			t1.Mul(bi, bi)
+			t0.Add(&t0, &t1) // t0 = |b|^2
+			if t0.Sign() == 0 {
+				return e.err("divide by zero")
+			}
+			cr.Quo(cr, &t0) // cr = Re(a*conj(b))/|b|^2 = Re(a/b)
+			ci.Quo(ci, &t0) // ci = Im(a*conj(b))/|b|^2 = Im(a/b)
+		}
+		return result{nil, untComplex{cr, ci}}
+	}
+
+	return e.err("invalid operation")
+}
+
+// findLocalVar finds a local variable (or function parameter) by name, and
+// returns its address and DWARF type.  It returns a nil type on failure.
+// The PC and SP are used to determine the current function and stack frame.
+func (s *Server) findLocalVar(name string, pc, sp uint64) (uint64, dwarf.Type) {
+	// Find the DWARF entry for the function at pc.
+	funcEntry, _, err := s.entryForPC(uint64(pc))
+	if err != nil {
+		return 0, nil
+	}
+
+	// Compute the stack frame pointer.
+	fpOffset, err := s.dwarfData.PCToSPOffset(uint64(pc))
+	if err != nil {
+		return 0, nil
+	}
+	framePointer := sp + uint64(fpOffset)
+
+	// Check each child of the function's DWARF entry to see if it is a parameter
+	// or local variable with the right name.  If so, return its address and type.
+	r := s.dwarfData.Reader()
+	r.Seek(funcEntry.Offset)
+	for {
+		varEntry, err := r.Next()
+		if err != nil {
+			break
+		}
+		if varEntry.Tag == 0 {
+			// This tag marks the end of the function's DWARF entry's children.
+			break
+		}
+
+		// Check this entry corresponds to a local variable or function parameter,
+		// that it has the correct name, and that we can get its type and location.
+		// If so, return them.
+		if varEntry.Tag != dwarf.TagFormalParameter && varEntry.Tag != dwarf.TagVariable {
+			continue
+		}
+		varName, ok := varEntry.Val(dwarf.AttrName).(string)
+		if !ok {
+			continue
+		}
+		if varName != name {
+			continue
+		}
+		varTypeOffset, ok := varEntry.Val(dwarf.AttrType).(dwarf.Offset)
+		if !ok {
+			continue
+		}
+		varType, err := s.dwarfData.Type(varTypeOffset)
+		if err != nil {
+			continue
+		}
+		locationAttribute := varEntry.Val(dwarf.AttrLocation)
+		if locationAttribute == nil {
+			continue
+		}
+		locationDescription, ok := locationAttribute.([]uint8)
+		if !ok {
+			continue
+		}
+		frameOffset, err := evalLocation(locationDescription)
+		if err != nil {
+			continue
+		}
+		return framePointer + uint64(frameOffset), varType
+	}
+
+	return 0, nil
+}
+
+// findGlobalVar finds a global variable by name, and returns its address and
+// DWARF type.  It returns a nil type on failure.
+func (s *Server) findGlobalVar(name string) (uint64, dwarf.Type) {
+	entry, err := s.dwarfData.LookupEntry(name)
+	if err != nil {
+		return 0, nil
+	}
+	loc, err := s.dwarfData.EntryLocation(entry)
+	if err != nil {
+		return 0, nil
+	}
+	ofs, err := s.dwarfData.EntryTypeOffset(entry)
+	if err != nil {
+		return 0, nil
+	}
+	typ, err := s.dwarfData.Type(ofs)
+	if err != nil {
+		return 0, nil
+	}
+	return loc, typ
+}
+
+// intFromInteger converts an untyped integer constant to an int32 or int64,
+// depending on the int size of the debugged program.
+// It returns an error on overflow, or if it can't determine the int size.
+func (e *evaluator) intFromInteger(v untInt) (interface{}, error) {
+	t, ok := e.getBaseType("int")
+	if !ok {
+		return nil, errors.New("couldn't get int size from DWARF info")
+	}
+	switch t.Common().ByteSize {
+	case 4:
+		if v.Cmp(bigIntMaxInt32) == +1 || v.Cmp(bigIntMinInt32) == -1 {
+			return nil, errors.New("constant overflows int")
+		}
+		return int32(v.Int64()), nil
+	case 8:
+		if v.Cmp(bigIntMaxInt64) == +1 || v.Cmp(bigIntMinInt64) == -1 {
+			return nil, errors.New("constant overflows int")
+		}
+		return v.Int64(), nil
+	}
+	return nil, errors.New("invalid int size in DWARF info")
+}
+
+// uint8Result constructs a result for a uint8 value.
+func (e *evaluator) uint8Result(v uint8) result {
+	t, ok := e.getBaseType("uint8")
+	if !ok {
+		e.err("couldn't construct uint8")
+	}
+	return result{t, uint8(v)}
+}
+
+// getBaseType returns the *dwarf.Type with a given name.
+// TODO: cache this.
+func (e *evaluator) getBaseType(name string) (dwarf.Type, bool) {
+	entry, err := e.server.dwarfData.LookupEntry(name)
+	if err != nil {
+		return nil, false
+	}
+	t, err := e.server.dwarfData.Type(entry.Offset)
+	if err != nil {
+		return nil, false
+	}
+	return t, true
+}
+
+// resultFrom constructs a result corresponding to a value in the program with
+// the given address and DWARF type.
+func (e *evaluator) resultFrom(a uint64, t dwarf.Type) result {
+	if a == 0 {
+		return e.err("nil pointer dereference")
+	}
+	v, err := e.server.value(t, a)
+	if err != nil {
+		return e.err(err.Error())
+	}
+	return result{t, v}
+}
+
+// convertUntyped converts x to be the same type as y, if x is untyped and the
+// conversion is possible.
+//
+// An untyped bool can be converted to a boolean type.
+// An untyped string can be converted to a string type.
+// An untyped integer, rune, float or complex value can be converted to a
+// numeric type, or to an untyped value later in that list.
+//
+// x is returned unchanged if none of these cases apply.
+func convertUntyped(x, y result) result {
+	switch a := x.v.(type) {
+	case untInt:
+		i := a.Int
+		switch y.v.(type) {
+		case int8:
+			return result{y.d, int8(i.Int64())}
+		case int16:
+			return result{y.d, int16(i.Int64())}
+		case int32:
+			return result{y.d, int32(i.Int64())}
+		case int64:
+			return result{y.d, int64(i.Int64())}
+		case uint8:
+			return result{y.d, uint8(i.Uint64())}
+		case uint16:
+			return result{y.d, uint16(i.Uint64())}
+		case uint32:
+			return result{y.d, uint32(i.Uint64())}
+		case uint64:
+			return result{y.d, uint64(i.Uint64())}
+		case float32:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, f}
+		case float64:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, f}
+		case complex64:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, complex(f, 0)}
+		case complex128:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, complex(f, 0)}
+		case untRune:
+			return result{nil, untRune{i}}
+		case untFloat:
+			return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}}
+		case untComplex:
+			return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}}
+		}
+	case untRune:
+		i := a.Int
+		switch y.v.(type) {
+		case int8:
+			return result{y.d, int8(i.Int64())}
+		case int16:
+			return result{y.d, int16(i.Int64())}
+		case int32:
+			return result{y.d, int32(i.Int64())}
+		case int64:
+			return result{y.d, int64(i.Int64())}
+		case uint8:
+			return result{y.d, uint8(i.Uint64())}
+		case uint16:
+			return result{y.d, uint16(i.Uint64())}
+		case uint32:
+			return result{y.d, uint32(i.Uint64())}
+		case uint64:
+			return result{y.d, uint64(i.Uint64())}
+		case float32:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, f}
+		case float64:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, f}
+		case complex64:
+			f, _ := new(big.Float).SetInt(i).Float32()
+			return result{y.d, complex(f, 0)}
+		case complex128:
+			f, _ := new(big.Float).SetInt(i).Float64()
+			return result{y.d, complex(f, 0)}
+		case untRune:
+			return result{nil, untRune{i}}
+		case untFloat:
+			return result{nil, untFloat{new(big.Float).SetPrec(prec).SetInt(i)}}
+		case untComplex:
+			return result{nil, untComplex{new(big.Float).SetPrec(prec).SetInt(i), new(big.Float)}}
+		}
+	case untFloat:
+		if a.IsInt() {
+			i, _ := a.Int(nil)
+			switch y.v.(type) {
+			case int8:
+				return result{y.d, int8(i.Int64())}
+			case int16:
+				return result{y.d, int16(i.Int64())}
+			case int32:
+				return result{y.d, int32(i.Int64())}
+			case int64:
+				return result{y.d, int64(i.Int64())}
+			case uint8:
+				return result{y.d, uint8(i.Uint64())}
+			case uint16:
+				return result{y.d, uint16(i.Uint64())}
+			case uint32:
+				return result{y.d, uint32(i.Uint64())}
+			case uint64:
+				return result{y.d, uint64(i.Uint64())}
+			}
+		}
+		switch y.v.(type) {
+		case float32:
+			f, _ := a.Float32()
+			return result{y.d, float32(f)}
+		case float64:
+			f, _ := a.Float64()
+			return result{y.d, float64(f)}
+		case complex64:
+			f, _ := a.Float32()
+			return result{y.d, complex(f, 0)}
+		case complex128:
+			f, _ := a.Float64()
+			return result{y.d, complex(f, 0)}
+		case untComplex:
+			return result{nil, untComplex{a.Float, new(big.Float)}}
+		}
+	case untComplex:
+		if a.i.Sign() == 0 {
+			// a is a real number.
+			if a.r.IsInt() {
+				// a is an integer.
+				i, _ := a.r.Int(nil)
+				switch y.v.(type) {
+				case int8:
+					return result{y.d, int8(i.Int64())}
+				case int16:
+					return result{y.d, int16(i.Int64())}
+				case int32:
+					return result{y.d, int32(i.Int64())}
+				case int64:
+					return result{y.d, int64(i.Int64())}
+				case uint8:
+					return result{y.d, uint8(i.Uint64())}
+				case uint16:
+					return result{y.d, uint16(i.Uint64())}
+				case uint32:
+					return result{y.d, uint32(i.Uint64())}
+				case uint64:
+					return result{y.d, uint64(i.Uint64())}
+				}
+			}
+			switch y.v.(type) {
+			case float32:
+				f, _ := a.r.Float32()
+				return result{y.d, float32(f)}
+			case float64:
+				f, _ := a.r.Float64()
+				return result{y.d, float64(f)}
+			}
+		}
+		switch y.v.(type) {
+		case complex64:
+			r, _ := a.r.Float32()
+			i, _ := a.i.Float32()
+			return result{y.d, complex(r, i)}
+		case complex128:
+			r, _ := a.r.Float64()
+			i, _ := a.i.Float64()
+			return result{y.d, complex(r, i)}
+		}
+	case bool:
+		if x.d != nil {
+			// x is a typed bool, not an untyped bool.
+			break
+		}
+		switch y.v.(type) {
+		case bool:
+			return result{y.d, bool(a)}
+		}
+	case untString:
+		switch y.v.(type) {
+		case program.String:
+			return result{y.d, program.String{Length: uint64(len(a)), String: string(a)}}
+		}
+	}
+	return x
+}
diff --git a/ogle/program/server/server.go b/ogle/program/server/server.go
index 4b44581..ecffc18 100644
--- a/ogle/program/server/server.go
+++ b/ogle/program/server/server.go
@@ -6,6 +6,8 @@
 // It is the remote end of the client implementation of the Program interface.
 package server // import "golang.org/x/debug/ogle/program/server"
 
+//go:generate sh -c "m4 -P eval.m4 > eval.go"
+
 import (
 	"bytes"
 	"fmt"
@@ -158,6 +160,8 @@
 		c.errc <- s.handleClose(req, c.resp.(*proxyrpc.CloseResponse))
 	case *proxyrpc.EvalRequest:
 		c.errc <- s.handleEval(req, c.resp.(*proxyrpc.EvalResponse))
+	case *proxyrpc.EvaluateRequest:
+		c.errc <- s.handleEvaluate(req, c.resp.(*proxyrpc.EvaluateResponse))
 	case *proxyrpc.FramesRequest:
 		c.errc <- s.handleFrames(req, c.resp.(*proxyrpc.FramesResponse))
 	case *proxyrpc.OpenRequest:
@@ -554,6 +558,15 @@
 	return nil, fmt.Errorf("bad expression syntax: %q", expr)
 }
 
+func (s *Server) Evaluate(req *proxyrpc.EvaluateRequest, resp *proxyrpc.EvaluateResponse) error {
+	return s.call(s.otherc, req, resp)
+}
+
+func (s *Server) handleEvaluate(req *proxyrpc.EvaluateRequest, resp *proxyrpc.EvaluateResponse) (err error) {
+	resp.Result, err = s.evalExpression(req.Expression, s.stoppedRegs.Rip, s.stoppedRegs.Rsp)
+	return err
+}
+
 func (s *Server) lookupSource(pc uint64) (file string, line uint64, err error) {
 	if s.dwarfData == nil {
 		return