diff options
Diffstat (limited to 'src/pkg/exp')
68 files changed, 0 insertions, 22081 deletions
diff --git a/src/pkg/exp/README b/src/pkg/exp/README deleted file mode 100644 index e602e3ac9..000000000 --- a/src/pkg/exp/README +++ /dev/null @@ -1,3 +0,0 @@ -This directory tree contains experimental packages and -unfinished code that is subject to even more change than the -rest of the Go tree. diff --git a/src/pkg/exp/datafmt/Makefile b/src/pkg/exp/datafmt/Makefile deleted file mode 100644 index aa9453897..000000000 --- a/src/pkg/exp/datafmt/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 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. - -include ../../../Make.inc - -TARG=exp/datafmt -GOFILES=\ - datafmt.go\ - parser.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/datafmt/datafmt.go b/src/pkg/exp/datafmt/datafmt.go deleted file mode 100644 index 10e4b54f9..000000000 --- a/src/pkg/exp/datafmt/datafmt.go +++ /dev/null @@ -1,731 +0,0 @@ -// Copyright 2009 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. - -/* Package datafmt implements syntax-directed, type-driven formatting - of arbitrary data structures. Formatting a data structure consists of - two phases: first, a parser reads a format specification and builds a - "compiled" format. Then, the format can be applied repeatedly to - arbitrary values. Applying a format to a value evaluates to a []byte - containing the formatted value bytes, or nil. - - A format specification is a set of package declarations and format rules: - - Format = [ Entry { ";" Entry } [ ";" ] ] . - Entry = PackageDecl | FormatRule . - - (The syntax of a format specification is presented in the same EBNF - notation as used in the Go language specification. The syntax of white - space, comments, identifiers, and string literals is the same as in Go.) - - A package declaration binds a package name (such as 'ast') to a - package import path (such as '"go/ast"'). Each package used (in - a type name, see below) must be declared once before use. - - PackageDecl = PackageName ImportPath . - PackageName = identifier . - ImportPath = string . - - A format rule binds a rule name to a format expression. A rule name - may be a type name or one of the special names 'default' or '/'. - A type name may be the name of a predeclared type (for example, 'int', - 'float32', etc.), the package-qualified name of a user-defined type - (for example, 'ast.MapType'), or an identifier indicating the structure - of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', - or 'ptr'). Each rule must have a unique name; rules can be declared in - any order. - - FormatRule = RuleName "=" Expression . - RuleName = TypeName | "default" | "/" . - TypeName = [ PackageName "." ] identifier . - - To format a value, the value's type name is used to select the format rule - (there is an override mechanism, see below). The format expression of the - selected rule specifies how the value is formatted. Each format expression, - when applied to a value, evaluates to a byte sequence or nil. - - In its most general form, a format expression is a list of alternatives, - each of which is a sequence of operands: - - Expression = [ Sequence ] { "|" [ Sequence ] } . - Sequence = Operand { Operand } . - - The formatted result produced by an expression is the result of the first - alternative sequence that evaluates to a non-nil result; if there is no - such alternative, the expression evaluates to nil. The result produced by - an operand sequence is the concatenation of the results of its operands. - If any operand in the sequence evaluates to nil, the entire sequence - evaluates to nil. - - There are five kinds of operands: - - Operand = Literal | Field | Group | Option | Repetition . - - Literals evaluate to themselves, with two substitutions. First, - %-formats expand in the manner of fmt.Printf, with the current value - passed as the parameter. Second, the current indentation (see below) - is inserted after every newline or form feed character. - - Literal = string . - - This table shows string literals applied to the value 42 and the - corresponding formatted result: - - "foo" foo - "%x" 2a - "x = %d" x = 42 - "%#x = %d" 0x2a = 42 - - A field operand is a field name optionally followed by an alternate - rule name. The field name may be an identifier or one of the special - names @ or *. - - Field = FieldName [ ":" RuleName ] . - FieldName = identifier | "@" | "*" . - - If the field name is an identifier, the current value must be a struct, - and there must be a field with that name in the struct. The same lookup - rules apply as in the Go language (for instance, the name of an anonymous - field is the unqualified type name). The field name denotes the field - value in the struct. If the field is not found, formatting is aborted - and an error message is returned. (TODO consider changing the semantics - such that if a field is not found, it evaluates to nil). - - The special name '@' denotes the current value. - - The meaning of the special name '*' depends on the type of the current - value: - - array, slice types array, slice element (inside {} only, see below) - interfaces value stored in interface - pointers value pointed to by pointer - - (Implementation restriction: channel, function and map types are not - supported due to missing reflection support). - - Fields are evaluated as follows: If the field value is nil, or an array - or slice element does not exist, the result is nil (see below for details - on array/slice elements). If the value is not nil the field value is - formatted (recursively) using the rule corresponding to its type name, - or the alternate rule name, if given. - - The following example shows a complete format specification for a - struct 'myPackage.Point'. Assume the package - - package myPackage // in directory myDir/myPackage - type Point struct { - name string; - x, y int; - } - - Applying the format specification - - myPackage "myDir/myPackage"; - int = "%d"; - hexInt = "0x%x"; - string = "---%s---"; - myPackage.Point = name "{" x ", " y:hexInt "}"; - - to the value myPackage.Point{"foo", 3, 15} results in - - ---foo---{3, 0xf} - - Finally, an operand may be a grouped, optional, or repeated expression. - A grouped expression ("group") groups a more complex expression (body) - so that it can be used in place of a single operand: - - Group = "(" [ Indentation ">>" ] Body ")" . - Indentation = Expression . - Body = Expression . - - A group body may be prefixed by an indentation expression followed by '>>'. - The indentation expression is applied to the current value like any other - expression and the result, if not nil, is appended to the current indentation - during the evaluation of the body (see also formatting state, below). - - An optional expression ("option") is enclosed in '[]' brackets. - - Option = "[" Body "]" . - - An option evaluates to its body, except that if the body evaluates to nil, - the option expression evaluates to an empty []byte. Thus an option's purpose - is to protect the expression containing the option from a nil operand. - - A repeated expression ("repetition") is enclosed in '{}' braces. - - Repetition = "{" Body [ "/" Separator ] "}" . - Separator = Expression . - - A repeated expression is evaluated as follows: The body is evaluated - repeatedly and its results are concatenated until the body evaluates - to nil. The result of the repetition is the (possibly empty) concatenation, - but it is never nil. An implicit index is supplied for the evaluation of - the body: that index is used to address elements of arrays or slices. If - the corresponding elements do not exist, the field denoting the element - evaluates to nil (which in turn may terminate the repetition). - - The body of a repetition may be followed by a '/' and a "separator" - expression. If the separator is present, it is invoked between repetitions - of the body. - - The following example shows a complete format specification for formatting - a slice of unnamed type. Applying the specification - - int = "%b"; - array = { * / ", " }; // array is the type name for an unnamed slice - - to the value '[]int{2, 3, 5, 7}' results in - - 10, 11, 101, 111 - - Default rule: If a format rule named 'default' is present, it is used for - formatting a value if no other rule was found. A common default rule is - - default = "%v" - - to provide default formatting for basic types without having to specify - a specific rule for each basic type. - - Global separator rule: If a format rule named '/' is present, it is - invoked with the current value between literals. If the separator - expression evaluates to nil, it is ignored. - - For instance, a global separator rule may be used to punctuate a sequence - of values with commas. The rules: - - default = "%v"; - / = ", "; - - will format an argument list by printing each one in its default format, - separated by a comma and a space. -*/ -package datafmt - -import ( - "bytes" - "fmt" - "go/token" - "io" - "os" - "reflect" - "runtime" -) - - -// ---------------------------------------------------------------------------- -// Format representation - -// Custom formatters implement the Formatter function type. -// A formatter is invoked with the current formatting state, the -// value to format, and the rule name under which the formatter -// was installed (the same formatter function may be installed -// under different names). The formatter may access the current state -// to guide formatting and use State.Write to append to the state's -// output. -// -// A formatter must return a boolean value indicating if it evaluated -// to a non-nil value (true), or a nil value (false). -// -type Formatter func(state *State, value interface{}, ruleName string) bool - - -// A FormatterMap is a set of custom formatters. -// It maps a rule name to a formatter function. -// -type FormatterMap map[string]Formatter - - -// A parsed format expression is built from the following nodes. -// -type ( - expr interface{} - - alternatives []expr // x | y | z - - sequence []expr // x y z - - literal [][]byte // a list of string segments, possibly starting with '%' - - field struct { - fieldName string // including "@", "*" - ruleName string // "" if no rule name specified - } - - group struct { - indent, body expr // (indent >> body) - } - - option struct { - body expr // [body] - } - - repetition struct { - body, separator expr // {body / separator} - } - - custom struct { - ruleName string - fun Formatter - } -) - - -// A Format is the result of parsing a format specification. -// The format may be applied repeatedly to format values. -// -type Format map[string]expr - - -// ---------------------------------------------------------------------------- -// Formatting - -// An application-specific environment may be provided to Format.Apply; -// the environment is available inside custom formatters via State.Env(). -// Environments must implement copying; the Copy method must return an -// complete copy of the receiver. This is necessary so that the formatter -// can save and restore an environment (in case of an absent expression). -// -// If the Environment doesn't change during formatting (this is under -// control of the custom formatters), the Copy function can simply return -// the receiver, and thus can be very light-weight. -// -type Environment interface { - Copy() Environment -} - - -// State represents the current formatting state. -// It is provided as argument to custom formatters. -// -type State struct { - fmt Format // format in use - env Environment // user-supplied environment - errors chan os.Error // not chan *Error (errors <- nil would be wrong!) - hasOutput bool // true after the first literal has been written - indent bytes.Buffer // current indentation - output bytes.Buffer // format output - linePos token.Position // position of line beginning (Column == 0) - default_ expr // possibly nil - separator expr // possibly nil -} - - -func newState(fmt Format, env Environment, errors chan os.Error) *State { - s := new(State) - s.fmt = fmt - s.env = env - s.errors = errors - s.linePos = token.Position{Line: 1} - - // if we have a default rule, cache it's expression for fast access - if x, found := fmt["default"]; found { - s.default_ = x - } - - // if we have a global separator rule, cache it's expression for fast access - if x, found := fmt["/"]; found { - s.separator = x - } - - return s -} - - -// Env returns the environment passed to Format.Apply. -func (s *State) Env() interface{} { return s.env } - - -// LinePos returns the position of the current line beginning -// in the state's output buffer. Line numbers start at 1. -// -func (s *State) LinePos() token.Position { return s.linePos } - - -// Pos returns the position of the next byte to be written to the -// output buffer. Line numbers start at 1. -// -func (s *State) Pos() token.Position { - offs := s.output.Len() - return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} -} - - -// Write writes data to the output buffer, inserting the indentation -// string after each newline or form feed character. It cannot return an error. -// -func (s *State) Write(data []byte) (int, os.Error) { - n := 0 - i0 := 0 - for i, ch := range data { - if ch == '\n' || ch == '\f' { - // write text segment and indentation - n1, _ := s.output.Write(data[i0 : i+1]) - n2, _ := s.output.Write(s.indent.Bytes()) - n += n1 + n2 - i0 = i + 1 - s.linePos.Offset = s.output.Len() - s.linePos.Line++ - } - } - n3, _ := s.output.Write(data[i0:]) - return n + n3, nil -} - - -type checkpoint struct { - env Environment - hasOutput bool - outputLen int - linePos token.Position -} - - -func (s *State) save() checkpoint { - saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} - if s.env != nil { - saved.env = s.env.Copy() - } - return saved -} - - -func (s *State) restore(m checkpoint) { - s.env = m.env - s.output.Truncate(m.outputLen) -} - - -func (s *State) error(msg string) { - s.errors <- os.NewError(msg) - runtime.Goexit() -} - - -// TODO At the moment, unnamed types are simply mapped to the default -// names below. For instance, all unnamed arrays are mapped to -// 'array' which is not really sufficient. Eventually one may want -// to be able to specify rules for say an unnamed slice of T. -// - -func typename(typ reflect.Type) string { - switch typ.Kind() { - case reflect.Array: - return "array" - case reflect.Slice: - return "array" - case reflect.Chan: - return "chan" - case reflect.Func: - return "func" - case reflect.Interface: - return "interface" - case reflect.Map: - return "map" - case reflect.Ptr: - return "ptr" - } - return typ.String() -} - -func (s *State) getFormat(name string) expr { - if fexpr, found := s.fmt[name]; found { - return fexpr - } - - if s.default_ != nil { - return s.default_ - } - - s.error(fmt.Sprintf("no format rule for type: '%s'", name)) - return nil -} - - -// eval applies a format expression fexpr to a value. If the expression -// evaluates internally to a non-nil []byte, that slice is appended to -// the state's output buffer and eval returns true. Otherwise, eval -// returns false and the state remains unchanged. -// -func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { - // an empty format expression always evaluates - // to a non-nil (but empty) []byte - if fexpr == nil { - return true - } - - switch t := fexpr.(type) { - case alternatives: - // append the result of the first alternative that evaluates to - // a non-nil []byte to the state's output - mark := s.save() - for _, x := range t { - if s.eval(x, value, index) { - return true - } - s.restore(mark) - } - return false - - case sequence: - // append the result of all operands to the state's output - // unless a nil result is encountered - mark := s.save() - for _, x := range t { - if !s.eval(x, value, index) { - s.restore(mark) - return false - } - } - return true - - case literal: - // write separator, if any - if s.hasOutput { - // not the first literal - if s.separator != nil { - sep := s.separator // save current separator - s.separator = nil // and disable it (avoid recursion) - mark := s.save() - if !s.eval(sep, value, index) { - s.restore(mark) - } - s.separator = sep // enable it again - } - } - s.hasOutput = true - // write literal segments - for _, lit := range t { - if len(lit) > 1 && lit[0] == '%' { - // segment contains a %-format at the beginning - if lit[1] == '%' { - // "%%" is printed as a single "%" - s.Write(lit[1:]) - } else { - // use s instead of s.output to get indentation right - fmt.Fprintf(s, string(lit), value.Interface()) - } - } else { - // segment contains no %-formats - s.Write(lit) - } - } - return true // a literal never evaluates to nil - - case *field: - // determine field value - switch t.fieldName { - case "@": - // field value is current value - - case "*": - // indirection: operation is type-specific - switch v := value; v.Kind() { - case reflect.Array: - if v.Len() <= index { - return false - } - value = v.Index(index) - - case reflect.Slice: - if v.IsNil() || v.Len() <= index { - return false - } - value = v.Index(index) - - case reflect.Map: - s.error("reflection support for maps incomplete") - - case reflect.Ptr: - if v.IsNil() { - return false - } - value = v.Elem() - - case reflect.Interface: - if v.IsNil() { - return false - } - value = v.Elem() - - case reflect.Chan: - s.error("reflection support for chans incomplete") - - case reflect.Func: - s.error("reflection support for funcs incomplete") - - default: - s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) - } - - default: - // value is value of named field - var field reflect.Value - if sval := value; sval.Kind() == reflect.Struct { - field = sval.FieldByName(t.fieldName) - if !field.IsValid() { - // TODO consider just returning false in this case - s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) - } - } - value = field - } - - // determine rule - ruleName := t.ruleName - if ruleName == "" { - // no alternate rule name, value type determines rule - ruleName = typename(value.Type()) - } - fexpr = s.getFormat(ruleName) - - mark := s.save() - if !s.eval(fexpr, value, index) { - s.restore(mark) - return false - } - return true - - case *group: - // remember current indentation - indentLen := s.indent.Len() - - // update current indentation - mark := s.save() - s.eval(t.indent, value, index) - // if the indentation evaluates to nil, the state's output buffer - // didn't change - either way it's ok to append the difference to - // the current indentation - s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) - s.restore(mark) - - // format group body - mark = s.save() - b := true - if !s.eval(t.body, value, index) { - s.restore(mark) - b = false - } - - // reset indentation - s.indent.Truncate(indentLen) - return b - - case *option: - // evaluate the body and append the result to the state's output - // buffer unless the result is nil - mark := s.save() - if !s.eval(t.body, value, 0) { // TODO is 0 index correct? - s.restore(mark) - } - return true // an option never evaluates to nil - - case *repetition: - // evaluate the body and append the result to the state's output - // buffer until a result is nil - for i := 0; ; i++ { - mark := s.save() - // write separator, if any - if i > 0 && t.separator != nil { - // nil result from separator is ignored - mark := s.save() - if !s.eval(t.separator, value, i) { - s.restore(mark) - } - } - if !s.eval(t.body, value, i) { - s.restore(mark) - break - } - } - return true // a repetition never evaluates to nil - - case *custom: - // invoke the custom formatter to obtain the result - mark := s.save() - if !t.fun(s, value.Interface(), t.ruleName) { - s.restore(mark) - return false - } - return true - } - - panic("unreachable") - return false -} - - -// Eval formats each argument according to the format -// f and returns the resulting []byte and os.Error. If -// an error occurred, the []byte contains the partially -// formatted result. An environment env may be passed -// in which is available in custom formatters through -// the state parameter. -// -func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { - if f == nil { - return nil, os.NewError("format is nil") - } - - errors := make(chan os.Error) - s := newState(f, env, errors) - - go func() { - for _, v := range args { - fld := reflect.ValueOf(v) - if !fld.IsValid() { - errors <- os.NewError("nil argument") - return - } - mark := s.save() - if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? - s.restore(mark) - } - } - errors <- nil // no errors - }() - - err := <-errors - return s.output.Bytes(), err -} - - -// ---------------------------------------------------------------------------- -// Convenience functions - -// Fprint formats each argument according to the format f -// and writes to w. The result is the total number of bytes -// written and an os.Error, if any. -// -func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { - data, err := f.Eval(env, args...) - if err != nil { - // TODO should we print partial result in case of error? - return 0, err - } - return w.Write(data) -} - - -// Print formats each argument according to the format f -// and writes to standard output. The result is the total -// number of bytes written and an os.Error, if any. -// -func (f Format) Print(args ...interface{}) (int, os.Error) { - return f.Fprint(os.Stdout, nil, args...) -} - - -// Sprint formats each argument according to the format f -// and returns the resulting string. If an error occurs -// during formatting, the result string contains the -// partially formatted result followed by an error message. -// -func (f Format) Sprint(args ...interface{}) string { - var buf bytes.Buffer - _, err := f.Fprint(&buf, nil, args...) - if err != nil { - var i interface{} = args - fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) - } - return buf.String() -} diff --git a/src/pkg/exp/datafmt/datafmt_test.go b/src/pkg/exp/datafmt/datafmt_test.go deleted file mode 100644 index d7c70b21d..000000000 --- a/src/pkg/exp/datafmt/datafmt_test.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2009 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. - -package datafmt - -import ( - "fmt" - "testing" - "go/token" -) - - -var fset = token.NewFileSet() - - -func parse(t *testing.T, form string, fmap FormatterMap) Format { - f, err := Parse(fset, "", []byte(form), fmap) - if err != nil { - t.Errorf("Parse(%s): %v", form, err) - return nil - } - return f -} - - -func verify(t *testing.T, f Format, expected string, args ...interface{}) { - if f == nil { - return // allow other tests to run - } - result := f.Sprint(args...) - if result != expected { - t.Errorf( - "result : `%s`\nexpected: `%s`\n\n", - result, expected) - } -} - - -func formatter(s *State, value interface{}, rule_name string) bool { - switch rule_name { - case "/": - fmt.Fprintf(s, "%d %d %d", s.Pos().Line, s.LinePos().Column, s.Pos().Column) - return true - case "blank": - s.Write([]byte{' '}) - return true - case "int": - if value.(int)&1 == 0 { - fmt.Fprint(s, "even ") - } else { - fmt.Fprint(s, "odd ") - } - return true - case "nil": - return false - case "testing.T": - s.Write([]byte("testing.T")) - return true - } - panic("unreachable") - return false -} - - -func TestCustomFormatters(t *testing.T) { - fmap0 := FormatterMap{"/": formatter} - fmap1 := FormatterMap{"int": formatter, "blank": formatter, "nil": formatter} - fmap2 := FormatterMap{"testing.T": formatter} - - f := parse(t, `int=`, fmap0) - verify(t, f, ``, 1, 2, 3) - - f = parse(t, `int="#"`, nil) - verify(t, f, `###`, 1, 2, 3) - - f = parse(t, `int="#";string="%s"`, fmap0) - verify(t, f, "#1 0 1#1 0 7#1 0 13\n2 0 0foo2 0 8\n", 1, 2, 3, "\n", "foo", "\n") - - f = parse(t, ``, fmap1) - verify(t, f, `even odd even odd `, 0, 1, 2, 3) - - f = parse(t, `/ =@:blank; float64="#"`, fmap1) - verify(t, f, `# # #`, 0.0, 1.0, 2.0) - - f = parse(t, `float64=@:nil`, fmap1) - verify(t, f, ``, 0.0, 1.0, 2.0) - - f = parse(t, `testing "testing"; ptr=*`, fmap2) - verify(t, f, `testing.T`, t) - - // TODO needs more tests -} - - -// ---------------------------------------------------------------------------- -// Formatting of basic and simple composite types - -func check(t *testing.T, form, expected string, args ...interface{}) { - f := parse(t, form, nil) - if f == nil { - return // allow other tests to run - } - result := f.Sprint(args...) - if result != expected { - t.Errorf( - "format : %s\nresult : `%s`\nexpected: `%s`\n\n", - form, result, expected) - } -} - - -func TestBasicTypes(t *testing.T) { - check(t, ``, ``) - check(t, `bool=":%v"`, `:true:false`, true, false) - check(t, `int="%b %d %o 0x%x"`, `101010 42 52 0x2a`, 42) - - check(t, `int="%"`, `%`, 42) - check(t, `int="%%"`, `%`, 42) - check(t, `int="**%%**"`, `**%**`, 42) - check(t, `int="%%%%%%"`, `%%%`, 42) - check(t, `int="%%%d%%"`, `%42%`, 42) - - const i = -42 - const is = `-42` - check(t, `int ="%d"`, is, i) - check(t, `int8 ="%d"`, is, int8(i)) - check(t, `int16="%d"`, is, int16(i)) - check(t, `int32="%d"`, is, int32(i)) - check(t, `int64="%d"`, is, int64(i)) - - const u = 42 - const us = `42` - check(t, `uint ="%d"`, us, uint(u)) - check(t, `uint8 ="%d"`, us, uint8(u)) - check(t, `uint16="%d"`, us, uint16(u)) - check(t, `uint32="%d"`, us, uint32(u)) - check(t, `uint64="%d"`, us, uint64(u)) - - const f = 3.141592 - const fs = `3.141592` - check(t, `float64="%g"`, fs, f) - check(t, `float32="%g"`, fs, float32(f)) - check(t, `float64="%g"`, fs, float64(f)) -} - - -func TestArrayTypes(t *testing.T) { - var a0 [10]int - check(t, `array="array";`, `array`, a0) - - a1 := [...]int{1, 2, 3} - check(t, `array="array";`, `array`, a1) - check(t, `array={*}; int="%d";`, `123`, a1) - check(t, `array={* / ", "}; int="%d";`, `1, 2, 3`, a1) - check(t, `array={* / *}; int="%d";`, `12233`, a1) - - a2 := []interface{}{42, "foo", 3.14} - check(t, `array={* / ", "}; interface=*; string="bar"; default="%v";`, `42, bar, 3.14`, a2) -} - - -func TestChanTypes(t *testing.T) { - var c0 chan int - check(t, `chan="chan"`, `chan`, c0) - - c1 := make(chan int) - go func() { c1 <- 42 }() - check(t, `chan="chan"`, `chan`, c1) - // check(t, `chan=*`, `42`, c1); // reflection support for chans incomplete -} - - -func TestFuncTypes(t *testing.T) { - var f0 func() int - check(t, `func="func"`, `func`, f0) - - f1 := func() int { return 42 } - check(t, `func="func"`, `func`, f1) - // check(t, `func=*`, `42`, f1); // reflection support for funcs incomplete -} - - -func TestMapTypes(t *testing.T) { - var m0 map[string]int - check(t, `map="map"`, `map`, m0) - - m1 := map[string]int{} - check(t, `map="map"`, `map`, m1) - // check(t, `map=*`, ``, m1); // reflection support for maps incomplete -} - - -func TestPointerTypes(t *testing.T) { - var p0 *int - check(t, `ptr="ptr"`, `ptr`, p0) - check(t, `ptr=*`, ``, p0) - check(t, `ptr=*|"nil"`, `nil`, p0) - - x := 99991 - p1 := &x - check(t, `ptr="ptr"`, `ptr`, p1) - check(t, `ptr=*; int="%d"`, `99991`, p1) -} - - -func TestDefaultRule(t *testing.T) { - check(t, `default="%v"`, `42foo3.14`, 42, "foo", 3.14) - check(t, `default="%v"; int="%x"`, `abcdef`, 10, 11, 12, 13, 14, 15) - check(t, `default="%v"; int="%x"`, `ab**ef`, 10, 11, "**", 14, 15) - check(t, `default="%x"; int=@:default`, `abcdef`, 10, 11, 12, 13, 14, 15) -} - - -func TestGlobalSeparatorRule(t *testing.T) { - check(t, `int="%d"; / ="-"`, `1-2-3-4`, 1, 2, 3, 4) - check(t, `int="%x%x"; / ="*"`, `aa*aa`, 10, 10) -} - - -// ---------------------------------------------------------------------------- -// Formatting of a struct - -type T1 struct { - a int -} - -const F1 = `datafmt "datafmt";` + - `int = "%d";` + - `datafmt.T1 = "<" a ">";` - -func TestStruct1(t *testing.T) { check(t, F1, "<42>", T1{42}) } - - -// ---------------------------------------------------------------------------- -// Formatting of a struct with an optional field (ptr) - -type T2 struct { - s string - p *T1 -} - -const F2a = F1 + - `string = "%s";` + - `ptr = *;` + - `datafmt.T2 = s ["-" p "-"];` - -const F2b = F1 + - `string = "%s";` + - `ptr = *;` + - `datafmt.T2 = s ("-" p "-" | "empty");` - -func TestStruct2(t *testing.T) { - check(t, F2a, "foo", T2{"foo", nil}) - check(t, F2a, "bar-<17>-", T2{"bar", &T1{17}}) - check(t, F2b, "fooempty", T2{"foo", nil}) -} - - -// ---------------------------------------------------------------------------- -// Formatting of a struct with a repetitive field (slice) - -type T3 struct { - s string - a []int -} - -const F3a = `datafmt "datafmt";` + - `default = "%v";` + - `array = *;` + - `datafmt.T3 = s {" " a a / ","};` - -const F3b = `datafmt "datafmt";` + - `int = "%d";` + - `string = "%s";` + - `array = *;` + - `nil = ;` + - `empty = *:nil;` + - `datafmt.T3 = s [a:empty ": " {a / "-"}]` - -func TestStruct3(t *testing.T) { - check(t, F3a, "foo", T3{"foo", nil}) - check(t, F3a, "foo 00, 11, 22", T3{"foo", []int{0, 1, 2}}) - check(t, F3b, "bar", T3{"bar", nil}) - check(t, F3b, "bal: 2-3-5", T3{"bal", []int{2, 3, 5}}) -} - - -// ---------------------------------------------------------------------------- -// Formatting of a struct with alternative field - -type T4 struct { - x *int - a []int -} - -const F4a = `datafmt "datafmt";` + - `int = "%d";` + - `ptr = *;` + - `array = *;` + - `nil = ;` + - `empty = *:nil;` + - `datafmt.T4 = "<" (x:empty x | "-") ">" ` - -const F4b = `datafmt "datafmt";` + - `int = "%d";` + - `ptr = *;` + - `array = *;` + - `nil = ;` + - `empty = *:nil;` + - `datafmt.T4 = "<" (a:empty {a / ", "} | "-") ">" ` - -func TestStruct4(t *testing.T) { - x := 7 - check(t, F4a, "<->", T4{nil, nil}) - check(t, F4a, "<7>", T4{&x, nil}) - check(t, F4b, "<->", T4{nil, nil}) - check(t, F4b, "<2, 3, 7>", T4{nil, []int{2, 3, 7}}) -} - - -// ---------------------------------------------------------------------------- -// Formatting a struct (documentation example) - -type Point struct { - name string - x, y int -} - -const FPoint = `datafmt "datafmt";` + - `int = "%d";` + - `hexInt = "0x%x";` + - `string = "---%s---";` + - `datafmt.Point = name "{" x ", " y:hexInt "}";` - -func TestStructPoint(t *testing.T) { - p := Point{"foo", 3, 15} - check(t, FPoint, "---foo---{3, 0xf}", p) -} - - -// ---------------------------------------------------------------------------- -// Formatting a slice (documentation example) - -const FSlice = `int = "%b";` + - `array = { * / ", " }` - -func TestSlice(t *testing.T) { check(t, FSlice, "10, 11, 101, 111", []int{2, 3, 5, 7}) } - - -// TODO add more tests diff --git a/src/pkg/exp/datafmt/parser.go b/src/pkg/exp/datafmt/parser.go deleted file mode 100644 index 7dedb531a..000000000 --- a/src/pkg/exp/datafmt/parser.go +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright 2009 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. - -package datafmt - -import ( - "container/vector" - "go/scanner" - "go/token" - "os" - "strconv" - "strings" -) - -// ---------------------------------------------------------------------------- -// Parsing - -type parser struct { - scanner.ErrorVector - scanner scanner.Scanner - file *token.File - pos token.Pos // token position - tok token.Token // one token look-ahead - lit string // token literal - - packs map[string]string // PackageName -> ImportPath - rules map[string]expr // RuleName -> Expression -} - - -func (p *parser) next() { - p.pos, p.tok, p.lit = p.scanner.Scan() - switch p.tok { - case token.CHAN, token.FUNC, token.INTERFACE, token.MAP, token.STRUCT: - // Go keywords for composite types are type names - // returned by reflect. Accept them as identifiers. - p.tok = token.IDENT // p.lit is already set correctly - } -} - - -func (p *parser) init(fset *token.FileSet, filename string, src []byte) { - p.ErrorVector.Reset() - p.file = fset.AddFile(filename, fset.Base(), len(src)) - p.scanner.Init(p.file, src, p, scanner.AllowIllegalChars) // return '@' as token.ILLEGAL w/o error message - p.next() // initializes pos, tok, lit - p.packs = make(map[string]string) - p.rules = make(map[string]expr) -} - - -func (p *parser) error(pos token.Pos, msg string) { - p.Error(p.file.Position(pos), msg) -} - - -func (p *parser) errorExpected(pos token.Pos, msg string) { - msg = "expected " + msg - if pos == p.pos { - // the error happened at the current position; - // make the error message more specific - msg += ", found '" + p.tok.String() + "'" - if p.tok.IsLiteral() { - msg += " " + p.lit - } - } - p.error(pos, msg) -} - - -func (p *parser) expect(tok token.Token) token.Pos { - pos := p.pos - if p.tok != tok { - p.errorExpected(pos, "'"+tok.String()+"'") - } - p.next() // make progress in any case - return pos -} - - -func (p *parser) parseIdentifier() string { - name := p.lit - p.expect(token.IDENT) - return name -} - - -func (p *parser) parseTypeName() (string, bool) { - pos := p.pos - name, isIdent := p.parseIdentifier(), true - if p.tok == token.PERIOD { - // got a package name, lookup package - if importPath, found := p.packs[name]; found { - name = importPath - } else { - p.error(pos, "package not declared: "+name) - } - p.next() - name, isIdent = name+"."+p.parseIdentifier(), false - } - return name, isIdent -} - - -// Parses a rule name and returns it. If the rule name is -// a package-qualified type name, the package name is resolved. -// The 2nd result value is true iff the rule name consists of a -// single identifier only (and thus could be a package name). -// -func (p *parser) parseRuleName() (string, bool) { - name, isIdent := "", false - switch p.tok { - case token.IDENT: - name, isIdent = p.parseTypeName() - case token.DEFAULT: - name = "default" - p.next() - case token.QUO: - name = "/" - p.next() - default: - p.errorExpected(p.pos, "rule name") - p.next() // make progress in any case - } - return name, isIdent -} - - -func (p *parser) parseString() string { - s := "" - if p.tok == token.STRING { - s, _ = strconv.Unquote(p.lit) - // Unquote may fail with an error, but only if the scanner found - // an illegal string in the first place. In this case the error - // has already been reported. - p.next() - return s - } else { - p.expect(token.STRING) - } - return s -} - - -func (p *parser) parseLiteral() literal { - s := []byte(p.parseString()) - - // A string literal may contain %-format specifiers. To simplify - // and speed up printing of the literal, split it into segments - // that start with "%" possibly followed by a last segment that - // starts with some other character. - var list vector.Vector - i0 := 0 - for i := 0; i < len(s); i++ { - if s[i] == '%' && i+1 < len(s) { - // the next segment starts with a % format - if i0 < i { - // the current segment is not empty, split it off - list.Push(s[i0:i]) - i0 = i - } - i++ // skip %; let loop skip over char after % - } - } - // the final segment may start with any character - // (it is empty iff the string is empty) - list.Push(s[i0:]) - - // convert list into a literal - lit := make(literal, list.Len()) - for i := 0; i < list.Len(); i++ { - lit[i] = list.At(i).([]byte) - } - - return lit -} - - -func (p *parser) parseField() expr { - var fname string - switch p.tok { - case token.ILLEGAL: - if p.lit != "@" { - return nil - } - fname = "@" - p.next() - case token.MUL: - fname = "*" - p.next() - case token.IDENT: - fname = p.parseIdentifier() - default: - return nil - } - - var ruleName string - if p.tok == token.COLON { - p.next() - ruleName, _ = p.parseRuleName() - } - - return &field{fname, ruleName} -} - - -func (p *parser) parseOperand() (x expr) { - switch p.tok { - case token.STRING: - x = p.parseLiteral() - - case token.LPAREN: - p.next() - x = p.parseExpression() - if p.tok == token.SHR { - p.next() - x = &group{x, p.parseExpression()} - } - p.expect(token.RPAREN) - - case token.LBRACK: - p.next() - x = &option{p.parseExpression()} - p.expect(token.RBRACK) - - case token.LBRACE: - p.next() - x = p.parseExpression() - var div expr - if p.tok == token.QUO { - p.next() - div = p.parseExpression() - } - x = &repetition{x, div} - p.expect(token.RBRACE) - - default: - x = p.parseField() // may be nil - } - - return x -} - - -func (p *parser) parseSequence() expr { - var list vector.Vector - - for x := p.parseOperand(); x != nil; x = p.parseOperand() { - list.Push(x) - } - - // no need for a sequence if list.Len() < 2 - switch list.Len() { - case 0: - return nil - case 1: - return list.At(0).(expr) - } - - // convert list into a sequence - seq := make(sequence, list.Len()) - for i := 0; i < list.Len(); i++ { - seq[i] = list.At(i).(expr) - } - return seq -} - - -func (p *parser) parseExpression() expr { - var list vector.Vector - - for { - x := p.parseSequence() - if x != nil { - list.Push(x) - } - if p.tok != token.OR { - break - } - p.next() - } - - // no need for an alternatives if list.Len() < 2 - switch list.Len() { - case 0: - return nil - case 1: - return list.At(0).(expr) - } - - // convert list into a alternatives - alt := make(alternatives, list.Len()) - for i := 0; i < list.Len(); i++ { - alt[i] = list.At(i).(expr) - } - return alt -} - - -func (p *parser) parseFormat() { - for p.tok != token.EOF { - pos := p.pos - - name, isIdent := p.parseRuleName() - switch p.tok { - case token.STRING: - // package declaration - importPath := p.parseString() - - // add package declaration - if !isIdent { - p.error(pos, "illegal package name: "+name) - } else if _, found := p.packs[name]; !found { - p.packs[name] = importPath - } else { - p.error(pos, "package already declared: "+name) - } - - case token.ASSIGN: - // format rule - p.next() - x := p.parseExpression() - - // add rule - if _, found := p.rules[name]; !found { - p.rules[name] = x - } else { - p.error(pos, "format rule already declared: "+name) - } - - default: - p.errorExpected(p.pos, "package declaration or format rule") - p.next() // make progress in any case - } - - if p.tok == token.SEMICOLON { - p.next() - } else { - break - } - } - p.expect(token.EOF) -} - - -func remap(p *parser, name string) string { - i := strings.Index(name, ".") - if i >= 0 { - packageName, suffix := name[0:i], name[i:] - // lookup package - if importPath, found := p.packs[packageName]; found { - name = importPath + suffix - } else { - var invalidPos token.Position - p.Error(invalidPos, "package not declared: "+packageName) - } - } - return name -} - - -// Parse parses a set of format productions from source src. Custom -// formatters may be provided via a map of formatter functions. If -// there are no errors, the result is a Format and the error is nil. -// Otherwise the format is nil and a non-empty ErrorList is returned. -// -func Parse(fset *token.FileSet, filename string, src []byte, fmap FormatterMap) (Format, os.Error) { - // parse source - var p parser - p.init(fset, filename, src) - p.parseFormat() - - // add custom formatters, if any - for name, form := range fmap { - name = remap(&p, name) - if _, found := p.rules[name]; !found { - p.rules[name] = &custom{name, form} - } else { - var invalidPos token.Position - p.Error(invalidPos, "formatter already declared: "+name) - } - } - - return p.rules, p.GetError(scanner.NoMultiples) -} diff --git a/src/pkg/exp/eval/Makefile b/src/pkg/exp/eval/Makefile deleted file mode 100644 index 872316cad..000000000 --- a/src/pkg/exp/eval/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2009 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. - -include ../../../Make.inc - -TARG=exp/eval -GOFILES=\ - abort.go\ - bridge.go\ - compiler.go\ - expr.go\ - expr1.go\ - func.go\ - scope.go\ - stmt.go\ - type.go\ - typec.go\ - value.go\ - world.go\ - -include ../../../Make.pkg - -main.$O: main.go $(pkgdir)/$(TARG).a - $(GC) $< - -eval: main.$O - $(LD) -o $@ $< - -gen.$O: gen.go - $(GC) $< - -generate: gen.$O - $(LD) -o $@ $<;\ - ./generate > expr1.go;\ - gofmt -w expr1.go - diff --git a/src/pkg/exp/eval/abort.go b/src/pkg/exp/eval/abort.go deleted file mode 100644 index 22e17cec4..000000000 --- a/src/pkg/exp/eval/abort.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "fmt" - "os" - "runtime" -) - -// Abort aborts the thread's current computation, -// causing the innermost Try to return err. -func (t *Thread) Abort(err os.Error) { - if t.abort == nil { - panic("abort: " + err.String()) - } - t.abort <- err - runtime.Goexit() -} - -// Try executes a computation; if the computation -// Aborts, Try returns the error passed to abort. -func (t *Thread) Try(f func(t *Thread)) os.Error { - oc := t.abort - c := make(chan os.Error) - t.abort = c - go func() { - f(t) - c <- nil - }() - err := <-c - t.abort = oc - return err -} - -type DivByZeroError struct{} - -func (DivByZeroError) String() string { return "divide by zero" } - -type NilPointerError struct{} - -func (NilPointerError) String() string { return "nil pointer dereference" } - -type IndexError struct { - Idx, Len int64 -} - -func (e IndexError) String() string { - if e.Idx < 0 { - return fmt.Sprintf("negative index: %d", e.Idx) - } - return fmt.Sprintf("index %d exceeds length %d", e.Idx, e.Len) -} - -type SliceError struct { - Lo, Hi, Cap int64 -} - -func (e SliceError) String() string { - return fmt.Sprintf("slice [%d:%d]; cap %d", e.Lo, e.Hi, e.Cap) -} - -type KeyError struct { - Key interface{} -} - -func (e KeyError) String() string { return fmt.Sprintf("key '%v' not found in map", e.Key) } - -type NegativeLengthError struct { - Len int64 -} - -func (e NegativeLengthError) String() string { - return fmt.Sprintf("negative length: %d", e.Len) -} - -type NegativeCapacityError struct { - Len int64 -} - -func (e NegativeCapacityError) String() string { - return fmt.Sprintf("negative capacity: %d", e.Len) -} diff --git a/src/pkg/exp/eval/bridge.go b/src/pkg/exp/eval/bridge.go deleted file mode 100644 index f31d9ab9b..000000000 --- a/src/pkg/exp/eval/bridge.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "log" - "go/token" - "reflect" -) - -/* - * Type bridging - */ - -var ( - evalTypes = make(map[reflect.Type]Type) - nativeTypes = make(map[Type]reflect.Type) -) - -// TypeFromNative converts a regular Go type into a the corresponding -// interpreter Type. -func TypeFromNative(t reflect.Type) Type { - if et, ok := evalTypes[t]; ok { - return et - } - - var nt *NamedType - if t.Name() != "" { - name := t.PkgPath() + "·" + t.Name() - nt = &NamedType{token.NoPos, name, nil, true, make(map[string]Method)} - evalTypes[t] = nt - } - - var et Type - switch t.Kind() { - case reflect.Bool: - et = BoolType - - case reflect.Float32: - et = Float32Type - case reflect.Float64: - et = Float64Type - - case reflect.Int16: - et = Int16Type - case reflect.Int32: - et = Int32Type - case reflect.Int64: - et = Int64Type - case reflect.Int8: - et = Int8Type - case reflect.Int: - et = IntType - - case reflect.Uint16: - et = Uint16Type - case reflect.Uint32: - et = Uint32Type - case reflect.Uint64: - et = Uint64Type - case reflect.Uint8: - et = Uint8Type - case reflect.Uint: - et = UintType - case reflect.Uintptr: - et = UintptrType - - case reflect.String: - et = StringType - case reflect.Array: - et = NewArrayType(int64(t.Len()), TypeFromNative(t.Elem())) - case reflect.Chan: - log.Panicf("%T not implemented", t) - case reflect.Func: - nin := t.NumIn() - // Variadic functions have DotDotDotType at the end - variadic := t.IsVariadic() - if variadic { - nin-- - } - in := make([]Type, nin) - for i := range in { - in[i] = TypeFromNative(t.In(i)) - } - out := make([]Type, t.NumOut()) - for i := range out { - out[i] = TypeFromNative(t.Out(i)) - } - et = NewFuncType(in, variadic, out) - case reflect.Interface: - log.Panicf("%T not implemented", t) - case reflect.Map: - log.Panicf("%T not implemented", t) - case reflect.Ptr: - et = NewPtrType(TypeFromNative(t.Elem())) - case reflect.Slice: - et = NewSliceType(TypeFromNative(t.Elem())) - case reflect.Struct: - n := t.NumField() - fields := make([]StructField, n) - for i := 0; i < n; i++ { - sf := t.Field(i) - // TODO(austin) What to do about private fields? - fields[i].Name = sf.Name - fields[i].Type = TypeFromNative(sf.Type) - fields[i].Anonymous = sf.Anonymous - } - et = NewStructType(fields) - case reflect.UnsafePointer: - log.Panicf("%T not implemented", t) - default: - log.Panicf("unexpected reflect.Type: %T", t) - } - - if nt != nil { - if _, ok := et.(*NamedType); !ok { - nt.Complete(et) - et = nt - } - } - - nativeTypes[et] = t - evalTypes[t] = et - - return et -} - -// TypeOfNative returns the interpreter Type of a regular Go value. -func TypeOfNative(v interface{}) Type { return TypeFromNative(reflect.TypeOf(v)) } - -/* - * Function bridging - */ - -type nativeFunc struct { - fn func(*Thread, []Value, []Value) - in, out int -} - -func (f *nativeFunc) NewFrame() *Frame { - vars := make([]Value, f.in+f.out) - return &Frame{nil, vars} -} - -func (f *nativeFunc) Call(t *Thread) { f.fn(t, t.f.Vars[0:f.in], t.f.Vars[f.in:f.in+f.out]) } - -// FuncFromNative creates an interpreter function from a native -// function that takes its in and out arguments as slices of -// interpreter Value's. While somewhat inconvenient, this avoids -// value marshalling. -func FuncFromNative(fn func(*Thread, []Value, []Value), t *FuncType) FuncValue { - return &funcV{&nativeFunc{fn, len(t.In), len(t.Out)}} -} - -// FuncFromNativeTyped is like FuncFromNative, but constructs the -// function type from a function pointer using reflection. Typically, -// the type will be given as a nil pointer to a function with the -// desired signature. -func FuncFromNativeTyped(fn func(*Thread, []Value, []Value), t interface{}) (*FuncType, FuncValue) { - ft := TypeOfNative(t).(*FuncType) - return ft, FuncFromNative(fn, ft) -} diff --git a/src/pkg/exp/eval/compiler.go b/src/pkg/exp/eval/compiler.go deleted file mode 100644 index 9d2923bfc..000000000 --- a/src/pkg/exp/eval/compiler.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "fmt" - "go/scanner" - "go/token" -) - - -// A compiler captures information used throughout an entire -// compilation. Currently it includes only the error handler. -// -// TODO(austin) This might actually represent package level, in which -// case it should be package compiler. -type compiler struct { - fset *token.FileSet - errors scanner.ErrorHandler - numErrors int - silentErrors int -} - -func (a *compiler) diagAt(pos token.Pos, format string, args ...interface{}) { - a.errors.Error(a.fset.Position(pos), fmt.Sprintf(format, args...)) - a.numErrors++ -} - -func (a *compiler) numError() int { return a.numErrors + a.silentErrors } - -// The universal scope -func newUniverse() *Scope { - sc := &Scope{nil, 0} - sc.block = &block{ - offset: 0, - scope: sc, - global: true, - defs: make(map[string]Def), - } - return sc -} - -var universe *Scope = newUniverse() - - -// TODO(austin) These can all go in stmt.go now -type label struct { - name string - desc string - // The PC goto statements should jump to, or nil if this label - // cannot be goto'd (such as an anonymous for loop label). - gotoPC *uint - // The PC break statements should jump to, or nil if a break - // statement is invalid. - breakPC *uint - // The PC continue statements should jump to, or nil if a - // continue statement is invalid. - continuePC *uint - // The position where this label was resolved. If it has not - // been resolved yet, an invalid position. - resolved token.Pos - // The position where this label was first jumped to. - used token.Pos -} - -// A funcCompiler captures information used throughout the compilation -// of a single function body. -type funcCompiler struct { - *compiler - fnType *FuncType - // Whether the out variables are named. This affects what - // kinds of return statements are legal. - outVarsNamed bool - *codeBuf - flow *flowBuf - labels map[string]*label -} - -// A blockCompiler captures information used throughout the compilation -// of a single block within a function. -type blockCompiler struct { - *funcCompiler - block *block - // The label of this block, used for finding break and - // continue labels. - label *label - // The blockCompiler for the block enclosing this one, or nil - // for a function-level block. - parent *blockCompiler -} diff --git a/src/pkg/exp/eval/eval b/src/pkg/exp/eval/eval Binary files differdeleted file mode 100755 index 20231f2e2..000000000 --- a/src/pkg/exp/eval/eval +++ /dev/null diff --git a/src/pkg/exp/eval/eval_test.go b/src/pkg/exp/eval/eval_test.go deleted file mode 100644 index 541d3feb7..000000000 --- a/src/pkg/exp/eval/eval_test.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "big" - "flag" - "fmt" - "go/token" - "log" - "os" - "reflect" - "regexp" - "testing" -) - -// All tests are done using the same file set. -var fset = token.NewFileSet() - -// Print each statement or expression before parsing it -var noisy = false - -func init() { flag.BoolVar(&noisy, "noisy", false, "chatter during eval tests") } - -/* - * Generic statement/expression test framework - */ - -type test []job - -type job struct { - code string - cerr string - rterr string - val Value - noval bool -} - -func runTests(t *testing.T, baseName string, tests []test) { - delta := 1 - if testing.Short() { - delta = 16 - } - for i := 0; i < len(tests); i += delta { - name := fmt.Sprintf("%s[%d]", baseName, i) - tests[i].run(t, name) - } -} - -func (a test) run(t *testing.T, name string) { - w := newTestWorld() - for _, j := range a { - src := j.code + ";" // trailing semicolon to finish statement - if noisy { - println("code:", src) - } - - code, err := w.Compile(fset, src) - if err != nil { - if j.cerr == "" { - t.Errorf("%s: Compile %s: %v", name, src, err) - break - } - if !match(t, err, j.cerr) { - t.Errorf("%s: Compile %s = error %s; want %v", name, src, err, j.cerr) - break - } - continue - } - if j.cerr != "" { - t.Errorf("%s: Compile %s succeeded; want %s", name, src, j.cerr) - break - } - - val, err := code.Run() - if err != nil { - if j.rterr == "" { - t.Errorf("%s: Run %s: %v", name, src, err) - break - } - if !match(t, err, j.rterr) { - t.Errorf("%s: Run %s = error %s; want %v", name, src, err, j.rterr) - break - } - continue - } - if j.rterr != "" { - t.Errorf("%s: Run %s succeeded; want %s", name, src, j.rterr) - break - } - - if !j.noval && !reflect.DeepEqual(val, j.val) { - t.Errorf("%s: Run %s = %T(%v) want %T(%v)", name, src, val, val, j.val, j.val) - } - } -} - -func match(t *testing.T, err os.Error, pat string) bool { - ok, err1 := regexp.MatchString(pat, err.String()) - if err1 != nil { - t.Fatalf("compile regexp %s: %v", pat, err1) - } - return ok -} - - -/* - * Test constructors - */ - -// Expression compile error -func CErr(expr string, cerr string) test { return test([]job{{code: expr, cerr: cerr}}) } - -// Expression runtime error -func RErr(expr string, rterr string) test { return test([]job{{code: expr, rterr: rterr}}) } - -// Expression value -func Val(expr string, val interface{}) test { - return test([]job{{code: expr, val: toValue(val)}}) -} - -// Statement runs without error -func Run(stmts string) test { return test([]job{{code: stmts, noval: true}}) } - -// Two statements without error. -// TODO(rsc): Should be possible with Run but the parser -// won't let us do both top-level and non-top-level statements. -func Run2(stmt1, stmt2 string) test { - return test([]job{{code: stmt1, noval: true}, {code: stmt2, noval: true}}) -} - -// Statement runs and test one expression's value -func Val1(stmts string, expr1 string, val1 interface{}) test { - return test([]job{ - {code: stmts, noval: true}, - {code: expr1, val: toValue(val1)}, - }) -} - -// Statement runs and test two expressions' values -func Val2(stmts string, expr1 string, val1 interface{}, expr2 string, val2 interface{}) test { - return test([]job{ - {code: stmts, noval: true}, - {code: expr1, val: toValue(val1)}, - {code: expr2, val: toValue(val2)}, - }) -} - -/* - * Value constructors - */ - -type vstruct []interface{} - -type varray []interface{} - -type vslice struct { - arr varray - len, cap int -} - -func toValue(val interface{}) Value { - switch val := val.(type) { - case bool: - r := boolV(val) - return &r - case uint8: - r := uint8V(val) - return &r - case uint: - r := uintV(val) - return &r - case int: - r := intV(val) - return &r - case *big.Int: - return &idealIntV{val} - case float64: - r := float64V(val) - return &r - case *big.Rat: - return &idealFloatV{val} - case string: - r := stringV(val) - return &r - case vstruct: - elems := make([]Value, len(val)) - for i, e := range val { - elems[i] = toValue(e) - } - r := structV(elems) - return &r - case varray: - elems := make([]Value, len(val)) - for i, e := range val { - elems[i] = toValue(e) - } - r := arrayV(elems) - return &r - case vslice: - return &sliceV{Slice{toValue(val.arr).(ArrayValue), int64(val.len), int64(val.cap)}} - case Func: - return &funcV{val} - } - log.Panicf("toValue(%T) not implemented", val) - panic("unreachable") -} - -/* - * Default test scope - */ - -type testFunc struct{} - -func (*testFunc) NewFrame() *Frame { return &Frame{nil, make([]Value, 2)} } - -func (*testFunc) Call(t *Thread) { - n := t.f.Vars[0].(IntValue).Get(t) - - res := n + 1 - - t.f.Vars[1].(IntValue).Set(t, res) -} - -type oneTwoFunc struct{} - -func (*oneTwoFunc) NewFrame() *Frame { return &Frame{nil, make([]Value, 2)} } - -func (*oneTwoFunc) Call(t *Thread) { - t.f.Vars[0].(IntValue).Set(t, 1) - t.f.Vars[1].(IntValue).Set(t, 2) -} - -type voidFunc struct{} - -func (*voidFunc) NewFrame() *Frame { return &Frame{nil, []Value{}} } - -func (*voidFunc) Call(t *Thread) {} - -func newTestWorld() *World { - w := NewWorld() - - def := func(name string, t Type, val interface{}) { w.DefineVar(name, t, toValue(val)) } - - w.DefineConst("c", IdealIntType, toValue(big.NewInt(1))) - def("i", IntType, 1) - def("i2", IntType, 2) - def("u", UintType, uint(1)) - def("f", Float64Type, 1.0) - def("s", StringType, "abc") - def("t", NewStructType([]StructField{{"a", IntType, false}}), vstruct{1}) - def("ai", NewArrayType(2, IntType), varray{1, 2}) - def("aai", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{1, 2}, varray{3, 4}}) - def("aai2", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{5, 6}, varray{7, 8}}) - def("fn", NewFuncType([]Type{IntType}, false, []Type{IntType}), &testFunc{}) - def("oneTwo", NewFuncType([]Type{}, false, []Type{IntType, IntType}), &oneTwoFunc{}) - def("void", NewFuncType([]Type{}, false, []Type{}), &voidFunc{}) - def("sli", NewSliceType(IntType), vslice{varray{1, 2, 3}, 2, 3}) - - return w -} diff --git a/src/pkg/exp/eval/expr.go b/src/pkg/exp/eval/expr.go deleted file mode 100644 index 14a0659b6..000000000 --- a/src/pkg/exp/eval/expr.go +++ /dev/null @@ -1,2015 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "big" - "fmt" - "go/ast" - "go/token" - "log" - "strconv" - "strings" - "os" -) - -var ( - idealZero = big.NewInt(0) - idealOne = big.NewInt(1) -) - -// An expr is the result of compiling an expression. It stores the -// type of the expression and its evaluator function. -type expr struct { - *exprInfo - t Type - - // Evaluate this node as the given type. - eval interface{} - - // Map index expressions permit special forms of assignment, - // for which we need to know the Map and key. - evalMapValue func(t *Thread) (Map, interface{}) - - // Evaluate to the "address of" this value; that is, the - // settable Value object. nil for expressions whose address - // cannot be taken. - evalAddr func(t *Thread) Value - - // Execute this expression as a statement. Only expressions - // that are valid expression statements should set this. - exec func(t *Thread) - - // If this expression is a type, this is its compiled type. - // This is only permitted in the function position of a call - // expression. In this case, t should be nil. - valType Type - - // A short string describing this expression for error - // messages. - desc string -} - -// exprInfo stores information needed to compile any expression node. -// Each expr also stores its exprInfo so further expressions can be -// compiled from it. -type exprInfo struct { - *compiler - pos token.Pos -} - -func (a *exprInfo) newExpr(t Type, desc string) *expr { - return &expr{exprInfo: a, t: t, desc: desc} -} - -func (a *exprInfo) diag(format string, args ...interface{}) { - a.diagAt(a.pos, format, args...) -} - -func (a *exprInfo) diagOpType(op token.Token, vt Type) { - a.diag("illegal operand type for '%v' operator\n\t%v", op, vt) -} - -func (a *exprInfo) diagOpTypes(op token.Token, lt Type, rt Type) { - a.diag("illegal operand types for '%v' operator\n\t%v\n\t%v", op, lt, rt) -} - -/* - * Common expression manipulations - */ - -// a.convertTo(t) converts the value of the analyzed expression a, -// which must be a constant, ideal number, to a new analyzed -// expression with a constant value of type t. -// -// TODO(austin) Rename to resolveIdeal or something? -func (a *expr) convertTo(t Type) *expr { - if !a.t.isIdeal() { - log.Panicf("attempted to convert from %v, expected ideal", a.t) - } - - var rat *big.Rat - - // XXX(Spec) The spec says "It is erroneous". - // - // It is an error to assign a value with a non-zero fractional - // part to an integer, or if the assignment would overflow or - // underflow, or in general if the value cannot be represented - // by the type of the variable. - switch a.t { - case IdealFloatType: - rat = a.asIdealFloat()() - if t.isInteger() && !rat.IsInt() { - a.diag("constant %v truncated to integer", rat.FloatString(6)) - return nil - } - case IdealIntType: - i := a.asIdealInt()() - rat = new(big.Rat).SetInt(i) - default: - log.Panicf("unexpected ideal type %v", a.t) - } - - // Check bounds - if t, ok := t.lit().(BoundedType); ok { - if rat.Cmp(t.minVal()) < 0 { - a.diag("constant %v underflows %v", rat.FloatString(6), t) - return nil - } - if rat.Cmp(t.maxVal()) > 0 { - a.diag("constant %v overflows %v", rat.FloatString(6), t) - return nil - } - } - - // Convert rat to type t. - res := a.newExpr(t, a.desc) - switch t := t.lit().(type) { - case *uintType: - n, d := rat.Num(), rat.Denom() - f := new(big.Int).Quo(n, d) - f = f.Abs(f) - v := uint64(f.Int64()) - res.eval = func(*Thread) uint64 { return v } - case *intType: - n, d := rat.Num(), rat.Denom() - f := new(big.Int).Quo(n, d) - v := f.Int64() - res.eval = func(*Thread) int64 { return v } - case *idealIntType: - n, d := rat.Num(), rat.Denom() - f := new(big.Int).Quo(n, d) - res.eval = func() *big.Int { return f } - case *floatType: - n, d := rat.Num(), rat.Denom() - v := float64(n.Int64()) / float64(d.Int64()) - res.eval = func(*Thread) float64 { return v } - case *idealFloatType: - res.eval = func() *big.Rat { return rat } - default: - log.Panicf("cannot convert to type %T", t) - } - - return res -} - -// convertToInt converts this expression to an integer, if possible, -// or produces an error if not. This accepts ideal ints, uints, and -// ints. If max is not -1, produces an error if possible if the value -// exceeds max. If negErr is not "", produces an error if possible if -// the value is negative. -func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr { - switch a.t.lit().(type) { - case *idealIntType: - val := a.asIdealInt()() - if negErr != "" && val.Sign() < 0 { - a.diag("negative %s: %s", negErr, val) - return nil - } - bound := max - if negErr == "slice" { - bound++ - } - if max != -1 && val.Cmp(big.NewInt(bound)) >= 0 { - a.diag("index %s exceeds length %d", val, max) - return nil - } - return a.convertTo(IntType) - - case *uintType: - // Convert to int - na := a.newExpr(IntType, a.desc) - af := a.asUint() - na.eval = func(t *Thread) int64 { return int64(af(t)) } - return na - - case *intType: - // Good as is - return a - } - - a.diag("illegal operand type for %s\n\t%v", errOp, a.t) - return nil -} - -// derefArray returns an expression of array type if the given -// expression is a *array type. Otherwise, returns the given -// expression. -func (a *expr) derefArray() *expr { - if pt, ok := a.t.lit().(*PtrType); ok { - if _, ok := pt.Elem.lit().(*ArrayType); ok { - deref := a.compileStarExpr(a) - if deref == nil { - log.Panicf("failed to dereference *array") - } - return deref - } - } - return a -} - -/* - * Assignments - */ - -// An assignCompiler compiles assignment operations. Anything other -// than short declarations should use the compileAssign wrapper. -// -// There are three valid types of assignment: -// 1) T = T -// Assigning a single expression with single-valued type to a -// single-valued type. -// 2) MT = T, T, ... -// Assigning multiple expressions with single-valued types to a -// multi-valued type. -// 3) MT = MT -// Assigning a single expression with multi-valued type to a -// multi-valued type. -type assignCompiler struct { - *compiler - pos token.Pos - // The RHS expressions. This may include nil's for - // expressions that failed to compile. - rs []*expr - // The (possibly unary) MultiType of the RHS. - rmt *MultiType - // Whether this is an unpack assignment (case 3). - isUnpack bool - // Whether map special assignment forms are allowed. - allowMap bool - // Whether this is a "r, ok = a[x]" assignment. - isMapUnpack bool - // The operation name to use in error messages, such as - // "assignment" or "function call". - errOp string - // The name to use for positions in error messages, such as - // "argument". - errPosName string -} - -// Type check the RHS of an assignment, returning a new assignCompiler -// and indicating if the type check succeeded. This always returns an -// assignCompiler with rmt set, but if type checking fails, slots in -// the MultiType may be nil. If rs contains nil's, type checking will -// fail and these expressions given a nil type. -func (a *compiler) checkAssign(pos token.Pos, rs []*expr, errOp, errPosName string) (*assignCompiler, bool) { - c := &assignCompiler{ - compiler: a, - pos: pos, - rs: rs, - errOp: errOp, - errPosName: errPosName, - } - - // Is this an unpack? - if len(rs) == 1 && rs[0] != nil { - if rmt, isUnpack := rs[0].t.(*MultiType); isUnpack { - c.rmt = rmt - c.isUnpack = true - return c, true - } - } - - // Create MultiType for RHS and check that all RHS expressions - // are single-valued. - rts := make([]Type, len(rs)) - ok := true - for i, r := range rs { - if r == nil { - ok = false - continue - } - - if _, isMT := r.t.(*MultiType); isMT { - r.diag("multi-valued expression not allowed in %s", errOp) - ok = false - continue - } - - rts[i] = r.t - } - - c.rmt = NewMultiType(rts) - return c, ok -} - -func (a *assignCompiler) allowMapForms(nls int) { - a.allowMap = true - - // Update unpacking info if this is r, ok = a[x] - if nls == 2 && len(a.rs) == 1 && a.rs[0] != nil && a.rs[0].evalMapValue != nil { - a.isUnpack = true - a.rmt = NewMultiType([]Type{a.rs[0].t, BoolType}) - a.isMapUnpack = true - } -} - -// compile type checks and compiles an assignment operation, returning -// a function that expects an l-value and the frame in which to -// evaluate the RHS expressions. The l-value must have exactly the -// type given by lt. Returns nil if type checking fails. -func (a *assignCompiler) compile(b *block, lt Type) func(Value, *Thread) { - lmt, isMT := lt.(*MultiType) - rmt, isUnpack := a.rmt, a.isUnpack - - // Create unary MultiType for single LHS - if !isMT { - lmt = NewMultiType([]Type{lt}) - } - - // Check that the assignment count matches - lcount := len(lmt.Elems) - rcount := len(rmt.Elems) - if lcount != rcount { - msg := "not enough" - pos := a.pos - if rcount > lcount { - msg = "too many" - if lcount > 0 { - pos = a.rs[lcount-1].pos - } - } - a.diagAt(pos, "%s %ss for %s\n\t%s\n\t%s", msg, a.errPosName, a.errOp, lt, rmt) - return nil - } - - bad := false - - // If this is an unpack, create a temporary to store the - // multi-value and replace the RHS with expressions to pull - // out values from the temporary. Technically, this is only - // necessary when we need to perform assignment conversions. - var effect func(*Thread) - if isUnpack { - // This leaks a slot, but is definitely safe. - temp := b.DefineTemp(a.rmt) - tempIdx := temp.Index - if tempIdx < 0 { - panic(fmt.Sprintln("tempidx", tempIdx)) - } - if a.isMapUnpack { - rf := a.rs[0].evalMapValue - vt := a.rmt.Elems[0] - effect = func(t *Thread) { - m, k := rf(t) - v := m.Elem(t, k) - found := boolV(true) - if v == nil { - found = boolV(false) - v = vt.Zero() - } - t.f.Vars[tempIdx] = multiV([]Value{v, &found}) - } - } else { - rf := a.rs[0].asMulti() - effect = func(t *Thread) { t.f.Vars[tempIdx] = multiV(rf(t)) } - } - orig := a.rs[0] - a.rs = make([]*expr, len(a.rmt.Elems)) - for i, t := range a.rmt.Elems { - if t.isIdeal() { - log.Panicf("Right side of unpack contains ideal: %s", rmt) - } - a.rs[i] = orig.newExpr(t, orig.desc) - index := i - a.rs[i].genValue(func(t *Thread) Value { return t.f.Vars[tempIdx].(multiV)[index] }) - } - } - // Now len(a.rs) == len(a.rmt) and we've reduced any unpacking - // to multi-assignment. - - // TODO(austin) Deal with assignment special cases. - - // Values of any type may always be assigned to variables of - // compatible static type. - for i, lt := range lmt.Elems { - rt := rmt.Elems[i] - - // When [an ideal is] (used in an expression) assigned - // to a variable or typed constant, the destination - // must be able to represent the assigned value. - if rt.isIdeal() { - a.rs[i] = a.rs[i].convertTo(lmt.Elems[i]) - if a.rs[i] == nil { - bad = true - continue - } - rt = a.rs[i].t - } - - // A pointer p to an array can be assigned to a slice - // variable v with compatible element type if the type - // of p or v is unnamed. - if rpt, ok := rt.lit().(*PtrType); ok { - if at, ok := rpt.Elem.lit().(*ArrayType); ok { - if lst, ok := lt.lit().(*SliceType); ok { - if lst.Elem.compat(at.Elem, false) && (rt.lit() == Type(rt) || lt.lit() == Type(lt)) { - rf := a.rs[i].asPtr() - a.rs[i] = a.rs[i].newExpr(lt, a.rs[i].desc) - len := at.Len - a.rs[i].eval = func(t *Thread) Slice { return Slice{rf(t).(ArrayValue), len, len} } - rt = a.rs[i].t - } - } - } - } - - if !lt.compat(rt, false) { - if len(a.rs) == 1 { - a.rs[0].diag("illegal operand types for %s\n\t%v\n\t%v", a.errOp, lt, rt) - } else { - a.rs[i].diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", a.errPosName, i+1, a.errOp, lt, rt) - } - bad = true - } - } - if bad { - return nil - } - - // Compile - if !isMT { - // Case 1 - return genAssign(lt, a.rs[0]) - } - // Case 2 or 3 - as := make([]func(lv Value, t *Thread), len(a.rs)) - for i, r := range a.rs { - as[i] = genAssign(lmt.Elems[i], r) - } - return func(lv Value, t *Thread) { - if effect != nil { - effect(t) - } - lmv := lv.(multiV) - for i, a := range as { - a(lmv[i], t) - } - } -} - -// compileAssign compiles an assignment operation without the full -// generality of an assignCompiler. See assignCompiler for a -// description of the arguments. -func (a *compiler) compileAssign(pos token.Pos, b *block, lt Type, rs []*expr, errOp, errPosName string) func(Value, *Thread) { - ac, ok := a.checkAssign(pos, rs, errOp, errPosName) - if !ok { - return nil - } - return ac.compile(b, lt) -} - -/* - * Expression compiler - */ - -// An exprCompiler stores information used throughout the compilation -// of a single expression. It does not embed funcCompiler because -// expressions can appear at top level. -type exprCompiler struct { - *compiler - // The block this expression is being compiled in. - block *block - // Whether this expression is used in a constant context. - constant bool -} - -// compile compiles an expression AST. callCtx should be true if this -// AST is in the function position of a function call node; it allows -// the returned expression to be a type or a built-in function (which -// otherwise result in errors). -func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr { - ei := &exprInfo{a.compiler, x.Pos()} - - switch x := x.(type) { - // Literals - case *ast.BasicLit: - switch x.Kind { - case token.INT: - return ei.compileIntLit(string(x.Value)) - case token.FLOAT: - return ei.compileFloatLit(string(x.Value)) - case token.CHAR: - return ei.compileCharLit(string(x.Value)) - case token.STRING: - return ei.compileStringLit(string(x.Value)) - default: - log.Panicf("unexpected basic literal type %v", x.Kind) - } - - case *ast.CompositeLit: - goto notimpl - - case *ast.FuncLit: - decl := ei.compileFuncType(a.block, x.Type) - if decl == nil { - // TODO(austin) Try compiling the body, - // perhaps with dummy argument definitions - return nil - } - fn := ei.compileFunc(a.block, decl, x.Body) - if fn == nil { - return nil - } - if a.constant { - a.diagAt(x.Pos(), "function literal used in constant expression") - return nil - } - return ei.compileFuncLit(decl, fn) - - // Types - case *ast.ArrayType: - // TODO(austin) Use a multi-type case - goto typeexpr - - case *ast.ChanType: - goto typeexpr - - case *ast.Ellipsis: - goto typeexpr - - case *ast.FuncType: - goto typeexpr - - case *ast.InterfaceType: - goto typeexpr - - case *ast.MapType: - goto typeexpr - - // Remaining expressions - case *ast.BadExpr: - // Error already reported by parser - a.silentErrors++ - return nil - - case *ast.BinaryExpr: - l, r := a.compile(x.X, false), a.compile(x.Y, false) - if l == nil || r == nil { - return nil - } - return ei.compileBinaryExpr(x.Op, l, r) - - case *ast.CallExpr: - l := a.compile(x.Fun, true) - args := make([]*expr, len(x.Args)) - bad := false - for i, arg := range x.Args { - if i == 0 && l != nil && (l.t == Type(makeType) || l.t == Type(newType)) { - argei := &exprInfo{a.compiler, arg.Pos()} - args[i] = argei.exprFromType(a.compileType(a.block, arg)) - } else { - args[i] = a.compile(arg, false) - } - if args[i] == nil { - bad = true - } - } - if bad || l == nil { - return nil - } - if a.constant { - a.diagAt(x.Pos(), "function call in constant context") - return nil - } - - if l.valType != nil { - a.diagAt(x.Pos(), "type conversions not implemented") - return nil - } else if ft, ok := l.t.(*FuncType); ok && ft.builtin != "" { - return ei.compileBuiltinCallExpr(a.block, ft, args) - } else { - return ei.compileCallExpr(a.block, l, args) - } - - case *ast.Ident: - return ei.compileIdent(a.block, a.constant, callCtx, x.Name) - - case *ast.IndexExpr: - l, r := a.compile(x.X, false), a.compile(x.Index, false) - if l == nil || r == nil { - return nil - } - return ei.compileIndexExpr(l, r) - - case *ast.SliceExpr: - var lo, hi *expr - arr := a.compile(x.X, false) - if x.Low == nil { - // beginning was omitted, so we need to provide it - ei := &exprInfo{a.compiler, x.Pos()} - lo = ei.compileIntLit("0") - } else { - lo = a.compile(x.Low, false) - } - if x.High == nil { - // End was omitted, so we need to compute len(x.X) - ei := &exprInfo{a.compiler, x.Pos()} - hi = ei.compileBuiltinCallExpr(a.block, lenType, []*expr{arr}) - } else { - hi = a.compile(x.High, false) - } - if arr == nil || lo == nil || hi == nil { - return nil - } - return ei.compileSliceExpr(arr, lo, hi) - - case *ast.KeyValueExpr: - goto notimpl - - case *ast.ParenExpr: - return a.compile(x.X, callCtx) - - case *ast.SelectorExpr: - v := a.compile(x.X, false) - if v == nil { - return nil - } - return ei.compileSelectorExpr(v, x.Sel.Name) - - case *ast.StarExpr: - // We pass down our call context because this could be - // a pointer type (and thus a type conversion) - v := a.compile(x.X, callCtx) - if v == nil { - return nil - } - if v.valType != nil { - // Turns out this was a pointer type, not a dereference - return ei.exprFromType(NewPtrType(v.valType)) - } - return ei.compileStarExpr(v) - - case *ast.StructType: - goto notimpl - - case *ast.TypeAssertExpr: - goto notimpl - - case *ast.UnaryExpr: - v := a.compile(x.X, false) - if v == nil { - return nil - } - return ei.compileUnaryExpr(x.Op, v) - } - log.Panicf("unexpected ast node type %T", x) - panic("unreachable") - -typeexpr: - if !callCtx { - a.diagAt(x.Pos(), "type used as expression") - return nil - } - return ei.exprFromType(a.compileType(a.block, x)) - -notimpl: - a.diagAt(x.Pos(), "%T expression node not implemented", x) - return nil -} - -func (a *exprInfo) exprFromType(t Type) *expr { - if t == nil { - return nil - } - expr := a.newExpr(nil, "type") - expr.valType = t - return expr -} - -func (a *exprInfo) compileIdent(b *block, constant bool, callCtx bool, name string) *expr { - bl, level, def := b.Lookup(name) - if def == nil { - a.diag("%s: undefined", name) - return nil - } - switch def := def.(type) { - case *Constant: - expr := a.newExpr(def.Type, "constant") - if ft, ok := def.Type.(*FuncType); ok && ft.builtin != "" { - // XXX(Spec) I don't think anything says that - // built-in functions can't be used as values. - if !callCtx { - a.diag("built-in function %s cannot be used as a value", ft.builtin) - return nil - } - // Otherwise, we leave the evaluators empty - // because this is handled specially - } else { - expr.genConstant(def.Value) - } - return expr - case *Variable: - if constant { - a.diag("variable %s used in constant expression", name) - return nil - } - if bl.global { - return a.compileGlobalVariable(def) - } - return a.compileVariable(level, def) - case Type: - if callCtx { - return a.exprFromType(def) - } - a.diag("type %v used as expression", name) - return nil - } - log.Panicf("name %s has unknown type %T", name, def) - panic("unreachable") -} - -func (a *exprInfo) compileVariable(level int, v *Variable) *expr { - if v.Type == nil { - // Placeholder definition from an earlier error - a.silentErrors++ - return nil - } - expr := a.newExpr(v.Type, "variable") - expr.genIdentOp(level, v.Index) - return expr -} - -func (a *exprInfo) compileGlobalVariable(v *Variable) *expr { - if v.Type == nil { - // Placeholder definition from an earlier error - a.silentErrors++ - return nil - } - if v.Init == nil { - v.Init = v.Type.Zero() - } - expr := a.newExpr(v.Type, "variable") - val := v.Init - expr.genValue(func(t *Thread) Value { return val }) - return expr -} - -func (a *exprInfo) compileIdealInt(i *big.Int, desc string) *expr { - expr := a.newExpr(IdealIntType, desc) - expr.eval = func() *big.Int { return i } - return expr -} - -func (a *exprInfo) compileIntLit(lit string) *expr { - i, _ := new(big.Int).SetString(lit, 0) - return a.compileIdealInt(i, "integer literal") -} - -func (a *exprInfo) compileCharLit(lit string) *expr { - if lit[0] != '\'' { - // Caught by parser - a.silentErrors++ - return nil - } - v, _, tail, err := strconv.UnquoteChar(lit[1:], '\'') - if err != nil || tail != "'" { - // Caught by parser - a.silentErrors++ - return nil - } - return a.compileIdealInt(big.NewInt(int64(v)), "character literal") -} - -func (a *exprInfo) compileFloatLit(lit string) *expr { - f, ok := new(big.Rat).SetString(lit) - if !ok { - log.Panicf("malformed float literal %s at %v passed parser", lit, a.pos) - } - expr := a.newExpr(IdealFloatType, "float literal") - expr.eval = func() *big.Rat { return f } - return expr -} - -func (a *exprInfo) compileString(s string) *expr { - // Ideal strings don't have a named type but they are - // compatible with type string. - - // TODO(austin) Use unnamed string type. - expr := a.newExpr(StringType, "string literal") - expr.eval = func(*Thread) string { return s } - return expr -} - -func (a *exprInfo) compileStringLit(lit string) *expr { - s, err := strconv.Unquote(lit) - if err != nil { - a.diag("illegal string literal, %v", err) - return nil - } - return a.compileString(s) -} - -func (a *exprInfo) compileStringList(list []*expr) *expr { - ss := make([]string, len(list)) - for i, s := range list { - ss[i] = s.asString()(nil) - } - return a.compileString(strings.Join(ss, "")) -} - -func (a *exprInfo) compileFuncLit(decl *FuncDecl, fn func(*Thread) Func) *expr { - expr := a.newExpr(decl.Type, "function literal") - expr.eval = fn - return expr -} - -func (a *exprInfo) compileSelectorExpr(v *expr, name string) *expr { - // mark marks a field that matches the selector name. It - // tracks the best depth found so far and whether more than - // one field has been found at that depth. - bestDepth := -1 - ambig := false - amberr := "" - mark := func(depth int, pathName string) { - switch { - case bestDepth == -1 || depth < bestDepth: - bestDepth = depth - ambig = false - amberr = "" - - case depth == bestDepth: - ambig = true - - default: - log.Panicf("Marked field at depth %d, but already found one at depth %d", depth, bestDepth) - } - amberr += "\n\t" + pathName[1:] - } - - visited := make(map[Type]bool) - - // find recursively searches for the named field, starting at - // type t. If it finds the named field, it returns a function - // which takes an expr that represents a value of type 't' and - // returns an expr that retrieves the named field. We delay - // expr construction to avoid producing lots of useless expr's - // as we search. - // - // TODO(austin) Now that the expression compiler works on - // semantic values instead of AST's, there should be a much - // better way of doing this. - var find func(Type, int, string) func(*expr) *expr - find = func(t Type, depth int, pathName string) func(*expr) *expr { - // Don't bother looking if we've found something shallower - if bestDepth != -1 && bestDepth < depth { - return nil - } - - // Don't check the same type twice and avoid loops - if visited[t] { - return nil - } - visited[t] = true - - // Implicit dereference - deref := false - if ti, ok := t.(*PtrType); ok { - deref = true - t = ti.Elem - } - - // If it's a named type, look for methods - if ti, ok := t.(*NamedType); ok { - _, ok := ti.methods[name] - if ok { - mark(depth, pathName+"."+name) - log.Panic("Methods not implemented") - } - t = ti.Def - } - - // If it's a struct type, check fields and embedded types - var builder func(*expr) *expr - if t, ok := t.(*StructType); ok { - for i, f := range t.Elems { - var sub func(*expr) *expr - switch { - case f.Name == name: - mark(depth, pathName+"."+name) - sub = func(e *expr) *expr { return e } - - case f.Anonymous: - sub = find(f.Type, depth+1, pathName+"."+f.Name) - if sub == nil { - continue - } - - default: - continue - } - - // We found something. Create a - // builder for accessing this field. - ft := f.Type - index := i - builder = func(parent *expr) *expr { - if deref { - parent = a.compileStarExpr(parent) - } - expr := a.newExpr(ft, "selector expression") - pf := parent.asStruct() - evalAddr := func(t *Thread) Value { return pf(t).Field(t, index) } - expr.genValue(evalAddr) - return sub(expr) - } - } - } - - return builder - } - - builder := find(v.t, 0, "") - if builder == nil { - a.diag("type %v has no field or method %s", v.t, name) - return nil - } - if ambig { - a.diag("field %s is ambiguous in type %v%s", name, v.t, amberr) - return nil - } - - return builder(v) -} - -func (a *exprInfo) compileSliceExpr(arr, lo, hi *expr) *expr { - // Type check object - arr = arr.derefArray() - - var at Type - var maxIndex int64 = -1 - - switch lt := arr.t.lit().(type) { - case *ArrayType: - at = NewSliceType(lt.Elem) - maxIndex = lt.Len - - case *SliceType: - at = lt - - case *stringType: - at = lt - - default: - a.diag("cannot slice %v", arr.t) - return nil - } - - // Type check index and convert to int - // XXX(Spec) It's unclear if ideal floats with no - // fractional part are allowed here. 6g allows it. I - // believe that's wrong. - lo = lo.convertToInt(maxIndex, "slice", "slice") - hi = hi.convertToInt(maxIndex, "slice", "slice") - if lo == nil || hi == nil { - return nil - } - - expr := a.newExpr(at, "slice expression") - - // Compile - lof := lo.asInt() - hif := hi.asInt() - switch lt := arr.t.lit().(type) { - case *ArrayType: - arrf := arr.asArray() - bound := lt.Len - expr.eval = func(t *Thread) Slice { - arr, lo, hi := arrf(t), lof(t), hif(t) - if lo > hi || hi > bound || lo < 0 { - t.Abort(SliceError{lo, hi, bound}) - } - return Slice{arr.Sub(lo, bound-lo), hi - lo, bound - lo} - } - - case *SliceType: - arrf := arr.asSlice() - expr.eval = func(t *Thread) Slice { - arr, lo, hi := arrf(t), lof(t), hif(t) - if lo > hi || hi > arr.Cap || lo < 0 { - t.Abort(SliceError{lo, hi, arr.Cap}) - } - return Slice{arr.Base.Sub(lo, arr.Cap-lo), hi - lo, arr.Cap - lo} - } - - case *stringType: - arrf := arr.asString() - // TODO(austin) This pulls over the whole string in a - // remote setting, instead of creating a substring backed - // by remote memory. - expr.eval = func(t *Thread) string { - arr, lo, hi := arrf(t), lof(t), hif(t) - if lo > hi || hi > int64(len(arr)) || lo < 0 { - t.Abort(SliceError{lo, hi, int64(len(arr))}) - } - return arr[lo:hi] - } - - default: - log.Panicf("unexpected left operand type %T", arr.t.lit()) - } - - return expr -} - -func (a *exprInfo) compileIndexExpr(l, r *expr) *expr { - // Type check object - l = l.derefArray() - - var at Type - intIndex := false - var maxIndex int64 = -1 - - switch lt := l.t.lit().(type) { - case *ArrayType: - at = lt.Elem - intIndex = true - maxIndex = lt.Len - - case *SliceType: - at = lt.Elem - intIndex = true - - case *stringType: - at = Uint8Type - intIndex = true - - case *MapType: - at = lt.Elem - if r.t.isIdeal() { - r = r.convertTo(lt.Key) - if r == nil { - return nil - } - } - if !lt.Key.compat(r.t, false) { - a.diag("cannot use %s as index into %s", r.t, lt) - return nil - } - - default: - a.diag("cannot index into %v", l.t) - return nil - } - - // Type check index and convert to int if necessary - if intIndex { - // XXX(Spec) It's unclear if ideal floats with no - // fractional part are allowed here. 6g allows it. I - // believe that's wrong. - r = r.convertToInt(maxIndex, "index", "index") - if r == nil { - return nil - } - } - - expr := a.newExpr(at, "index expression") - - // Compile - switch lt := l.t.lit().(type) { - case *ArrayType: - lf := l.asArray() - rf := r.asInt() - bound := lt.Len - expr.genValue(func(t *Thread) Value { - l, r := lf(t), rf(t) - if r < 0 || r >= bound { - t.Abort(IndexError{r, bound}) - } - return l.Elem(t, r) - }) - - case *SliceType: - lf := l.asSlice() - rf := r.asInt() - expr.genValue(func(t *Thread) Value { - l, r := lf(t), rf(t) - if l.Base == nil { - t.Abort(NilPointerError{}) - } - if r < 0 || r >= l.Len { - t.Abort(IndexError{r, l.Len}) - } - return l.Base.Elem(t, r) - }) - - case *stringType: - lf := l.asString() - rf := r.asInt() - // TODO(austin) This pulls over the whole string in a - // remote setting, instead of just the one character. - expr.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - if r < 0 || r >= int64(len(l)) { - t.Abort(IndexError{r, int64(len(l))}) - } - return uint64(l[r]) - } - - case *MapType: - lf := l.asMap() - rf := r.asInterface() - expr.genValue(func(t *Thread) Value { - m := lf(t) - k := rf(t) - if m == nil { - t.Abort(NilPointerError{}) - } - e := m.Elem(t, k) - if e == nil { - t.Abort(KeyError{k}) - } - return e - }) - // genValue makes things addressable, but map values - // aren't addressable. - expr.evalAddr = nil - expr.evalMapValue = func(t *Thread) (Map, interface{}) { - // TODO(austin) Key check? nil check? - return lf(t), rf(t) - } - - default: - log.Panicf("unexpected left operand type %T", l.t.lit()) - } - - return expr -} - -func (a *exprInfo) compileCallExpr(b *block, l *expr, as []*expr) *expr { - // TODO(austin) Variadic functions. - - // Type check - - // XXX(Spec) Calling a named function type is okay. I really - // think there needs to be a general discussion of named - // types. A named type creates a new, distinct type, but the - // type of that type is still whatever it's defined to. Thus, - // in "type Foo int", Foo is still an integer type and in - // "type Foo func()", Foo is a function type. - lt, ok := l.t.lit().(*FuncType) - if !ok { - a.diag("cannot call non-function type %v", l.t) - return nil - } - - // The arguments must be single-valued expressions assignment - // compatible with the parameters of F. - // - // XXX(Spec) The spec is wrong. It can also be a single - // multi-valued expression. - nin := len(lt.In) - assign := a.compileAssign(a.pos, b, NewMultiType(lt.In), as, "function call", "argument") - if assign == nil { - return nil - } - - var t Type - nout := len(lt.Out) - switch nout { - case 0: - t = EmptyType - case 1: - t = lt.Out[0] - default: - t = NewMultiType(lt.Out) - } - expr := a.newExpr(t, "function call") - - // Gather argument and out types to initialize frame variables - vts := make([]Type, nin+nout) - copy(vts, lt.In) - copy(vts[nin:], lt.Out) - - // Compile - lf := l.asFunc() - call := func(t *Thread) []Value { - fun := lf(t) - fr := fun.NewFrame() - for i, t := range vts { - fr.Vars[i] = t.Zero() - } - assign(multiV(fr.Vars[0:nin]), t) - oldf := t.f - t.f = fr - fun.Call(t) - t.f = oldf - return fr.Vars[nin : nin+nout] - } - expr.genFuncCall(call) - - return expr -} - -func (a *exprInfo) compileBuiltinCallExpr(b *block, ft *FuncType, as []*expr) *expr { - checkCount := func(min, max int) bool { - if len(as) < min { - a.diag("not enough arguments to %s", ft.builtin) - return false - } else if len(as) > max { - a.diag("too many arguments to %s", ft.builtin) - return false - } - return true - } - - switch ft { - case capType: - if !checkCount(1, 1) { - return nil - } - arg := as[0].derefArray() - expr := a.newExpr(IntType, "function call") - switch t := arg.t.lit().(type) { - case *ArrayType: - // TODO(austin) It would be nice if this could - // be a constant int. - v := t.Len - expr.eval = func(t *Thread) int64 { return v } - - case *SliceType: - vf := arg.asSlice() - expr.eval = func(t *Thread) int64 { return vf(t).Cap } - - //case *ChanType: - - default: - a.diag("illegal argument type for cap function\n\t%v", arg.t) - return nil - } - return expr - - case copyType: - if !checkCount(2, 2) { - return nil - } - src := as[1] - dst := as[0] - if src.t != dst.t { - a.diag("arguments to built-in function 'copy' must have same type\nsrc: %s\ndst: %s\n", src.t, dst.t) - return nil - } - if _, ok := src.t.lit().(*SliceType); !ok { - a.diag("src argument to 'copy' must be a slice (got: %s)", src.t) - return nil - } - if _, ok := dst.t.lit().(*SliceType); !ok { - a.diag("dst argument to 'copy' must be a slice (got: %s)", dst.t) - return nil - } - expr := a.newExpr(IntType, "function call") - srcf := src.asSlice() - dstf := dst.asSlice() - expr.eval = func(t *Thread) int64 { - src, dst := srcf(t), dstf(t) - nelems := src.Len - if nelems > dst.Len { - nelems = dst.Len - } - dst.Base.Sub(0, nelems).Assign(t, src.Base.Sub(0, nelems)) - return nelems - } - return expr - - case lenType: - if !checkCount(1, 1) { - return nil - } - arg := as[0].derefArray() - expr := a.newExpr(IntType, "function call") - switch t := arg.t.lit().(type) { - case *stringType: - vf := arg.asString() - expr.eval = func(t *Thread) int64 { return int64(len(vf(t))) } - - case *ArrayType: - // TODO(austin) It would be nice if this could - // be a constant int. - v := t.Len - expr.eval = func(t *Thread) int64 { return v } - - case *SliceType: - vf := arg.asSlice() - expr.eval = func(t *Thread) int64 { return vf(t).Len } - - case *MapType: - vf := arg.asMap() - expr.eval = func(t *Thread) int64 { - // XXX(Spec) What's the len of an - // uninitialized map? - m := vf(t) - if m == nil { - return 0 - } - return m.Len(t) - } - - //case *ChanType: - - default: - a.diag("illegal argument type for len function\n\t%v", arg.t) - return nil - } - return expr - - case makeType: - if !checkCount(1, 3) { - return nil - } - // XXX(Spec) What are the types of the - // arguments? Do they have to be ints? 6g - // accepts any integral type. - var lenexpr, capexpr *expr - var lenf, capf func(*Thread) int64 - if len(as) > 1 { - lenexpr = as[1].convertToInt(-1, "length", "make function") - if lenexpr == nil { - return nil - } - lenf = lenexpr.asInt() - } - if len(as) > 2 { - capexpr = as[2].convertToInt(-1, "capacity", "make function") - if capexpr == nil { - return nil - } - capf = capexpr.asInt() - } - - switch t := as[0].valType.lit().(type) { - case *SliceType: - // A new, initialized slice value for a given - // element type T is made using the built-in - // function make, which takes a slice type and - // parameters specifying the length and - // optionally the capacity. - if !checkCount(2, 3) { - return nil - } - et := t.Elem - expr := a.newExpr(t, "function call") - expr.eval = func(t *Thread) Slice { - l := lenf(t) - // XXX(Spec) What if len or cap is - // negative? The runtime panics. - if l < 0 { - t.Abort(NegativeLengthError{l}) - } - c := l - if capf != nil { - c = capf(t) - if c < 0 { - t.Abort(NegativeCapacityError{c}) - } - // XXX(Spec) What happens if - // len > cap? The runtime - // sets cap to len. - if l > c { - c = l - } - } - base := arrayV(make([]Value, c)) - for i := int64(0); i < c; i++ { - base[i] = et.Zero() - } - return Slice{&base, l, c} - } - return expr - - case *MapType: - // A new, empty map value is made using the - // built-in function make, which takes the map - // type and an optional capacity hint as - // arguments. - if !checkCount(1, 2) { - return nil - } - expr := a.newExpr(t, "function call") - expr.eval = func(t *Thread) Map { - if lenf == nil { - return make(evalMap) - } - l := lenf(t) - return make(evalMap, l) - } - return expr - - //case *ChanType: - - default: - a.diag("illegal argument type for make function\n\t%v", as[0].valType) - return nil - } - - case closeType, closedType: - a.diag("built-in function %s not implemented", ft.builtin) - return nil - - case newType: - if !checkCount(1, 1) { - return nil - } - - t := as[0].valType - expr := a.newExpr(NewPtrType(t), "new") - expr.eval = func(*Thread) Value { return t.Zero() } - return expr - - case panicType, printType, printlnType: - evals := make([]func(*Thread) interface{}, len(as)) - for i, x := range as { - evals[i] = x.asInterface() - } - spaces := ft == printlnType - newline := ft != printType - printer := func(t *Thread) { - for i, eval := range evals { - if i > 0 && spaces { - print(" ") - } - v := eval(t) - type stringer interface { - String() string - } - switch v1 := v.(type) { - case bool: - print(v1) - case uint64: - print(v1) - case int64: - print(v1) - case float64: - print(v1) - case string: - print(v1) - case stringer: - print(v1.String()) - default: - print("???") - } - } - if newline { - print("\n") - } - } - expr := a.newExpr(EmptyType, "print") - expr.exec = printer - if ft == panicType { - expr.exec = func(t *Thread) { - printer(t) - t.Abort(os.NewError("panic")) - } - } - return expr - } - - log.Panicf("unexpected built-in function '%s'", ft.builtin) - panic("unreachable") -} - -func (a *exprInfo) compileStarExpr(v *expr) *expr { - switch vt := v.t.lit().(type) { - case *PtrType: - expr := a.newExpr(vt.Elem, "indirect expression") - vf := v.asPtr() - expr.genValue(func(t *Thread) Value { - v := vf(t) - if v == nil { - t.Abort(NilPointerError{}) - } - return v - }) - return expr - } - - a.diagOpType(token.MUL, v.t) - return nil -} - -var unaryOpDescs = make(map[token.Token]string) - -func (a *exprInfo) compileUnaryExpr(op token.Token, v *expr) *expr { - // Type check - var t Type - switch op { - case token.ADD, token.SUB: - if !v.t.isInteger() && !v.t.isFloat() { - a.diagOpType(op, v.t) - return nil - } - t = v.t - - case token.NOT: - if !v.t.isBoolean() { - a.diagOpType(op, v.t) - return nil - } - t = BoolType - - case token.XOR: - if !v.t.isInteger() { - a.diagOpType(op, v.t) - return nil - } - t = v.t - - case token.AND: - // The unary prefix address-of operator & generates - // the address of its operand, which must be a - // variable, pointer indirection, field selector, or - // array or slice indexing operation. - if v.evalAddr == nil { - a.diag("cannot take the address of %s", v.desc) - return nil - } - - // TODO(austin) Implement "It is illegal to take the - // address of a function result variable" once I have - // function result variables. - - t = NewPtrType(v.t) - - case token.ARROW: - log.Panicf("Unary op %v not implemented", op) - - default: - log.Panicf("unknown unary operator %v", op) - } - - desc, ok := unaryOpDescs[op] - if !ok { - desc = "unary " + op.String() + " expression" - unaryOpDescs[op] = desc - } - - // Compile - expr := a.newExpr(t, desc) - switch op { - case token.ADD: - // Just compile it out - expr = v - expr.desc = desc - - case token.SUB: - expr.genUnaryOpNeg(v) - - case token.NOT: - expr.genUnaryOpNot(v) - - case token.XOR: - expr.genUnaryOpXor(v) - - case token.AND: - vf := v.evalAddr - expr.eval = func(t *Thread) Value { return vf(t) } - - default: - log.Panicf("Compilation of unary op %v not implemented", op) - } - - return expr -} - -var binOpDescs = make(map[token.Token]string) - -func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr { - // Save the original types of l.t and r.t for error messages. - origlt := l.t - origrt := r.t - - // XXX(Spec) What is the exact definition of a "named type"? - - // XXX(Spec) Arithmetic operators: "Integer types" apparently - // means all types compatible with basic integer types, though - // this is never explained. Likewise for float types, etc. - // This relates to the missing explanation of named types. - - // XXX(Spec) Operators: "If both operands are ideal numbers, - // the conversion is to ideal floats if one of the operands is - // an ideal float (relevant for / and %)." How is that - // relevant only for / and %? If I add an ideal int and an - // ideal float, I get an ideal float. - - if op != token.SHL && op != token.SHR { - // Except in shift expressions, if one operand has - // numeric type and the other operand is an ideal - // number, the ideal number is converted to match the - // type of the other operand. - if (l.t.isInteger() || l.t.isFloat()) && !l.t.isIdeal() && r.t.isIdeal() { - r = r.convertTo(l.t) - } else if (r.t.isInteger() || r.t.isFloat()) && !r.t.isIdeal() && l.t.isIdeal() { - l = l.convertTo(r.t) - } - if l == nil || r == nil { - return nil - } - - // Except in shift expressions, if both operands are - // ideal numbers and one is an ideal float, the other - // is converted to ideal float. - if l.t.isIdeal() && r.t.isIdeal() { - if l.t.isInteger() && r.t.isFloat() { - l = l.convertTo(r.t) - } else if l.t.isFloat() && r.t.isInteger() { - r = r.convertTo(l.t) - } - if l == nil || r == nil { - return nil - } - } - } - - // Useful type predicates - // TODO(austin) CL 33668 mandates identical types except for comparisons. - compat := func() bool { return l.t.compat(r.t, false) } - integers := func() bool { return l.t.isInteger() && r.t.isInteger() } - floats := func() bool { return l.t.isFloat() && r.t.isFloat() } - strings := func() bool { - // TODO(austin) Deal with named types - return l.t == StringType && r.t == StringType - } - booleans := func() bool { return l.t.isBoolean() && r.t.isBoolean() } - - // Type check - var t Type - switch op { - case token.ADD: - if !compat() || (!integers() && !floats() && !strings()) { - a.diagOpTypes(op, origlt, origrt) - return nil - } - t = l.t - - case token.SUB, token.MUL, token.QUO: - if !compat() || (!integers() && !floats()) { - a.diagOpTypes(op, origlt, origrt) - return nil - } - t = l.t - - case token.REM, token.AND, token.OR, token.XOR, token.AND_NOT: - if !compat() || !integers() { - a.diagOpTypes(op, origlt, origrt) - return nil - } - t = l.t - - case token.SHL, token.SHR: - // XXX(Spec) Is it okay for the right operand to be an - // ideal float with no fractional part? "The right - // operand in a shift operation must be always be of - // unsigned integer type or an ideal number that can - // be safely converted into an unsigned integer type - // (§Arithmetic operators)" suggests so and 6g agrees. - - if !l.t.isInteger() || !(r.t.isInteger() || r.t.isIdeal()) { - a.diagOpTypes(op, origlt, origrt) - return nil - } - - // The right operand in a shift operation must be - // always be of unsigned integer type or an ideal - // number that can be safely converted into an - // unsigned integer type. - if r.t.isIdeal() { - r2 := r.convertTo(UintType) - if r2 == nil { - return nil - } - - // If the left operand is not ideal, convert - // the right to not ideal. - if !l.t.isIdeal() { - r = r2 - } - - // If both are ideal, but the right side isn't - // an ideal int, convert it to simplify things. - if l.t.isIdeal() && !r.t.isInteger() { - r = r.convertTo(IdealIntType) - if r == nil { - log.Panicf("conversion to uintType succeeded, but conversion to idealIntType failed") - } - } - } else if _, ok := r.t.lit().(*uintType); !ok { - a.diag("right operand of shift must be unsigned") - return nil - } - - if l.t.isIdeal() && !r.t.isIdeal() { - // XXX(Spec) What is the meaning of "ideal >> - // non-ideal"? Russ says the ideal should be - // converted to an int. 6g propagates the - // type down from assignments as a hint. - - l = l.convertTo(IntType) - if l == nil { - return nil - } - } - - // At this point, we should have one of three cases: - // 1) uint SHIFT uint - // 2) int SHIFT uint - // 3) ideal int SHIFT ideal int - - t = l.t - - case token.LOR, token.LAND: - if !booleans() { - return nil - } - // XXX(Spec) There's no mention of *which* boolean - // type the logical operators return. From poking at - // 6g, it appears to be the named boolean type, NOT - // the type of the left operand, and NOT an unnamed - // boolean type. - - t = BoolType - - case token.ARROW: - // The operands in channel sends differ in type: one - // is always a channel and the other is a variable or - // value of the channel's element type. - log.Panic("Binary op <- not implemented") - t = BoolType - - case token.LSS, token.GTR, token.LEQ, token.GEQ: - // XXX(Spec) It's really unclear what types which - // comparison operators apply to. I feel like the - // text is trying to paint a Venn diagram for me, - // which it's really pretty simple: <, <=, >, >= apply - // only to numeric types and strings. == and != apply - // to everything except arrays and structs, and there - // are some restrictions on when it applies to slices. - - if !compat() || (!integers() && !floats() && !strings()) { - a.diagOpTypes(op, origlt, origrt) - return nil - } - t = BoolType - - case token.EQL, token.NEQ: - // XXX(Spec) The rules for type checking comparison - // operators are spread across three places that all - // partially overlap with each other: the Comparison - // Compatibility section, the Operators section, and - // the Comparison Operators section. The Operators - // section should just say that operators require - // identical types (as it does currently) except that - // there a few special cases for comparison, which are - // described in section X. Currently it includes just - // one of the four special cases. The Comparison - // Compatibility section and the Comparison Operators - // section should either be merged, or at least the - // Comparison Compatibility section should be - // exclusively about type checking and the Comparison - // Operators section should be exclusively about - // semantics. - - // XXX(Spec) Comparison operators: "All comparison - // operators apply to basic types except bools." This - // is very difficult to parse. It's explained much - // better in the Comparison Compatibility section. - - // XXX(Spec) Comparison compatibility: "Function - // values are equal if they refer to the same - // function." is rather vague. It should probably be - // similar to the way the rule for map values is - // written: Function values are equal if they were - // created by the same execution of a function literal - // or refer to the same function declaration. This is - // *almost* but not quite what 6g implements. If a - // function literals does not capture any variables, - // then multiple executions of it will result in the - // same closure. Russ says he'll change that. - - // TODO(austin) Deal with remaining special cases - - if !compat() { - a.diagOpTypes(op, origlt, origrt) - return nil - } - // Arrays and structs may not be compared to anything. - switch l.t.(type) { - case *ArrayType, *StructType: - a.diagOpTypes(op, origlt, origrt) - return nil - } - t = BoolType - - default: - log.Panicf("unknown binary operator %v", op) - } - - desc, ok := binOpDescs[op] - if !ok { - desc = op.String() + " expression" - binOpDescs[op] = desc - } - - // Check for ideal divide by zero - switch op { - case token.QUO, token.REM: - if r.t.isIdeal() { - if (r.t.isInteger() && r.asIdealInt()().Sign() == 0) || - (r.t.isFloat() && r.asIdealFloat()().Sign() == 0) { - a.diag("divide by zero") - return nil - } - } - } - - // Compile - expr := a.newExpr(t, desc) - switch op { - case token.ADD: - expr.genBinOpAdd(l, r) - - case token.SUB: - expr.genBinOpSub(l, r) - - case token.MUL: - expr.genBinOpMul(l, r) - - case token.QUO: - expr.genBinOpQuo(l, r) - - case token.REM: - expr.genBinOpRem(l, r) - - case token.AND: - expr.genBinOpAnd(l, r) - - case token.OR: - expr.genBinOpOr(l, r) - - case token.XOR: - expr.genBinOpXor(l, r) - - case token.AND_NOT: - expr.genBinOpAndNot(l, r) - - case token.SHL: - if l.t.isIdeal() { - lv := l.asIdealInt()() - rv := r.asIdealInt()() - const maxShift = 99999 - if rv.Cmp(big.NewInt(maxShift)) > 0 { - a.diag("left shift by %v; exceeds implementation limit of %v", rv, maxShift) - expr.t = nil - return nil - } - val := new(big.Int).Lsh(lv, uint(rv.Int64())) - expr.eval = func() *big.Int { return val } - } else { - expr.genBinOpShl(l, r) - } - - case token.SHR: - if l.t.isIdeal() { - lv := l.asIdealInt()() - rv := r.asIdealInt()() - val := new(big.Int).Rsh(lv, uint(rv.Int64())) - expr.eval = func() *big.Int { return val } - } else { - expr.genBinOpShr(l, r) - } - - case token.LSS: - expr.genBinOpLss(l, r) - - case token.GTR: - expr.genBinOpGtr(l, r) - - case token.LEQ: - expr.genBinOpLeq(l, r) - - case token.GEQ: - expr.genBinOpGeq(l, r) - - case token.EQL: - expr.genBinOpEql(l, r) - - case token.NEQ: - expr.genBinOpNeq(l, r) - - case token.LAND: - expr.genBinOpLogAnd(l, r) - - case token.LOR: - expr.genBinOpLogOr(l, r) - - default: - log.Panicf("Compilation of binary op %v not implemented", op) - } - - return expr -} - -// TODO(austin) This is a hack to eliminate a circular dependency -// between type.go and expr.go -func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) { - lenExpr := a.compileExpr(b, true, expr) - if lenExpr == nil { - return 0, false - } - - // XXX(Spec) Are ideal floats with no fractional part okay? - if lenExpr.t.isIdeal() { - lenExpr = lenExpr.convertTo(IntType) - if lenExpr == nil { - return 0, false - } - } - - if !lenExpr.t.isInteger() { - a.diagAt(expr.Pos(), "array size must be an integer") - return 0, false - } - - switch lenExpr.t.lit().(type) { - case *intType: - return lenExpr.asInt()(nil), true - case *uintType: - return int64(lenExpr.asUint()(nil)), true - } - log.Panicf("unexpected integer type %T", lenExpr.t) - return 0, false -} - -func (a *compiler) compileExpr(b *block, constant bool, expr ast.Expr) *expr { - ec := &exprCompiler{a, b, constant} - nerr := a.numError() - e := ec.compile(expr, false) - if e == nil && nerr == a.numError() { - log.Panicf("expression compilation failed without reporting errors") - } - return e -} - -// extractEffect separates out any effects that the expression may -// have, returning a function that will perform those effects and a -// new exprCompiler that is guaranteed to be side-effect free. These -// are the moral equivalents of "temp := expr" and "temp" (or "temp := -// &expr" and "*temp" for addressable exprs). Because this creates a -// temporary variable, the caller should create a temporary block for -// the compilation of this expression and the evaluation of the -// results. -func (a *expr) extractEffect(b *block, errOp string) (func(*Thread), *expr) { - // Create "&a" if a is addressable - rhs := a - if a.evalAddr != nil { - rhs = a.compileUnaryExpr(token.AND, rhs) - } - - // Create temp - ac, ok := a.checkAssign(a.pos, []*expr{rhs}, errOp, "") - if !ok { - return nil, nil - } - if len(ac.rmt.Elems) != 1 { - a.diag("multi-valued expression not allowed in %s", errOp) - return nil, nil - } - tempType := ac.rmt.Elems[0] - if tempType.isIdeal() { - // It's too bad we have to duplicate this rule. - switch { - case tempType.isInteger(): - tempType = IntType - case tempType.isFloat(): - tempType = Float64Type - default: - log.Panicf("unexpected ideal type %v", tempType) - } - } - temp := b.DefineTemp(tempType) - tempIdx := temp.Index - - // Create "temp := rhs" - assign := ac.compile(b, tempType) - if assign == nil { - log.Panicf("compileAssign type check failed") - } - - effect := func(t *Thread) { - tempVal := tempType.Zero() - t.f.Vars[tempIdx] = tempVal - assign(tempVal, t) - } - - // Generate "temp" or "*temp" - getTemp := a.compileVariable(0, temp) - if a.evalAddr == nil { - return effect, getTemp - } - - deref := a.compileStarExpr(getTemp) - if deref == nil { - return nil, nil - } - return effect, deref -} diff --git a/src/pkg/exp/eval/expr1.go b/src/pkg/exp/eval/expr1.go deleted file mode 100755 index 5d0e50000..000000000 --- a/src/pkg/exp/eval/expr1.go +++ /dev/null @@ -1,1874 +0,0 @@ -// This file is machine generated by gen.go. -// 6g gen.go && 6l gen.6 && ./6.out >expr1.go - -package eval - -import ( - "big" - "log" -) - -/* - * "As" functions. These retrieve evaluator functions from an - * expr, panicking if the requested evaluator has the wrong type. - */ -func (a *expr) asBool() func(*Thread) bool { - return a.eval.(func(*Thread) bool) -} -func (a *expr) asUint() func(*Thread) uint64 { - return a.eval.(func(*Thread) uint64) -} -func (a *expr) asInt() func(*Thread) int64 { - return a.eval.(func(*Thread) int64) -} -func (a *expr) asIdealInt() func() *big.Int { - return a.eval.(func() *big.Int) -} -func (a *expr) asFloat() func(*Thread) float64 { - return a.eval.(func(*Thread) float64) -} -func (a *expr) asIdealFloat() func() *big.Rat { - return a.eval.(func() *big.Rat) -} -func (a *expr) asString() func(*Thread) string { - return a.eval.(func(*Thread) string) -} -func (a *expr) asArray() func(*Thread) ArrayValue { - return a.eval.(func(*Thread) ArrayValue) -} -func (a *expr) asStruct() func(*Thread) StructValue { - return a.eval.(func(*Thread) StructValue) -} -func (a *expr) asPtr() func(*Thread) Value { - return a.eval.(func(*Thread) Value) -} -func (a *expr) asFunc() func(*Thread) Func { - return a.eval.(func(*Thread) Func) -} -func (a *expr) asSlice() func(*Thread) Slice { - return a.eval.(func(*Thread) Slice) -} -func (a *expr) asMap() func(*Thread) Map { - return a.eval.(func(*Thread) Map) -} -func (a *expr) asMulti() func(*Thread) []Value { - return a.eval.(func(*Thread) []Value) -} - -func (a *expr) asInterface() func(*Thread) interface{} { - switch sf := a.eval.(type) { - case func(t *Thread) bool: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) uint64: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) int64: - return func(t *Thread) interface{} { return sf(t) } - case func() *big.Int: - return func(*Thread) interface{} { return sf() } - case func(t *Thread) float64: - return func(t *Thread) interface{} { return sf(t) } - case func() *big.Rat: - return func(*Thread) interface{} { return sf() } - case func(t *Thread) string: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) ArrayValue: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) StructValue: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) Value: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) Func: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) Slice: - return func(t *Thread) interface{} { return sf(t) } - case func(t *Thread) Map: - return func(t *Thread) interface{} { return sf(t) } - default: - log.Panicf("unexpected expression node type %T at %v", a.eval, a.pos) - } - panic("fail") -} - -/* - * Operator generators. - */ - -func (a *expr) genConstant(v Value) { - switch a.t.lit().(type) { - case *boolType: - a.eval = func(t *Thread) bool { return v.(BoolValue).Get(t) } - case *uintType: - a.eval = func(t *Thread) uint64 { return v.(UintValue).Get(t) } - case *intType: - a.eval = func(t *Thread) int64 { return v.(IntValue).Get(t) } - case *idealIntType: - val := v.(IdealIntValue).Get() - a.eval = func() *big.Int { return val } - case *floatType: - a.eval = func(t *Thread) float64 { return v.(FloatValue).Get(t) } - case *idealFloatType: - val := v.(IdealFloatValue).Get() - a.eval = func() *big.Rat { return val } - case *stringType: - a.eval = func(t *Thread) string { return v.(StringValue).Get(t) } - case *ArrayType: - a.eval = func(t *Thread) ArrayValue { return v.(ArrayValue).Get(t) } - case *StructType: - a.eval = func(t *Thread) StructValue { return v.(StructValue).Get(t) } - case *PtrType: - a.eval = func(t *Thread) Value { return v.(PtrValue).Get(t) } - case *FuncType: - a.eval = func(t *Thread) Func { return v.(FuncValue).Get(t) } - case *SliceType: - a.eval = func(t *Thread) Slice { return v.(SliceValue).Get(t) } - case *MapType: - a.eval = func(t *Thread) Map { return v.(MapValue).Get(t) } - default: - log.Panicf("unexpected constant type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genIdentOp(level, index int) { - a.evalAddr = func(t *Thread) Value { return t.f.Get(level, index) } - switch a.t.lit().(type) { - case *boolType: - a.eval = func(t *Thread) bool { return t.f.Get(level, index).(BoolValue).Get(t) } - case *uintType: - a.eval = func(t *Thread) uint64 { return t.f.Get(level, index).(UintValue).Get(t) } - case *intType: - a.eval = func(t *Thread) int64 { return t.f.Get(level, index).(IntValue).Get(t) } - case *floatType: - a.eval = func(t *Thread) float64 { return t.f.Get(level, index).(FloatValue).Get(t) } - case *stringType: - a.eval = func(t *Thread) string { return t.f.Get(level, index).(StringValue).Get(t) } - case *ArrayType: - a.eval = func(t *Thread) ArrayValue { return t.f.Get(level, index).(ArrayValue).Get(t) } - case *StructType: - a.eval = func(t *Thread) StructValue { return t.f.Get(level, index).(StructValue).Get(t) } - case *PtrType: - a.eval = func(t *Thread) Value { return t.f.Get(level, index).(PtrValue).Get(t) } - case *FuncType: - a.eval = func(t *Thread) Func { return t.f.Get(level, index).(FuncValue).Get(t) } - case *SliceType: - a.eval = func(t *Thread) Slice { return t.f.Get(level, index).(SliceValue).Get(t) } - case *MapType: - a.eval = func(t *Thread) Map { return t.f.Get(level, index).(MapValue).Get(t) } - default: - log.Panicf("unexpected identifier type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genFuncCall(call func(t *Thread) []Value) { - a.exec = func(t *Thread) { call(t) } - switch a.t.lit().(type) { - case *boolType: - a.eval = func(t *Thread) bool { return call(t)[0].(BoolValue).Get(t) } - case *uintType: - a.eval = func(t *Thread) uint64 { return call(t)[0].(UintValue).Get(t) } - case *intType: - a.eval = func(t *Thread) int64 { return call(t)[0].(IntValue).Get(t) } - case *floatType: - a.eval = func(t *Thread) float64 { return call(t)[0].(FloatValue).Get(t) } - case *stringType: - a.eval = func(t *Thread) string { return call(t)[0].(StringValue).Get(t) } - case *ArrayType: - a.eval = func(t *Thread) ArrayValue { return call(t)[0].(ArrayValue).Get(t) } - case *StructType: - a.eval = func(t *Thread) StructValue { return call(t)[0].(StructValue).Get(t) } - case *PtrType: - a.eval = func(t *Thread) Value { return call(t)[0].(PtrValue).Get(t) } - case *FuncType: - a.eval = func(t *Thread) Func { return call(t)[0].(FuncValue).Get(t) } - case *SliceType: - a.eval = func(t *Thread) Slice { return call(t)[0].(SliceValue).Get(t) } - case *MapType: - a.eval = func(t *Thread) Map { return call(t)[0].(MapValue).Get(t) } - case *MultiType: - a.eval = func(t *Thread) []Value { return call(t) } - default: - log.Panicf("unexpected result type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genValue(vf func(*Thread) Value) { - a.evalAddr = vf - switch a.t.lit().(type) { - case *boolType: - a.eval = func(t *Thread) bool { return vf(t).(BoolValue).Get(t) } - case *uintType: - a.eval = func(t *Thread) uint64 { return vf(t).(UintValue).Get(t) } - case *intType: - a.eval = func(t *Thread) int64 { return vf(t).(IntValue).Get(t) } - case *floatType: - a.eval = func(t *Thread) float64 { return vf(t).(FloatValue).Get(t) } - case *stringType: - a.eval = func(t *Thread) string { return vf(t).(StringValue).Get(t) } - case *ArrayType: - a.eval = func(t *Thread) ArrayValue { return vf(t).(ArrayValue).Get(t) } - case *StructType: - a.eval = func(t *Thread) StructValue { return vf(t).(StructValue).Get(t) } - case *PtrType: - a.eval = func(t *Thread) Value { return vf(t).(PtrValue).Get(t) } - case *FuncType: - a.eval = func(t *Thread) Func { return vf(t).(FuncValue).Get(t) } - case *SliceType: - a.eval = func(t *Thread) Slice { return vf(t).(SliceValue).Get(t) } - case *MapType: - a.eval = func(t *Thread) Map { return vf(t).(MapValue).Get(t) } - default: - log.Panicf("unexpected result type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genUnaryOpNeg(v *expr) { - switch a.t.lit().(type) { - case *uintType: - vf := v.asUint() - a.eval = func(t *Thread) uint64 { v := vf(t); return -v } - case *intType: - vf := v.asInt() - a.eval = func(t *Thread) int64 { v := vf(t); return -v } - case *idealIntType: - val := v.asIdealInt()() - val.Neg(val) - a.eval = func() *big.Int { return val } - case *floatType: - vf := v.asFloat() - a.eval = func(t *Thread) float64 { v := vf(t); return -v } - case *idealFloatType: - val := v.asIdealFloat()() - val.Neg(val) - a.eval = func() *big.Rat { return val } - default: - log.Panicf("unexpected type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genUnaryOpNot(v *expr) { - switch a.t.lit().(type) { - case *boolType: - vf := v.asBool() - a.eval = func(t *Thread) bool { v := vf(t); return !v } - default: - log.Panicf("unexpected type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genUnaryOpXor(v *expr) { - switch a.t.lit().(type) { - case *uintType: - vf := v.asUint() - a.eval = func(t *Thread) uint64 { v := vf(t); return ^v } - case *intType: - vf := v.asInt() - a.eval = func(t *Thread) int64 { v := vf(t); return ^v } - case *idealIntType: - val := v.asIdealInt()() - val.Not(val) - a.eval = func() *big.Int { return val } - default: - log.Panicf("unexpected type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genBinOpLogAnd(l, r *expr) { - lf := l.asBool() - rf := r.asBool() - a.eval = func(t *Thread) bool { return lf(t) && rf(t) } -} - -func (a *expr) genBinOpLogOr(l, r *expr) { - lf := l.asBool() - rf := r.asBool() - a.eval = func(t *Thread) bool { return lf(t) || rf(t) } -} - -func (a *expr) genBinOpAdd(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l + r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l + r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l + r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l + r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l + r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l + r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l + r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l + r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l + r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l + r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Add(l, r) - a.eval = func() *big.Int { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - switch t.Bits { - case 32: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - ret = l + r - return float64(float32(ret)) - } - case 64: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - ret = l + r - return float64(float64(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Add(l, r) - a.eval = func() *big.Rat { return val } - case *stringType: - lf := l.asString() - rf := r.asString() - a.eval = func(t *Thread) string { - l, r := lf(t), rf(t) - return l + r - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpSub(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l - r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l - r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l - r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l - r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l - r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l - r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l - r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l - r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l - r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l - r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Sub(l, r) - a.eval = func() *big.Int { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - switch t.Bits { - case 32: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - ret = l - r - return float64(float32(ret)) - } - case 64: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - ret = l - r - return float64(float64(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Sub(l, r) - a.eval = func() *big.Rat { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpMul(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l * r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l * r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l * r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l * r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l * r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l * r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l * r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l * r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l * r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l * r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Mul(l, r) - a.eval = func() *big.Int { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - switch t.Bits { - case 32: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - ret = l * r - return float64(float32(ret)) - } - case 64: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - ret = l * r - return float64(float64(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Mul(l, r) - a.eval = func() *big.Rat { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpQuo(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Quo(l, r) - a.eval = func() *big.Int { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - switch t.Bits { - case 32: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return float64(float32(ret)) - } - case 64: - a.eval = func(t *Thread) float64 { - l, r := lf(t), rf(t) - var ret float64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l / r - return float64(float64(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Quo(l, r) - a.eval = func() *big.Rat { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpRem(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - if r == 0 { - t.Abort(DivByZeroError{}) - } - ret = l % r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Rem(l, r) - a.eval = func() *big.Int { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpAnd(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l & r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l & r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l & r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l & r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l & r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l & r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l & r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l & r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l & r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l & r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.And(l, r) - a.eval = func() *big.Int { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpOr(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l | r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l | r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l | r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l | r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l | r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l | r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l | r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l | r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l | r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l | r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Or(l, r) - a.eval = func() *big.Int { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpXor(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l ^ r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l ^ r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l ^ r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l ^ r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l ^ r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l ^ r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l ^ r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l ^ r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l ^ r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l ^ r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Xor(l, r) - a.eval = func() *big.Int { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpAndNot(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l &^ r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l &^ r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l &^ r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l &^ r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l &^ r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asInt() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l &^ r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l &^ r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l &^ r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l &^ r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l &^ r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.AndNot(l, r) - a.eval = func() *big.Int { return val } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpShl(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l << r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l << r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l << r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l << r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l << r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l << r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l << r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l << r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l << r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l << r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpShr(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l >> r - return uint64(uint8(ret)) - } - case 16: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l >> r - return uint64(uint16(ret)) - } - case 32: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l >> r - return uint64(uint32(ret)) - } - case 64: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l >> r - return uint64(uint64(ret)) - } - case 0: - a.eval = func(t *Thread) uint64 { - l, r := lf(t), rf(t) - var ret uint64 - ret = l >> r - return uint64(uint(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - case *intType: - lf := l.asInt() - rf := r.asUint() - switch t.Bits { - case 8: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l >> r - return int64(int8(ret)) - } - case 16: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l >> r - return int64(int16(ret)) - } - case 32: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l >> r - return int64(int32(ret)) - } - case 64: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l >> r - return int64(int64(ret)) - } - case 0: - a.eval = func(t *Thread) int64 { - l, r := lf(t), rf(t) - var ret int64 - ret = l >> r - return int64(int(ret)) - } - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpLss(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l < r - } - case *intType: - lf := l.asInt() - rf := r.asInt() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l < r - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Cmp(r) < 0 - a.eval = func(t *Thread) bool { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l < r - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Cmp(r) < 0 - a.eval = func(t *Thread) bool { return val } - case *stringType: - lf := l.asString() - rf := r.asString() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l < r - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpGtr(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l > r - } - case *intType: - lf := l.asInt() - rf := r.asInt() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l > r - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Cmp(r) > 0 - a.eval = func(t *Thread) bool { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l > r - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Cmp(r) > 0 - a.eval = func(t *Thread) bool { return val } - case *stringType: - lf := l.asString() - rf := r.asString() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l > r - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpLeq(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l <= r - } - case *intType: - lf := l.asInt() - rf := r.asInt() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l <= r - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Cmp(r) <= 0 - a.eval = func(t *Thread) bool { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l <= r - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Cmp(r) <= 0 - a.eval = func(t *Thread) bool { return val } - case *stringType: - lf := l.asString() - rf := r.asString() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l <= r - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpGeq(l, r *expr) { - switch t := l.t.lit().(type) { - case *uintType: - lf := l.asUint() - rf := r.asUint() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l >= r - } - case *intType: - lf := l.asInt() - rf := r.asInt() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l >= r - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Cmp(r) >= 0 - a.eval = func(t *Thread) bool { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l >= r - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Cmp(r) >= 0 - a.eval = func(t *Thread) bool { return val } - case *stringType: - lf := l.asString() - rf := r.asString() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l >= r - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpEql(l, r *expr) { - switch t := l.t.lit().(type) { - case *boolType: - lf := l.asBool() - rf := r.asBool() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - case *uintType: - lf := l.asUint() - rf := r.asUint() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - case *intType: - lf := l.asInt() - rf := r.asInt() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Cmp(r) == 0 - a.eval = func(t *Thread) bool { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Cmp(r) == 0 - a.eval = func(t *Thread) bool { return val } - case *stringType: - lf := l.asString() - rf := r.asString() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - case *PtrType: - lf := l.asPtr() - rf := r.asPtr() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - case *FuncType: - lf := l.asFunc() - rf := r.asFunc() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - case *MapType: - lf := l.asMap() - rf := r.asMap() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l == r - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func (a *expr) genBinOpNeq(l, r *expr) { - switch t := l.t.lit().(type) { - case *boolType: - lf := l.asBool() - rf := r.asBool() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - case *uintType: - lf := l.asUint() - rf := r.asUint() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - case *intType: - lf := l.asInt() - rf := r.asInt() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - case *idealIntType: - l := l.asIdealInt()() - r := r.asIdealInt()() - val := l.Cmp(r) != 0 - a.eval = func(t *Thread) bool { return val } - case *floatType: - lf := l.asFloat() - rf := r.asFloat() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - case *idealFloatType: - l := l.asIdealFloat()() - r := r.asIdealFloat()() - val := l.Cmp(r) != 0 - a.eval = func(t *Thread) bool { return val } - case *stringType: - lf := l.asString() - rf := r.asString() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - case *PtrType: - lf := l.asPtr() - rf := r.asPtr() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - case *FuncType: - lf := l.asFunc() - rf := r.asFunc() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - case *MapType: - lf := l.asMap() - rf := r.asMap() - a.eval = func(t *Thread) bool { - l, r := lf(t), rf(t) - return l != r - } - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -func genAssign(lt Type, r *expr) func(lv Value, t *Thread) { - switch lt.lit().(type) { - case *boolType: - rf := r.asBool() - return func(lv Value, t *Thread) { lv.(BoolValue).Set(t, rf(t)) } - case *uintType: - rf := r.asUint() - return func(lv Value, t *Thread) { lv.(UintValue).Set(t, rf(t)) } - case *intType: - rf := r.asInt() - return func(lv Value, t *Thread) { lv.(IntValue).Set(t, rf(t)) } - case *floatType: - rf := r.asFloat() - return func(lv Value, t *Thread) { lv.(FloatValue).Set(t, rf(t)) } - case *stringType: - rf := r.asString() - return func(lv Value, t *Thread) { lv.(StringValue).Set(t, rf(t)) } - case *ArrayType: - rf := r.asArray() - return func(lv Value, t *Thread) { lv.Assign(t, rf(t)) } - case *StructType: - rf := r.asStruct() - return func(lv Value, t *Thread) { lv.Assign(t, rf(t)) } - case *PtrType: - rf := r.asPtr() - return func(lv Value, t *Thread) { lv.(PtrValue).Set(t, rf(t)) } - case *FuncType: - rf := r.asFunc() - return func(lv Value, t *Thread) { lv.(FuncValue).Set(t, rf(t)) } - case *SliceType: - rf := r.asSlice() - return func(lv Value, t *Thread) { lv.(SliceValue).Set(t, rf(t)) } - case *MapType: - rf := r.asMap() - return func(lv Value, t *Thread) { lv.(MapValue).Set(t, rf(t)) } - default: - log.Panicf("unexpected left operand type %v at %v", lt, r.pos) - } - panic("fail") -} diff --git a/src/pkg/exp/eval/expr_test.go b/src/pkg/exp/eval/expr_test.go deleted file mode 100644 index 0dbce4315..000000000 --- a/src/pkg/exp/eval/expr_test.go +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "big" - "testing" -) - -var undefined = "undefined" -var typeAsExpr = "type .* used as expression" -var badCharLit = "character literal" -var unknownEscape = "unknown escape sequence" -var opTypes = "illegal (operand|argument) type|cannot index into" -var badAddrOf = "cannot take the address" -var constantTruncated = "constant [^ ]* truncated" -var constantUnderflows = "constant [^ ]* underflows" -var constantOverflows = "constant [^ ]* overflows" -var implLimit = "implementation limit" -var mustBeUnsigned = "must be unsigned" -var divByZero = "divide by zero" - -var hugeInteger = new(big.Int).Lsh(idealOne, 64) - -var exprTests = []test{ - Val("i", 1), - CErr("zzz", undefined), - // TODO(austin) Test variable in constant context - //CErr("t", typeAsExpr), - - Val("'a'", big.NewInt('a')), - Val("'\\uffff'", big.NewInt('\uffff')), - Val("'\\n'", big.NewInt('\n')), - CErr("''+x", badCharLit), - // Produces two parse errors - //CErr("'''", ""), - CErr("'\n'", badCharLit), - CErr("'\\z'", unknownEscape), - CErr("'ab'", badCharLit), - - Val("1.0", big.NewRat(1, 1)), - Val("1.", big.NewRat(1, 1)), - Val(".1", big.NewRat(1, 10)), - Val("1e2", big.NewRat(100, 1)), - - Val("\"abc\"", "abc"), - Val("\"\"", ""), - Val("\"\\n\\\"\"", "\n\""), - CErr("\"\\z\"", unknownEscape), - CErr("\"abc", "string not terminated"), - - Val("(i)", 1), - - Val("ai[0]", 1), - Val("(&ai)[0]", 1), - Val("ai[1]", 2), - Val("ai[i]", 2), - Val("ai[u]", 2), - CErr("ai[f]", opTypes), - CErr("ai[0][0]", opTypes), - CErr("ai[2]", "index 2 exceeds"), - CErr("ai[1+1]", "index 2 exceeds"), - CErr("ai[-1]", "negative index"), - RErr("ai[i+i]", "index 2 exceeds"), - RErr("ai[-i]", "negative index"), - CErr("i[0]", opTypes), - CErr("f[0]", opTypes), - - Val("aai[0][0]", 1), - Val("aai[1][1]", 4), - CErr("aai[2][0]", "index 2 exceeds"), - CErr("aai[0][2]", "index 2 exceeds"), - - Val("sli[0]", 1), - Val("sli[1]", 2), - CErr("sli[-1]", "negative index"), - RErr("sli[-i]", "negative index"), - RErr("sli[2]", "index 2 exceeds"), - - Val("s[0]", uint8('a')), - Val("s[1]", uint8('b')), - CErr("s[-1]", "negative index"), - RErr("s[-i]", "negative index"), - RErr("s[3]", "index 3 exceeds"), - - Val("ai[0:2]", vslice{varray{1, 2}, 2, 2}), - Val("ai[0:1]", vslice{varray{1, 2}, 1, 2}), - Val("ai[0:]", vslice{varray{1, 2}, 2, 2}), - Val("ai[i:]", vslice{varray{2}, 1, 1}), - - Val("sli[0:2]", vslice{varray{1, 2, 3}, 2, 3}), - Val("sli[0:i]", vslice{varray{1, 2, 3}, 1, 3}), - Val("sli[1:]", vslice{varray{2, 3}, 1, 2}), - - CErr("1(2)", "cannot call"), - CErr("fn(1,2)", "too many"), - CErr("fn()", "not enough"), - CErr("fn(true)", opTypes), - CErr("fn(true)", "function call"), - // Single argument functions don't say which argument. - //CErr("fn(true)", "argument 1"), - Val("fn(1)", 2), - Val("fn(1.0)", 2), - CErr("fn(1.5)", constantTruncated), - Val("fn(i)", 2), - CErr("fn(u)", opTypes), - - CErr("void()+2", opTypes), - CErr("oneTwo()+2", opTypes), - - Val("cap(ai)", 2), - Val("cap(&ai)", 2), - Val("cap(aai)", 2), - Val("cap(sli)", 3), - CErr("cap(0)", opTypes), - CErr("cap(i)", opTypes), - CErr("cap(s)", opTypes), - - Val("len(s)", 3), - Val("len(ai)", 2), - Val("len(&ai)", 2), - Val("len(ai[0:])", 2), - Val("len(ai[1:])", 1), - Val("len(ai[2:])", 0), - Val("len(aai)", 2), - Val("len(sli)", 2), - Val("len(sli[0:])", 2), - Val("len(sli[1:])", 1), - Val("len(sli[2:])", 0), - // TODO(austin) Test len of map - CErr("len(0)", opTypes), - CErr("len(i)", opTypes), - - CErr("*i", opTypes), - Val("*&i", 1), - Val("*&(i)", 1), - CErr("&1", badAddrOf), - CErr("&c", badAddrOf), - Val("*(&ai[0])", 1), - - Val("+1", big.NewInt(+1)), - Val("+1.0", big.NewRat(1, 1)), - Val("01.5", big.NewRat(15, 10)), - CErr("+\"x\"", opTypes), - - Val("-42", big.NewInt(-42)), - Val("-i", -1), - Val("-f", -1.0), - // 6g bug? - //Val("-(f-1)", -0.0), - CErr("-\"x\"", opTypes), - - // TODO(austin) Test unary ! - - Val("^2", big.NewInt(^2)), - Val("^(-2)", big.NewInt(^(-2))), - CErr("^2.0", opTypes), - CErr("^2.5", opTypes), - Val("^i", ^1), - Val("^u", ^uint(1)), - CErr("^f", opTypes), - - Val("1+i", 2), - Val("1+u", uint(2)), - Val("3.0+i", 4), - Val("1+1", big.NewInt(2)), - Val("f+f", 2.0), - Val("1+f", 2.0), - Val("1.0+1", big.NewRat(2, 1)), - Val("\"abc\" + \"def\"", "abcdef"), - CErr("i+u", opTypes), - CErr("-1+u", constantUnderflows), - // TODO(austin) Test named types - - Val("2-1", big.NewInt(1)), - Val("2.0-1", big.NewRat(1, 1)), - Val("f-2", -1.0), - Val("-0.0", big.NewRat(0, 1)), - Val("2*2", big.NewInt(4)), - Val("2*i", 2), - Val("3/2", big.NewInt(1)), - Val("3/i", 3), - CErr("1/0", divByZero), - CErr("1.0/0", divByZero), - RErr("i/0", divByZero), - Val("3%2", big.NewInt(1)), - Val("i%2", 1), - CErr("3%0", divByZero), - CErr("3.0%0", opTypes), - RErr("i%0", divByZero), - - // Examples from "Arithmetic operators" - Val("5/3", big.NewInt(1)), - Val("(i+4)/(i+2)", 1), - Val("5%3", big.NewInt(2)), - Val("(i+4)%(i+2)", 2), - Val("-5/3", big.NewInt(-1)), - Val("(i-6)/(i+2)", -1), - Val("-5%3", big.NewInt(-2)), - Val("(i-6)%(i+2)", -2), - Val("5/-3", big.NewInt(-1)), - Val("(i+4)/(i-4)", -1), - Val("5%-3", big.NewInt(2)), - Val("(i+4)%(i-4)", 2), - Val("-5/-3", big.NewInt(1)), - Val("(i-6)/(i-4)", 1), - Val("-5%-3", big.NewInt(-2)), - Val("(i-6)%(i-4)", -2), - - // Examples from "Arithmetic operators" - Val("11/4", big.NewInt(2)), - Val("(i+10)/4", 2), - Val("11%4", big.NewInt(3)), - Val("(i+10)%4", 3), - Val("11>>2", big.NewInt(2)), - Val("(i+10)>>2", 2), - Val("11&3", big.NewInt(3)), - Val("(i+10)&3", 3), - Val("-11/4", big.NewInt(-2)), - Val("(i-12)/4", -2), - Val("-11%4", big.NewInt(-3)), - Val("(i-12)%4", -3), - Val("-11>>2", big.NewInt(-3)), - Val("(i-12)>>2", -3), - Val("-11&3", big.NewInt(1)), - Val("(i-12)&3", 1), - - // TODO(austin) Test bit ops - - // For shift, we try nearly every combination of positive - // ideal int, negative ideal int, big ideal int, ideal - // fractional float, ideal non-fractional float, int, uint, - // and float. - Val("2<<2", big.NewInt(2<<2)), - CErr("2<<(-1)", constantUnderflows), - CErr("2<<0x10000000000000000", constantOverflows), - CErr("2<<2.5", constantTruncated), - Val("2<<2.0", big.NewInt(2<<2.0)), - CErr("2<<i", mustBeUnsigned), - Val("2<<u", 2<<1), - CErr("2<<f", opTypes), - - Val("-2<<2", big.NewInt(-2<<2)), - CErr("-2<<(-1)", constantUnderflows), - CErr("-2<<0x10000000000000000", constantOverflows), - CErr("-2<<2.5", constantTruncated), - Val("-2<<2.0", big.NewInt(-2<<2.0)), - CErr("-2<<i", mustBeUnsigned), - Val("-2<<u", -2<<1), - CErr("-2<<f", opTypes), - - Val("0x10000000000000000<<2", new(big.Int).Lsh(hugeInteger, 2)), - CErr("0x10000000000000000<<(-1)", constantUnderflows), - CErr("0x10000000000000000<<0x10000000000000000", constantOverflows), - CErr("0x10000000000000000<<2.5", constantTruncated), - Val("0x10000000000000000<<2.0", new(big.Int).Lsh(hugeInteger, 2)), - CErr("0x10000000000000000<<i", mustBeUnsigned), - CErr("0x10000000000000000<<u", constantOverflows), - CErr("0x10000000000000000<<f", opTypes), - - CErr("2.5<<2", opTypes), - CErr("2.0<<2", opTypes), - - Val("i<<2", 1<<2), - CErr("i<<(-1)", constantUnderflows), - CErr("i<<0x10000000000000000", constantOverflows), - CErr("i<<2.5", constantTruncated), - Val("i<<2.0", 1<<2), - CErr("i<<i", mustBeUnsigned), - Val("i<<u", 1<<1), - CErr("i<<f", opTypes), - Val("i<<u", 1<<1), - - Val("u<<2", uint(1<<2)), - CErr("u<<(-1)", constantUnderflows), - CErr("u<<0x10000000000000000", constantOverflows), - CErr("u<<2.5", constantTruncated), - Val("u<<2.0", uint(1<<2)), - CErr("u<<i", mustBeUnsigned), - Val("u<<u", uint(1<<1)), - CErr("u<<f", opTypes), - Val("u<<u", uint(1<<1)), - - CErr("f<<2", opTypes), - - // <, <=, >, >= - Val("1<2", 1 < 2), - Val("1<=2", 1 <= 2), - Val("2<=2", 2 <= 2), - Val("1>2", 1 > 2), - Val("1>=2", 1 >= 2), - Val("2>=2", 2 >= 2), - - Val("i<2", 1 < 2), - Val("i<=2", 1 <= 2), - Val("i+1<=2", 2 <= 2), - Val("i>2", 1 > 2), - Val("i>=2", 1 >= 2), - Val("i+1>=2", 2 >= 2), - - Val("u<2", 1 < 2), - Val("f<2", 1 < 2), - - Val("s<\"b\"", true), - Val("s<\"a\"", false), - Val("s<=\"abc\"", true), - Val("s>\"aa\"", true), - Val("s>\"ac\"", false), - Val("s>=\"abc\"", true), - - CErr("i<u", opTypes), - CErr("i<f", opTypes), - CErr("i<s", opTypes), - CErr("&i<&i", opTypes), - CErr("ai<ai", opTypes), - - // ==, != - Val("1==1", true), - Val("1!=1", false), - Val("1==2", false), - Val("1!=2", true), - - Val("1.0==1", true), - Val("1.5==1", false), - - Val("i==1", true), - Val("i!=1", false), - Val("i==2", false), - Val("i!=2", true), - - Val("u==1", true), - Val("f==1", true), - - Val("s==\"abc\"", true), - Val("s!=\"abc\"", false), - Val("s==\"abcd\"", false), - Val("s!=\"abcd\"", true), - - Val("&i==&i", true), - Val("&i==&i2", false), - - Val("fn==fn", true), - Val("fn==func(int)int{return 0}", false), - - CErr("i==u", opTypes), - CErr("i==f", opTypes), - CErr("&i==&f", opTypes), - CErr("ai==ai", opTypes), - CErr("t==t", opTypes), - CErr("fn==oneTwo", opTypes), -} - -func TestExpr(t *testing.T) { runTests(t, "exprTests", exprTests) } diff --git a/src/pkg/exp/eval/func.go b/src/pkg/exp/eval/func.go deleted file mode 100644 index cb1b579e4..000000000 --- a/src/pkg/exp/eval/func.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2009 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. - -package eval - -import "os" - -/* - * Virtual machine - */ - -type Thread struct { - abort chan os.Error - pc uint - // The execution frame of this function. This remains the - // same throughout a function invocation. - f *Frame -} - -type code []func(*Thread) - -func (i code) exec(t *Thread) { - opc := t.pc - t.pc = 0 - l := uint(len(i)) - for t.pc < l { - pc := t.pc - t.pc++ - i[pc](t) - } - t.pc = opc -} - -/* - * Code buffer - */ - -type codeBuf struct { - instrs code -} - -func newCodeBuf() *codeBuf { return &codeBuf{make(code, 0, 16)} } - -func (b *codeBuf) push(instr func(*Thread)) { - b.instrs = append(b.instrs, instr) -} - -func (b *codeBuf) nextPC() uint { return uint(len(b.instrs)) } - -func (b *codeBuf) get() code { - // Freeze this buffer into an array of exactly the right size - a := make(code, len(b.instrs)) - copy(a, b.instrs) - return code(a) -} - -/* - * User-defined functions - */ - -type evalFunc struct { - outer *Frame - frameSize int - code code -} - -func (f *evalFunc) NewFrame() *Frame { return f.outer.child(f.frameSize) } - -func (f *evalFunc) Call(t *Thread) { f.code.exec(t) } diff --git a/src/pkg/exp/eval/gen.go b/src/pkg/exp/eval/gen.go deleted file mode 100644 index 1e00bdcd0..000000000 --- a/src/pkg/exp/eval/gen.go +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2009 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. - -package main - -// generate operator implementations - -import ( - "log" - "os" - "template" -) - -type Op struct { - Name string - Expr string - Body string // overrides Expr - ConstExpr string - AsRightName string - ReturnType string - Types []*Type -} - -type Size struct { - Bits int - Sized string -} - -type Type struct { - Repr string - Value string - Native string - As string - IsIdeal bool - HasAssign bool - Sizes []Size -} - -var ( - boolType = &Type{Repr: "*boolType", Value: "BoolValue", Native: "bool", As: "asBool"} - uintType = &Type{Repr: "*uintType", Value: "UintValue", Native: "uint64", As: "asUint", - Sizes: []Size{{8, "uint8"}, {16, "uint16"}, {32, "uint32"}, {64, "uint64"}, {0, "uint"}}, - } - intType = &Type{Repr: "*intType", Value: "IntValue", Native: "int64", As: "asInt", - Sizes: []Size{{8, "int8"}, {16, "int16"}, {32, "int32"}, {64, "int64"}, {0, "int"}}, - } - idealIntType = &Type{Repr: "*idealIntType", Value: "IdealIntValue", Native: "*big.Int", As: "asIdealInt", IsIdeal: true} - floatType = &Type{Repr: "*floatType", Value: "FloatValue", Native: "float64", As: "asFloat", - Sizes: []Size{{32, "float32"}, {64, "float64"}}, - } - idealFloatType = &Type{Repr: "*idealFloatType", Value: "IdealFloatValue", Native: "*big.Rat", As: "asIdealFloat", IsIdeal: true} - stringType = &Type{Repr: "*stringType", Value: "StringValue", Native: "string", As: "asString"} - arrayType = &Type{Repr: "*ArrayType", Value: "ArrayValue", Native: "ArrayValue", As: "asArray", HasAssign: true} - structType = &Type{Repr: "*StructType", Value: "StructValue", Native: "StructValue", As: "asStruct", HasAssign: true} - ptrType = &Type{Repr: "*PtrType", Value: "PtrValue", Native: "Value", As: "asPtr"} - funcType = &Type{Repr: "*FuncType", Value: "FuncValue", Native: "Func", As: "asFunc"} - sliceType = &Type{Repr: "*SliceType", Value: "SliceValue", Native: "Slice", As: "asSlice"} - mapType = &Type{Repr: "*MapType", Value: "MapValue", Native: "Map", As: "asMap"} - - all = []*Type{ - boolType, - uintType, - intType, - idealIntType, - floatType, - idealFloatType, - stringType, - arrayType, - structType, - ptrType, - funcType, - sliceType, - mapType, - } - bools = all[0:1] - integers = all[1:4] - shiftable = all[1:3] - numbers = all[1:6] - addable = all[1:7] - cmpable = []*Type{ - boolType, - uintType, - intType, - idealIntType, - floatType, - idealFloatType, - stringType, - ptrType, - funcType, - mapType, - } -) - -var unOps = []Op{ - {Name: "Neg", Expr: "-v", ConstExpr: "val.Neg(val)", Types: numbers}, - {Name: "Not", Expr: "!v", Types: bools}, - {Name: "Xor", Expr: "^v", ConstExpr: "val.Not(val)", Types: integers}, -} - -var binOps = []Op{ - {Name: "Add", Expr: "l + r", ConstExpr: "l.Add(l, r)", Types: addable}, - {Name: "Sub", Expr: "l - r", ConstExpr: "l.Sub(l, r)", Types: numbers}, - {Name: "Mul", Expr: "l * r", ConstExpr: "l.Mul(l, r)", Types: numbers}, - {Name: "Quo", - Body: "if r == 0 { t.Abort(DivByZeroError{}) }; ret = l / r", - ConstExpr: "l.Quo(l, r)", - Types: numbers, - }, - {Name: "Rem", - Body: "if r == 0 { t.Abort(DivByZeroError{}) }; ret = l % r", - ConstExpr: "l.Rem(l, r)", - Types: integers, - }, - {Name: "And", Expr: "l & r", ConstExpr: "l.And(l, r)", Types: integers}, - {Name: "Or", Expr: "l | r", ConstExpr: "l.Or(l, r)", Types: integers}, - {Name: "Xor", Expr: "l ^ r", ConstExpr: "l.Xor(l, r)", Types: integers}, - {Name: "AndNot", Expr: "l &^ r", ConstExpr: "l.AndNot(l, r)", Types: integers}, - {Name: "Shl", Expr: "l << r", ConstExpr: "l.Lsh(l, uint(r.Value()))", - AsRightName: "asUint", Types: shiftable, - }, - {Name: "Shr", Expr: "l >> r", ConstExpr: "new(big.Int).Rsh(l, uint(r.Value()))", - AsRightName: "asUint", Types: shiftable, - }, - {Name: "Lss", Expr: "l < r", ConstExpr: "l.Cmp(r) < 0", ReturnType: "bool", Types: addable}, - {Name: "Gtr", Expr: "l > r", ConstExpr: "l.Cmp(r) > 0", ReturnType: "bool", Types: addable}, - {Name: "Leq", Expr: "l <= r", ConstExpr: "l.Cmp(r) <= 0", ReturnType: "bool", Types: addable}, - {Name: "Geq", Expr: "l >= r", ConstExpr: "l.Cmp(r) >= 0", ReturnType: "bool", Types: addable}, - {Name: "Eql", Expr: "l == r", ConstExpr: "l.Cmp(r) == 0", ReturnType: "bool", Types: cmpable}, - {Name: "Neq", Expr: "l != r", ConstExpr: "l.Cmp(r) != 0", ReturnType: "bool", Types: cmpable}, -} - -type Data struct { - UnaryOps []Op - BinaryOps []Op - Types []*Type -} - -var data = Data{ - unOps, - binOps, - all, -} - -const templateStr = ` -// This file is machine generated by gen.go. -// 6g gen.go && 6l gen.6 && ./6.out >expr1.go - -package eval - -import ( - "big" - "log" -) - -/* - * "As" functions. These retrieve evaluator functions from an - * expr, panicking if the requested evaluator has the wrong type. - */ -«.repeated section Types» -«.section IsIdeal» -func (a *expr) «As»() (func() «Native») { - return a.eval.(func()(«Native»)) -} -«.or» -func (a *expr) «As»() (func(*Thread) «Native») { - return a.eval.(func(*Thread)(«Native»)) -} -«.end» -«.end» -func (a *expr) asMulti() (func(*Thread) []Value) { - return a.eval.(func(*Thread)[]Value) -} - -func (a *expr) asInterface() (func(*Thread) interface{}) { - switch sf := a.eval.(type) { -«.repeated section Types» -«.section IsIdeal» - case func()«Native»: - return func(*Thread) interface{} { return sf() } -«.or» - case func(t *Thread)«Native»: - return func(t *Thread) interface{} { return sf(t) } -«.end» -«.end» - default: - log.Panicf("unexpected expression node type %T at %v", a.eval, a.pos) - } - panic("fail") -} - -/* - * Operator generators. - */ - -func (a *expr) genConstant(v Value) { - switch a.t.lit().(type) { -«.repeated section Types» - case «Repr»: -«.section IsIdeal» - val := v.(«Value»).Get() - a.eval = func() «Native» { return val } -«.or» - a.eval = func(t *Thread) «Native» { return v.(«Value»).Get(t) } -«.end» -«.end» - default: - log.Panicf("unexpected constant type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genIdentOp(level, index int) { - a.evalAddr = func(t *Thread) Value { return t.f.Get(level, index) } - switch a.t.lit().(type) { -«.repeated section Types» -«.section IsIdeal» -«.or» - case «Repr»: - a.eval = func(t *Thread) «Native» { return t.f.Get(level, index).(«Value»).Get(t) } -«.end» -«.end» - default: - log.Panicf("unexpected identifier type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genFuncCall(call func(t *Thread) []Value) { - a.exec = func(t *Thread) { call(t)} - switch a.t.lit().(type) { -«.repeated section Types» -«.section IsIdeal» -«.or» - case «Repr»: - a.eval = func(t *Thread) «Native» { return call(t)[0].(«Value»).Get(t) } -«.end» -«.end» - case *MultiType: - a.eval = func(t *Thread) []Value { return call(t) } - default: - log.Panicf("unexpected result type %v at %v", a.t, a.pos) - } -} - -func (a *expr) genValue(vf func(*Thread) Value) { - a.evalAddr = vf - switch a.t.lit().(type) { -«.repeated section Types» -«.section IsIdeal» -«.or» - case «Repr»: - a.eval = func(t *Thread) «Native» { return vf(t).(«Value»).Get(t) } -«.end» -«.end» - default: - log.Panicf("unexpected result type %v at %v", a.t, a.pos) - } -} - -«.repeated section UnaryOps» -func (a *expr) genUnaryOp«Name»(v *expr) { - switch a.t.lit().(type) { -«.repeated section Types» - case «Repr»: -«.section IsIdeal» - val := v.«As»()() - «ConstExpr» - a.eval = func() «Native» { return val } -«.or» - vf := v.«As»() - a.eval = func(t *Thread) «Native» { v := vf(t); return «Expr» } -«.end» -«.end» - default: - log.Panicf("unexpected type %v at %v", a.t, a.pos) - } -} - -«.end» -func (a *expr) genBinOpLogAnd(l, r *expr) { - lf := l.asBool() - rf := r.asBool() - a.eval = func(t *Thread) bool { return lf(t) && rf(t) } -} - -func (a *expr) genBinOpLogOr(l, r *expr) { - lf := l.asBool() - rf := r.asBool() - a.eval = func(t *Thread) bool { return lf(t) || rf(t) } -} - -«.repeated section BinaryOps» -func (a *expr) genBinOp«Name»(l, r *expr) { - switch t := l.t.lit().(type) { -«.repeated section Types» - case «Repr»: - «.section IsIdeal» - l := l.«As»()() - r := r.«As»()() - val := «ConstExpr» - «.section ReturnType» - a.eval = func(t *Thread) «ReturnType» { return val } - «.or» - a.eval = func() «Native» { return val } - «.end» - «.or» - lf := l.«As»() - rf := r.«.section AsRightName»«@»«.or»«As»«.end»() - «.section ReturnType» - a.eval = func(t *Thread) «@» { - l, r := lf(t), rf(t) - return «Expr» - } - «.or» - «.section Sizes» - switch t.Bits { - «.repeated section @» - case «Bits»: - a.eval = func(t *Thread) «Native» { - l, r := lf(t), rf(t) - var ret «Native» - «.section Body» - «Body» - «.or» - ret = «Expr» - «.end» - return «Native»(«Sized»(ret)) - } - «.end» - default: - log.Panicf("unexpected size %d in type %v at %v", t.Bits, t, a.pos) - } - «.or» - a.eval = func(t *Thread) «Native» { - l, r := lf(t), rf(t) - return «Expr» - } - «.end» - «.end» - «.end» - «.end» - default: - log.Panicf("unexpected type %v at %v", l.t, a.pos) - } -} - -«.end» -func genAssign(lt Type, r *expr) (func(lv Value, t *Thread)) { - switch lt.lit().(type) { -«.repeated section Types» -«.section IsIdeal» -«.or» - case «Repr»: - rf := r.«As»() - return func(lv Value, t *Thread) { «.section HasAssign»lv.Assign(t, rf(t))«.or»lv.(«Value»).Set(t, rf(t))«.end» } -«.end» -«.end» - default: - log.Panicf("unexpected left operand type %v at %v", lt, r.pos) - } - panic("fail") -} -` - -func main() { - t := template.New(nil) - t.SetDelims("«", "»") - err := t.Parse(templateStr) - if err != nil { - log.Fatal(err) - } - err = t.Execute(os.Stdout, data) - if err != nil { - log.Fatal(err) - } -} diff --git a/src/pkg/exp/eval/main.go b/src/pkg/exp/eval/main.go deleted file mode 100644 index d87e8f240..000000000 --- a/src/pkg/exp/eval/main.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2009 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. - -package main - -import ( - "bufio" - "exp/eval" - "flag" - "go/parser" - "go/scanner" - "go/token" - "io/ioutil" - "os" -) - -var fset = token.NewFileSet() -var filename = flag.String("f", "", "file to run") - -func main() { - flag.Parse() - w := eval.NewWorld() - if *filename != "" { - data, err := ioutil.ReadFile(*filename) - if err != nil { - println(err.String()) - os.Exit(1) - } - file, err := parser.ParseFile(fset, *filename, data, 0) - if err != nil { - println(err.String()) - os.Exit(1) - } - code, err := w.CompileDeclList(fset, file.Decls) - if err != nil { - if list, ok := err.(scanner.ErrorList); ok { - for _, e := range list { - println(e.String()) - } - } else { - println(err.String()) - } - os.Exit(1) - } - _, err = code.Run() - if err != nil { - println(err.String()) - os.Exit(1) - } - code, err = w.Compile(fset, "init()") - if code != nil { - _, err := code.Run() - if err != nil { - println(err.String()) - os.Exit(1) - } - } - code, err = w.Compile(fset, "main()") - if err != nil { - println(err.String()) - os.Exit(1) - } - _, err = code.Run() - if err != nil { - println(err.String()) - os.Exit(1) - } - os.Exit(0) - } - - r := bufio.NewReader(os.Stdin) - for { - print("; ") - line, err := r.ReadString('\n') - if err != nil { - break - } - code, err := w.Compile(fset, line) - if err != nil { - println(err.String()) - continue - } - v, err := code.Run() - if err != nil { - println(err.String()) - continue - } - if v != nil { - println(v.String()) - } - } -} diff --git a/src/pkg/exp/eval/scope.go b/src/pkg/exp/eval/scope.go deleted file mode 100644 index 66305de25..000000000 --- a/src/pkg/exp/eval/scope.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "go/token" - "log" -) - -/* - * Blocks and scopes - */ - -// A definition can be a *Variable, *Constant, or Type. -type Def interface { - Pos() token.Pos -} - -type Variable struct { - VarPos token.Pos - // Index of this variable in the Frame structure - Index int - // Static type of this variable - Type Type - // Value of this variable. This is only used by Scope.NewFrame; - // therefore, it is useful for global scopes but cannot be used - // in function scopes. - Init Value -} - -func (v *Variable) Pos() token.Pos { - return v.VarPos -} - -type Constant struct { - ConstPos token.Pos - Type Type - Value Value -} - -func (c *Constant) Pos() token.Pos { - return c.ConstPos -} - -// A block represents a definition block in which a name may not be -// defined more than once. -type block struct { - // The block enclosing this one, including blocks in other - // scopes. - outer *block - // The nested block currently being compiled, or nil. - inner *block - // The Scope containing this block. - scope *Scope - // The Variables, Constants, and Types defined in this block. - defs map[string]Def - // The index of the first variable defined in this block. - // This must be greater than the index of any variable defined - // in any parent of this block within the same Scope at the - // time this block is entered. - offset int - // The number of Variables defined in this block. - numVars int - // If global, do not allocate new vars and consts in - // the frame; assume that the refs will be compiled in - // using defs[name].Init. - global bool -} - -// A Scope is the compile-time analogue of a Frame, which captures -// some subtree of blocks. -type Scope struct { - // The root block of this scope. - *block - // The maximum number of variables required at any point in - // this Scope. This determines the number of slots needed in - // Frame's created from this Scope at run-time. - maxVars int -} - -func (b *block) enterChild() *block { - if b.inner != nil && b.inner.scope == b.scope { - log.Panic("Failed to exit child block before entering another child") - } - sub := &block{ - outer: b, - scope: b.scope, - defs: make(map[string]Def), - offset: b.offset + b.numVars, - } - b.inner = sub - return sub -} - -func (b *block) exit() { - if b.outer == nil { - log.Panic("Cannot exit top-level block") - } - if b.outer.scope == b.scope { - if b.outer.inner != b { - log.Panic("Already exited block") - } - if b.inner != nil && b.inner.scope == b.scope { - log.Panic("Exit of parent block without exit of child block") - } - } - b.outer.inner = nil -} - -func (b *block) ChildScope() *Scope { - if b.inner != nil && b.inner.scope == b.scope { - log.Panic("Failed to exit child block before entering a child scope") - } - sub := b.enterChild() - sub.offset = 0 - sub.scope = &Scope{sub, 0} - return sub.scope -} - -func (b *block) DefineVar(name string, pos token.Pos, t Type) (*Variable, Def) { - if prev, ok := b.defs[name]; ok { - return nil, prev - } - v := b.defineSlot(t, false) - v.VarPos = pos - b.defs[name] = v - return v, nil -} - -func (b *block) DefineTemp(t Type) *Variable { return b.defineSlot(t, true) } - -func (b *block) defineSlot(t Type, temp bool) *Variable { - if b.inner != nil && b.inner.scope == b.scope { - log.Panic("Failed to exit child block before defining variable") - } - index := -1 - if !b.global || temp { - index = b.offset + b.numVars - b.numVars++ - if index >= b.scope.maxVars { - b.scope.maxVars = index + 1 - } - } - v := &Variable{token.NoPos, index, t, nil} - return v -} - -func (b *block) DefineConst(name string, pos token.Pos, t Type, v Value) (*Constant, Def) { - if prev, ok := b.defs[name]; ok { - return nil, prev - } - c := &Constant{pos, t, v} - b.defs[name] = c - return c, nil -} - -func (b *block) DefineType(name string, pos token.Pos, t Type) Type { - if _, ok := b.defs[name]; ok { - return nil - } - nt := &NamedType{pos, name, nil, true, make(map[string]Method)} - if t != nil { - nt.Complete(t) - } - b.defs[name] = nt - return nt -} - -func (b *block) Lookup(name string) (bl *block, level int, def Def) { - for b != nil { - if d, ok := b.defs[name]; ok { - return b, level, d - } - if b.outer != nil && b.scope != b.outer.scope { - level++ - } - b = b.outer - } - return nil, 0, nil -} - -func (s *Scope) NewFrame(outer *Frame) *Frame { return outer.child(s.maxVars) } - -/* - * Frames - */ - -type Frame struct { - Outer *Frame - Vars []Value -} - -func (f *Frame) Get(level int, index int) Value { - for ; level > 0; level-- { - f = f.Outer - } - return f.Vars[index] -} - -func (f *Frame) child(numVars int) *Frame { - // TODO(austin) This is probably rather expensive. All values - // require heap allocation and zeroing them when we execute a - // definition typically requires some computation. - return &Frame{f, make([]Value, numVars)} -} diff --git a/src/pkg/exp/eval/stmt.go b/src/pkg/exp/eval/stmt.go deleted file mode 100644 index 57bf20e6f..000000000 --- a/src/pkg/exp/eval/stmt.go +++ /dev/null @@ -1,1299 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "big" - "log" - "go/ast" - "go/token" -) - -const ( - returnPC = ^uint(0) - badPC = ^uint(1) -) - -/* - * Statement compiler - */ - -type stmtCompiler struct { - *blockCompiler - pos token.Pos - // This statement's label, or nil if it is not labeled. - stmtLabel *label -} - -func (a *stmtCompiler) diag(format string, args ...interface{}) { - a.diagAt(a.pos, format, args...) -} - -/* - * Flow checker - */ - -type flowEnt struct { - // Whether this flow entry is conditional. If true, flow can - // continue to the next PC. - cond bool - // True if this will terminate flow (e.g., a return statement). - // cond must be false and jumps must be nil if this is true. - term bool - // PC's that can be reached from this flow entry. - jumps []*uint - // Whether this flow entry has been visited by reachesEnd. - visited bool -} - -type flowBlock struct { - // If this is a goto, the target label. - target string - // The inner-most block containing definitions. - block *block - // The numVars from each block leading to the root of the - // scope, starting at block. - numVars []int -} - -type flowBuf struct { - cb *codeBuf - // ents is a map from PC's to flow entries. Any PC missing - // from this map is assumed to reach only PC+1. - ents map[uint]*flowEnt - // gotos is a map from goto positions to information on the - // block at the point of the goto. - gotos map[token.Pos]*flowBlock - // labels is a map from label name to information on the block - // at the point of the label. labels are tracked by name, - // since multiple labels at the same PC can have different - // blocks. - labels map[string]*flowBlock -} - -func newFlowBuf(cb *codeBuf) *flowBuf { - return &flowBuf{cb, make(map[uint]*flowEnt), make(map[token.Pos]*flowBlock), make(map[string]*flowBlock)} -} - -// put creates a flow control point for the next PC in the code buffer. -// This should be done before pushing the instruction into the code buffer. -func (f *flowBuf) put(cond bool, term bool, jumps []*uint) { - pc := f.cb.nextPC() - if ent, ok := f.ents[pc]; ok { - log.Panicf("Flow entry already exists at PC %d: %+v", pc, ent) - } - f.ents[pc] = &flowEnt{cond, term, jumps, false} -} - -// putTerm creates a flow control point at the next PC that -// unconditionally terminates execution. -func (f *flowBuf) putTerm() { f.put(false, true, nil) } - -// put1 creates a flow control point at the next PC that jumps to one -// PC and, if cond is true, can also continue to the PC following the -// next PC. -func (f *flowBuf) put1(cond bool, jumpPC *uint) { - f.put(cond, false, []*uint{jumpPC}) -} - -func newFlowBlock(target string, b *block) *flowBlock { - // Find the inner-most block containing definitions - for b.numVars == 0 && b.outer != nil && b.outer.scope == b.scope { - b = b.outer - } - - // Count parents leading to the root of the scope - n := 0 - for bp := b; bp.scope == b.scope; bp = bp.outer { - n++ - } - - // Capture numVars from each block to the root of the scope - numVars := make([]int, n) - i := 0 - for bp := b; i < n; bp = bp.outer { - numVars[i] = bp.numVars - i++ - } - - return &flowBlock{target, b, numVars} -} - -// putGoto captures the block at a goto statement. This should be -// called in addition to putting a flow control point. -func (f *flowBuf) putGoto(pos token.Pos, target string, b *block) { - f.gotos[pos] = newFlowBlock(target, b) -} - -// putLabel captures the block at a label. -func (f *flowBuf) putLabel(name string, b *block) { - f.labels[name] = newFlowBlock("", b) -} - -// reachesEnd returns true if the end of f's code buffer can be -// reached from the given program counter. Error reporting is the -// caller's responsibility. -func (f *flowBuf) reachesEnd(pc uint) bool { - endPC := f.cb.nextPC() - if pc > endPC { - log.Panicf("Reached bad PC %d past end PC %d", pc, endPC) - } - - for ; pc < endPC; pc++ { - ent, ok := f.ents[pc] - if !ok { - continue - } - - if ent.visited { - return false - } - ent.visited = true - - if ent.term { - return false - } - - // If anything can reach the end, we can reach the end - // from pc. - for _, j := range ent.jumps { - if f.reachesEnd(*j) { - return true - } - } - // If the jump was conditional, we can reach the next - // PC, so try reaching the end from it. - if ent.cond { - continue - } - return false - } - return true -} - -// gotosObeyScopes returns true if no goto statement causes any -// variables to come into scope that were not in scope at the point of -// the goto. Reports any errors using the given compiler. -func (f *flowBuf) gotosObeyScopes(a *compiler) { - for pos, src := range f.gotos { - tgt := f.labels[src.target] - - // The target block must be a parent of this block - numVars := src.numVars - b := src.block - for len(numVars) > 0 && b != tgt.block { - b = b.outer - numVars = numVars[1:] - } - if b != tgt.block { - // We jumped into a deeper block - a.diagAt(pos, "goto causes variables to come into scope") - return - } - - // There must be no variables in the target block that - // did not exist at the jump - tgtNumVars := tgt.numVars - for i := range numVars { - if tgtNumVars[i] > numVars[i] { - a.diagAt(pos, "goto causes variables to come into scope") - return - } - } - } -} - -/* - * Statement generation helpers - */ - -func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable { - v, prev := a.block.DefineVar(ident.Name, ident.Pos(), t) - if prev != nil { - if prev.Pos().IsValid() { - a.diagAt(ident.Pos(), "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Name, a.fset.Position(prev.Pos())) - } else { - a.diagAt(ident.Pos(), "variable %s redeclared in this block", ident.Name) - } - return nil - } - - // Initialize the variable - index := v.Index - if v.Index >= 0 { - a.push(func(v *Thread) { v.f.Vars[index] = t.Zero() }) - } - return v -} - -// TODO(austin) Move doAssign to here - -/* - * Statement compiler - */ - -func (a *stmtCompiler) compile(s ast.Stmt) { - if a.block.inner != nil { - log.Panic("Child scope still entered") - } - - notimpl := false - switch s := s.(type) { - case *ast.BadStmt: - // Error already reported by parser. - a.silentErrors++ - - case *ast.DeclStmt: - a.compileDeclStmt(s) - - case *ast.EmptyStmt: - // Do nothing. - - case *ast.LabeledStmt: - a.compileLabeledStmt(s) - - case *ast.ExprStmt: - a.compileExprStmt(s) - - case *ast.IncDecStmt: - a.compileIncDecStmt(s) - - case *ast.AssignStmt: - a.compileAssignStmt(s) - - case *ast.GoStmt: - notimpl = true - - case *ast.DeferStmt: - notimpl = true - - case *ast.ReturnStmt: - a.compileReturnStmt(s) - - case *ast.BranchStmt: - a.compileBranchStmt(s) - - case *ast.BlockStmt: - a.compileBlockStmt(s) - - case *ast.IfStmt: - a.compileIfStmt(s) - - case *ast.CaseClause: - a.diag("case clause outside switch") - - case *ast.SwitchStmt: - a.compileSwitchStmt(s) - - case *ast.TypeSwitchStmt: - notimpl = true - - case *ast.CommClause: - notimpl = true - - case *ast.SelectStmt: - notimpl = true - - case *ast.ForStmt: - a.compileForStmt(s) - - case *ast.RangeStmt: - notimpl = true - - default: - log.Panicf("unexpected ast node type %T", s) - } - - if notimpl { - a.diag("%T statement node not implemented", s) - } - - if a.block.inner != nil { - log.Panic("Forgot to exit child scope") - } -} - -func (a *stmtCompiler) compileDeclStmt(s *ast.DeclStmt) { - switch decl := s.Decl.(type) { - case *ast.BadDecl: - // Do nothing. Already reported by parser. - a.silentErrors++ - - case *ast.FuncDecl: - if !a.block.global { - log.Panic("FuncDecl at statement level") - } - - case *ast.GenDecl: - if decl.Tok == token.IMPORT && !a.block.global { - log.Panic("import at statement level") - } - - default: - log.Panicf("Unexpected Decl type %T", s.Decl) - } - a.compileDecl(s.Decl) -} - -func (a *stmtCompiler) compileVarDecl(decl *ast.GenDecl) { - for _, spec := range decl.Specs { - spec := spec.(*ast.ValueSpec) - if spec.Values == nil { - // Declaration without assignment - if spec.Type == nil { - // Parser should have caught - log.Panic("Type and Values nil") - } - t := a.compileType(a.block, spec.Type) - // Define placeholders even if type compile failed - for _, n := range spec.Names { - a.defineVar(n, t) - } - } else { - // Declaration with assignment - lhs := make([]ast.Expr, len(spec.Names)) - for i, n := range spec.Names { - lhs[i] = n - } - a.doAssign(lhs, spec.Values, decl.Tok, spec.Type) - } - } -} - -func (a *stmtCompiler) compileDecl(decl ast.Decl) { - switch d := decl.(type) { - case *ast.BadDecl: - // Do nothing. Already reported by parser. - a.silentErrors++ - - case *ast.FuncDecl: - decl := a.compileFuncType(a.block, d.Type) - if decl == nil { - return - } - // Declare and initialize v before compiling func - // so that body can refer to itself. - c, prev := a.block.DefineConst(d.Name.Name, a.pos, decl.Type, decl.Type.Zero()) - if prev != nil { - pos := prev.Pos() - if pos.IsValid() { - a.diagAt(d.Name.Pos(), "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Name, a.fset.Position(pos)) - } else { - a.diagAt(d.Name.Pos(), "identifier %s redeclared in this block", d.Name.Name) - } - } - fn := a.compileFunc(a.block, decl, d.Body) - if c == nil || fn == nil { - return - } - var zeroThread Thread - c.Value.(FuncValue).Set(nil, fn(&zeroThread)) - - case *ast.GenDecl: - switch d.Tok { - case token.IMPORT: - log.Panicf("%v not implemented", d.Tok) - case token.CONST: - log.Panicf("%v not implemented", d.Tok) - case token.TYPE: - a.compileTypeDecl(a.block, d) - case token.VAR: - a.compileVarDecl(d) - } - - default: - log.Panicf("Unexpected Decl type %T", decl) - } -} - -func (a *stmtCompiler) compileLabeledStmt(s *ast.LabeledStmt) { - // Define label - l, ok := a.labels[s.Label.Name] - if ok { - if l.resolved.IsValid() { - a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Name, a.fset.Position(l.resolved)) - } - } else { - pc := badPC - l = &label{name: s.Label.Name, gotoPC: &pc} - a.labels[l.name] = l - } - l.desc = "regular label" - l.resolved = s.Pos() - - // Set goto PC - *l.gotoPC = a.nextPC() - - // Define flow entry so we can check for jumps over declarations. - a.flow.putLabel(l.name, a.block) - - // Compile the statement. Reuse our stmtCompiler for simplicity. - sc := &stmtCompiler{a.blockCompiler, s.Stmt.Pos(), l} - sc.compile(s.Stmt) -} - -func (a *stmtCompiler) compileExprStmt(s *ast.ExprStmt) { - bc := a.enterChild() - defer bc.exit() - - e := a.compileExpr(bc.block, false, s.X) - if e == nil { - return - } - - if e.exec == nil { - a.diag("%s cannot be used as expression statement", e.desc) - return - } - - a.push(e.exec) -} - -func (a *stmtCompiler) compileIncDecStmt(s *ast.IncDecStmt) { - // Create temporary block for extractEffect - bc := a.enterChild() - defer bc.exit() - - l := a.compileExpr(bc.block, false, s.X) - if l == nil { - return - } - - if l.evalAddr == nil { - l.diag("cannot assign to %s", l.desc) - return - } - if !(l.t.isInteger() || l.t.isFloat()) { - l.diagOpType(s.Tok, l.t) - return - } - - var op token.Token - var desc string - switch s.Tok { - case token.INC: - op = token.ADD - desc = "increment statement" - case token.DEC: - op = token.SUB - desc = "decrement statement" - default: - log.Panicf("Unexpected IncDec token %v", s.Tok) - } - - effect, l := l.extractEffect(bc.block, desc) - - one := l.newExpr(IdealIntType, "constant") - one.pos = s.Pos() - one.eval = func() *big.Int { return big.NewInt(1) } - - binop := l.compileBinaryExpr(op, l, one) - if binop == nil { - return - } - - assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "", "") - if assign == nil { - log.Panicf("compileAssign type check failed") - } - - lf := l.evalAddr - a.push(func(v *Thread) { - effect(v) - assign(lf(v), v) - }) -} - -func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token, declTypeExpr ast.Expr) { - nerr := a.numError() - - // Compile right side first so we have the types when - // compiling the left side and so we don't see definitions - // made on the left side. - rs := make([]*expr, len(rhs)) - for i, re := range rhs { - rs[i] = a.compileExpr(a.block, false, re) - } - - errOp := "assignment" - if tok == token.DEFINE || tok == token.VAR { - errOp = "declaration" - } - ac, ok := a.checkAssign(a.pos, rs, errOp, "value") - ac.allowMapForms(len(lhs)) - - // If this is a definition and the LHS is too big, we won't be - // able to produce the usual error message because we can't - // begin to infer the types of the LHS. - if (tok == token.DEFINE || tok == token.VAR) && len(lhs) > len(ac.rmt.Elems) { - a.diag("not enough values for definition") - } - - // Compile left type if there is one - var declType Type - if declTypeExpr != nil { - declType = a.compileType(a.block, declTypeExpr) - } - - // Compile left side - ls := make([]*expr, len(lhs)) - nDefs := 0 - for i, le := range lhs { - // If this is a definition, get the identifier and its type - var ident *ast.Ident - var lt Type - switch tok { - case token.DEFINE: - // Check that it's an identifier - ident, ok = le.(*ast.Ident) - if !ok { - a.diagAt(le.Pos(), "left side of := must be a name") - // Suppress new definitions errors - nDefs++ - continue - } - - // Is this simply an assignment? - if _, ok := a.block.defs[ident.Name]; ok { - ident = nil - break - } - nDefs++ - - case token.VAR: - ident = le.(*ast.Ident) - } - - // If it's a definition, get or infer its type. - if ident != nil { - // Compute the identifier's type from the RHS - // type. We use the computed MultiType so we - // don't have to worry about unpacking. - switch { - case declTypeExpr != nil: - // We have a declaration type, use it. - // If declType is nil, we gave an - // error when we compiled it. - lt = declType - - case i >= len(ac.rmt.Elems): - // Define a placeholder. We already - // gave the "not enough" error above. - lt = nil - - case ac.rmt.Elems[i] == nil: - // We gave the error when we compiled - // the RHS. - lt = nil - - case ac.rmt.Elems[i].isIdeal(): - // If the type is absent and the - // corresponding expression is a - // constant expression of ideal - // integer or ideal float type, the - // type of the declared variable is - // int or float respectively. - switch { - case ac.rmt.Elems[i].isInteger(): - lt = IntType - case ac.rmt.Elems[i].isFloat(): - lt = Float64Type - default: - log.Panicf("unexpected ideal type %v", rs[i].t) - } - - default: - lt = ac.rmt.Elems[i] - } - } - - // If it's a definition, define the identifier - if ident != nil { - if a.defineVar(ident, lt) == nil { - continue - } - } - - // Compile LHS - ls[i] = a.compileExpr(a.block, false, le) - if ls[i] == nil { - continue - } - - if ls[i].evalMapValue != nil { - // Map indexes are not generally addressable, - // but they are assignable. - // - // TODO(austin) Now that the expression - // compiler uses semantic values, this might - // be easier to implement as a function call. - sub := ls[i] - ls[i] = ls[i].newExpr(sub.t, sub.desc) - ls[i].evalMapValue = sub.evalMapValue - mvf := sub.evalMapValue - et := sub.t - ls[i].evalAddr = func(t *Thread) Value { - m, k := mvf(t) - e := m.Elem(t, k) - if e == nil { - e = et.Zero() - m.SetElem(t, k, e) - } - return e - } - } else if ls[i].evalAddr == nil { - ls[i].diag("cannot assign to %s", ls[i].desc) - continue - } - } - - // A short variable declaration may redeclare variables - // provided they were originally declared in the same block - // with the same type, and at least one of the variables is - // new. - if tok == token.DEFINE && nDefs == 0 { - a.diag("at least one new variable must be declared") - return - } - - // If there have been errors, our arrays are full of nil's so - // get out of here now. - if nerr != a.numError() { - return - } - - // Check for 'a[x] = r, ok' - if len(ls) == 1 && len(rs) == 2 && ls[0].evalMapValue != nil { - a.diag("a[x] = r, ok form not implemented") - return - } - - // Create assigner - var lt Type - n := len(lhs) - if n == 1 { - lt = ls[0].t - } else { - lts := make([]Type, len(ls)) - for i, l := range ls { - if l != nil { - lts[i] = l.t - } - } - lt = NewMultiType(lts) - } - bc := a.enterChild() - defer bc.exit() - assign := ac.compile(bc.block, lt) - if assign == nil { - return - } - - // Compile - if n == 1 { - // Don't need temporaries and can avoid []Value. - lf := ls[0].evalAddr - a.push(func(t *Thread) { assign(lf(t), t) }) - } else if tok == token.VAR || (tok == token.DEFINE && nDefs == n) { - // Don't need temporaries - lfs := make([]func(*Thread) Value, n) - for i, l := range ls { - lfs[i] = l.evalAddr - } - a.push(func(t *Thread) { - dest := make([]Value, n) - for i, lf := range lfs { - dest[i] = lf(t) - } - assign(multiV(dest), t) - }) - } else { - // Need temporaries - lmt := lt.(*MultiType) - lfs := make([]func(*Thread) Value, n) - for i, l := range ls { - lfs[i] = l.evalAddr - } - a.push(func(t *Thread) { - temp := lmt.Zero().(multiV) - assign(temp, t) - // Copy to destination - for i := 0; i < n; i++ { - // TODO(austin) Need to evaluate LHS - // before RHS - lfs[i](t).Assign(t, temp[i]) - } - }) - } -} - -var assignOpToOp = map[token.Token]token.Token{ - token.ADD_ASSIGN: token.ADD, - token.SUB_ASSIGN: token.SUB, - token.MUL_ASSIGN: token.MUL, - token.QUO_ASSIGN: token.QUO, - token.REM_ASSIGN: token.REM, - - token.AND_ASSIGN: token.AND, - token.OR_ASSIGN: token.OR, - token.XOR_ASSIGN: token.XOR, - token.SHL_ASSIGN: token.SHL, - token.SHR_ASSIGN: token.SHR, - token.AND_NOT_ASSIGN: token.AND_NOT, -} - -func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) { - if len(s.Lhs) != 1 || len(s.Rhs) != 1 { - a.diag("tuple assignment cannot be combined with an arithmetic operation") - return - } - - // Create temporary block for extractEffect - bc := a.enterChild() - defer bc.exit() - - l := a.compileExpr(bc.block, false, s.Lhs[0]) - r := a.compileExpr(bc.block, false, s.Rhs[0]) - if l == nil || r == nil { - return - } - - if l.evalAddr == nil { - l.diag("cannot assign to %s", l.desc) - return - } - - effect, l := l.extractEffect(bc.block, "operator-assignment") - - binop := r.compileBinaryExpr(assignOpToOp[s.Tok], l, r) - if binop == nil { - return - } - - assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "assignment", "value") - if assign == nil { - log.Panicf("compileAssign type check failed") - } - - lf := l.evalAddr - a.push(func(t *Thread) { - effect(t) - assign(lf(t), t) - }) -} - -func (a *stmtCompiler) compileAssignStmt(s *ast.AssignStmt) { - switch s.Tok { - case token.ASSIGN, token.DEFINE: - a.doAssign(s.Lhs, s.Rhs, s.Tok, nil) - - default: - a.doAssignOp(s) - } -} - -func (a *stmtCompiler) compileReturnStmt(s *ast.ReturnStmt) { - if a.fnType == nil { - a.diag("cannot return at the top level") - return - } - - if len(s.Results) == 0 && (len(a.fnType.Out) == 0 || a.outVarsNamed) { - // Simple case. Simply exit from the function. - a.flow.putTerm() - a.push(func(v *Thread) { v.pc = returnPC }) - return - } - - bc := a.enterChild() - defer bc.exit() - - // Compile expressions - bad := false - rs := make([]*expr, len(s.Results)) - for i, re := range s.Results { - rs[i] = a.compileExpr(bc.block, false, re) - if rs[i] == nil { - bad = true - } - } - if bad { - return - } - - // Create assigner - - // However, if the expression list in the "return" statement - // is a single call to a multi-valued function, the values - // returned from the called function will be returned from - // this one. - assign := a.compileAssign(s.Pos(), bc.block, NewMultiType(a.fnType.Out), rs, "return", "value") - - // XXX(Spec) "The result types of the current function and the - // called function must match." Match is fuzzy. It should - // say that they must be assignment compatible. - - // Compile - start := len(a.fnType.In) - nout := len(a.fnType.Out) - a.flow.putTerm() - a.push(func(t *Thread) { - assign(multiV(t.f.Vars[start:start+nout]), t) - t.pc = returnPC - }) -} - -func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool, errOp, errCtx string) *label { - bc := a.blockCompiler - for ; bc != nil; bc = bc.parent { - if bc.label == nil { - continue - } - l := bc.label - if name == nil && pred(l) { - return l - } - if name != nil && l.name == name.Name { - if !pred(l) { - a.diag("cannot %s to %s %s", errOp, l.desc, l.name) - return nil - } - return l - } - } - if name == nil { - a.diag("%s outside %s", errOp, errCtx) - } else { - a.diag("%s label %s not defined", errOp, name.Name) - } - return nil -} - -func (a *stmtCompiler) compileBranchStmt(s *ast.BranchStmt) { - var pc *uint - - switch s.Tok { - case token.BREAK: - l := a.findLexicalLabel(s.Label, func(l *label) bool { return l.breakPC != nil }, "break", "for loop, switch, or select") - if l == nil { - return - } - pc = l.breakPC - - case token.CONTINUE: - l := a.findLexicalLabel(s.Label, func(l *label) bool { return l.continuePC != nil }, "continue", "for loop") - if l == nil { - return - } - pc = l.continuePC - - case token.GOTO: - l, ok := a.labels[s.Label.Name] - if !ok { - pc := badPC - l = &label{name: s.Label.Name, desc: "unresolved label", gotoPC: &pc, used: s.Pos()} - a.labels[l.name] = l - } - - pc = l.gotoPC - a.flow.putGoto(s.Pos(), l.name, a.block) - - case token.FALLTHROUGH: - a.diag("fallthrough outside switch") - return - - default: - log.Panicf("Unexpected branch token %v", s.Tok) - } - - a.flow.put1(false, pc) - a.push(func(v *Thread) { v.pc = *pc }) -} - -func (a *stmtCompiler) compileBlockStmt(s *ast.BlockStmt) { - bc := a.enterChild() - bc.compileStmts(s) - bc.exit() -} - -func (a *stmtCompiler) compileIfStmt(s *ast.IfStmt) { - // The scope of any variables declared by [the init] statement - // extends to the end of the "if" statement and the variables - // are initialized once before the statement is entered. - // - // XXX(Spec) What this really wants to say is that there's an - // implicit scope wrapping every if, for, and switch - // statement. This is subtly different from what it actually - // says when there's a non-block else clause, because that - // else claus has to execute in a scope that is *not* the - // surrounding scope. - bc := a.enterChild() - defer bc.exit() - - // Compile init statement, if any - if s.Init != nil { - bc.compileStmt(s.Init) - } - - elsePC := badPC - endPC := badPC - - // Compile condition, if any. If there is no condition, we - // fall through to the body. - if s.Cond != nil { - e := bc.compileExpr(bc.block, false, s.Cond) - switch { - case e == nil: - // Error reported by compileExpr - case !e.t.isBoolean(): - e.diag("'if' condition must be boolean\n\t%v", e.t) - default: - eval := e.asBool() - a.flow.put1(true, &elsePC) - a.push(func(t *Thread) { - if !eval(t) { - t.pc = elsePC - } - }) - } - } - - // Compile body - body := bc.enterChild() - body.compileStmts(s.Body) - body.exit() - - // Compile else - if s.Else != nil { - // Skip over else if we executed the body - a.flow.put1(false, &endPC) - a.push(func(v *Thread) { v.pc = endPC }) - elsePC = a.nextPC() - bc.compileStmt(s.Else) - } else { - elsePC = a.nextPC() - } - endPC = a.nextPC() -} - -func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) { - // Create implicit scope around switch - bc := a.enterChild() - defer bc.exit() - - // Compile init statement, if any - if s.Init != nil { - bc.compileStmt(s.Init) - } - - // Compile condition, if any, and extract its effects - var cond *expr - condbc := bc.enterChild() - if s.Tag != nil { - e := condbc.compileExpr(condbc.block, false, s.Tag) - if e != nil { - var effect func(*Thread) - effect, cond = e.extractEffect(condbc.block, "switch") - a.push(effect) - } - } - - // Count cases - ncases := 0 - hasDefault := false - for _, c := range s.Body.List { - clause, ok := c.(*ast.CaseClause) - if !ok { - a.diagAt(clause.Pos(), "switch statement must contain case clauses") - continue - } - if clause.List == nil { - if hasDefault { - a.diagAt(clause.Pos(), "switch statement contains more than one default case") - } - hasDefault = true - } else { - ncases += len(clause.List) - } - } - - // Compile case expressions - cases := make([]func(*Thread) bool, ncases) - i := 0 - for _, c := range s.Body.List { - clause, ok := c.(*ast.CaseClause) - if !ok { - continue - } - for _, v := range clause.List { - e := condbc.compileExpr(condbc.block, false, v) - switch { - case e == nil: - // Error reported by compileExpr - case cond == nil && !e.t.isBoolean(): - a.diagAt(v.Pos(), "'case' condition must be boolean") - case cond == nil: - cases[i] = e.asBool() - case cond != nil: - // Create comparison - // TOOD(austin) This produces bad error messages - compare := e.compileBinaryExpr(token.EQL, cond, e) - if compare != nil { - cases[i] = compare.asBool() - } - } - i++ - } - } - - // Emit condition - casePCs := make([]*uint, ncases+1) - endPC := badPC - - a.flow.put(false, false, casePCs) - a.push(func(t *Thread) { - for i, c := range cases { - if c(t) { - t.pc = *casePCs[i] - return - } - } - t.pc = *casePCs[ncases] - }) - condbc.exit() - - // Compile cases - i = 0 - for _, c := range s.Body.List { - clause, ok := c.(*ast.CaseClause) - if !ok { - continue - } - - // Save jump PC's - pc := a.nextPC() - if clause.List != nil { - for _ = range clause.List { - casePCs[i] = &pc - i++ - } - } else { - // Default clause - casePCs[ncases] = &pc - } - - // Compile body - fall := false - for j, s := range clause.Body { - if br, ok := s.(*ast.BranchStmt); ok && br.Tok == token.FALLTHROUGH { - // println("Found fallthrough"); - // It may be used only as the final - // non-empty statement in a case or - // default clause in an expression - // "switch" statement. - for _, s2 := range clause.Body[j+1:] { - // XXX(Spec) 6g also considers - // empty blocks to be empty - // statements. - if _, ok := s2.(*ast.EmptyStmt); !ok { - a.diagAt(s.Pos(), "fallthrough statement must be final statement in case") - break - } - } - fall = true - } else { - bc.compileStmt(s) - } - } - // Jump out of switch, unless there was a fallthrough - if !fall { - a.flow.put1(false, &endPC) - a.push(func(v *Thread) { v.pc = endPC }) - } - } - - // Get end PC - endPC = a.nextPC() - if !hasDefault { - casePCs[ncases] = &endPC - } -} - -func (a *stmtCompiler) compileForStmt(s *ast.ForStmt) { - // Wrap the entire for in a block. - bc := a.enterChild() - defer bc.exit() - - // Compile init statement, if any - if s.Init != nil { - bc.compileStmt(s.Init) - } - - bodyPC := badPC - postPC := badPC - checkPC := badPC - endPC := badPC - - // Jump to condition check. We generate slightly less code by - // placing the condition check after the body. - a.flow.put1(false, &checkPC) - a.push(func(v *Thread) { v.pc = checkPC }) - - // Compile body - bodyPC = a.nextPC() - body := bc.enterChild() - if a.stmtLabel != nil { - body.label = a.stmtLabel - } else { - body.label = &label{resolved: s.Pos()} - } - body.label.desc = "for loop" - body.label.breakPC = &endPC - body.label.continuePC = &postPC - body.compileStmts(s.Body) - body.exit() - - // Compile post, if any - postPC = a.nextPC() - if s.Post != nil { - // TODO(austin) Does the parser disallow short - // declarations in s.Post? - bc.compileStmt(s.Post) - } - - // Compile condition check, if any - checkPC = a.nextPC() - if s.Cond == nil { - // If the condition is absent, it is equivalent to true. - a.flow.put1(false, &bodyPC) - a.push(func(v *Thread) { v.pc = bodyPC }) - } else { - e := bc.compileExpr(bc.block, false, s.Cond) - switch { - case e == nil: - // Error reported by compileExpr - case !e.t.isBoolean(): - a.diag("'for' condition must be boolean\n\t%v", e.t) - default: - eval := e.asBool() - a.flow.put1(true, &bodyPC) - a.push(func(t *Thread) { - if eval(t) { - t.pc = bodyPC - } - }) - } - } - - endPC = a.nextPC() -} - -/* - * Block compiler - */ - -func (a *blockCompiler) compileStmt(s ast.Stmt) { - sc := &stmtCompiler{a, s.Pos(), nil} - sc.compile(s) -} - -func (a *blockCompiler) compileStmts(block *ast.BlockStmt) { - for _, sub := range block.List { - a.compileStmt(sub) - } -} - -func (a *blockCompiler) enterChild() *blockCompiler { - block := a.block.enterChild() - return &blockCompiler{ - funcCompiler: a.funcCompiler, - block: block, - parent: a, - } -} - -func (a *blockCompiler) exit() { a.block.exit() } - -/* - * Function compiler - */ - -func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) func(*Thread) Func { - // Create body scope - // - // The scope of a parameter or result is the body of the - // corresponding function. - bodyScope := b.ChildScope() - defer bodyScope.exit() - for i, t := range decl.Type.In { - if decl.InNames[i] != nil { - bodyScope.DefineVar(decl.InNames[i].Name, decl.InNames[i].Pos(), t) - } else { - bodyScope.DefineTemp(t) - } - } - for i, t := range decl.Type.Out { - if decl.OutNames[i] != nil { - bodyScope.DefineVar(decl.OutNames[i].Name, decl.OutNames[i].Pos(), t) - } else { - bodyScope.DefineTemp(t) - } - } - - // Create block context - cb := newCodeBuf() - fc := &funcCompiler{ - compiler: a, - fnType: decl.Type, - outVarsNamed: len(decl.OutNames) > 0 && decl.OutNames[0] != nil, - codeBuf: cb, - flow: newFlowBuf(cb), - labels: make(map[string]*label), - } - bc := &blockCompiler{ - funcCompiler: fc, - block: bodyScope.block, - } - - // Compile body - nerr := a.numError() - bc.compileStmts(body) - fc.checkLabels() - if nerr != a.numError() { - return nil - } - - // Check that the body returned if necessary. We only check - // this if there were no errors compiling the body. - if len(decl.Type.Out) > 0 && fc.flow.reachesEnd(0) { - // XXX(Spec) Not specified. - a.diagAt(body.Rbrace, "function ends without a return statement") - return nil - } - - code := fc.get() - maxVars := bodyScope.maxVars - return func(t *Thread) Func { return &evalFunc{t.f, maxVars, code} } -} - -// Checks that labels were resolved and that all jumps obey scoping -// rules. Reports an error and set fc.err if any check fails. -func (a *funcCompiler) checkLabels() { - nerr := a.numError() - for _, l := range a.labels { - if !l.resolved.IsValid() { - a.diagAt(l.used, "label %s not defined", l.name) - } - } - if nerr != a.numError() { - // Don't check scopes if we have unresolved labels - return - } - - // Executing the "goto" statement must not cause any variables - // to come into scope that were not already in scope at the - // point of the goto. - a.flow.gotosObeyScopes(a.compiler) -} diff --git a/src/pkg/exp/eval/stmt_test.go b/src/pkg/exp/eval/stmt_test.go deleted file mode 100644 index a8a3e1620..000000000 --- a/src/pkg/exp/eval/stmt_test.go +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2009 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. - -package eval - -import "testing" - -var atLeastOneDecl = "at least one new variable must be declared" - -var stmtTests = []test{ - // Short declarations - Val1("x := i", "x", 1), - Val1("x := f", "x", 1.0), - // Type defaulting - Val1("a := 42", "a", 42), - Val1("a := 1.0", "a", 1.0), - // Parallel assignment - Val2("a, b := 1, 2", "a", 1, "b", 2), - Val2("a, i := 1, 2", "a", 1, "i", 2), - CErr("a, i := 1, f", opTypes), - CErr("a, b := 1, 2, 3", "too many"), - CErr("a := 1, 2", "too many"), - CErr("a, b := 1", "not enough"), - // Mixed declarations - CErr("i := 1", atLeastOneDecl), - CErr("i, u := 1, 2", atLeastOneDecl), - Val2("i, x := 2, f", "i", 2, "x", 1.0), - // Various errors - CErr("1 := 2", "expected identifier"), - CErr("c, a := 1, 1", "cannot assign"), - // Unpacking - Val2("x, y := oneTwo()", "x", 1, "y", 2), - CErr("x := oneTwo()", "too many"), - CErr("x, y, z := oneTwo()", "not enough"), - CErr("x, y := oneTwo(), 2", "multi-valued"), - CErr("x := oneTwo()+2", opTypes), - // TOOD(austin) This error message is weird - CErr("x := void()", "not enough"), - // Placeholders - CErr("x := 1+\"x\"; i=x+1", opTypes), - - // Assignment - Val1("i = 2", "i", 2), - Val1("(i) = 2", "i", 2), - CErr("1 = 2", "cannot assign"), - CErr("1-1 = 2", "- expression"), - Val1("i = 2.0", "i", 2), - CErr("i = 2.2", constantTruncated), - CErr("u = -2", constantUnderflows), - CErr("i = f", opTypes), - CErr("i, u = 0, f", opTypes), - CErr("i, u = 0, f", "value 2"), - Val2("i, i2 = i2, i", "i", 2, "i2", 1), - CErr("c = 1", "cannot assign"), - - Val1("x := &i; *x = 2", "i", 2), - - Val1("ai[0] = 42", "ai", varray{42, 2}), - Val1("aai[1] = ai; ai[0] = 42", "aai", varray{varray{1, 2}, varray{1, 2}}), - Val1("aai = aai2", "aai", varray{varray{5, 6}, varray{7, 8}}), - - // Assignment conversions - Run("var sl []int; sl = &ai"), - CErr("type ST []int; type AT *[2]int; var x AT = &ai; var y ST = x", opTypes), - Run("type ST []int; var y ST = &ai"), - Run("type AT *[2]int; var x AT = &ai; var y []int = x"), - - // Op-assignment - Val1("i += 2", "i", 3), - Val("i", 1), - Val1("f += 2", "f", 3.0), - CErr("2 += 2", "cannot assign"), - CErr("i, j += 2", "cannot be combined"), - CErr("i += 2, 3", "cannot be combined"), - Val2("s2 := s; s += \"def\"", "s2", "abc", "s", "abcdef"), - CErr("s += 1", opTypes), - // Single evaluation - Val2("ai[func()int{i+=1;return 0}()] *= 3; i2 = ai[0]", "i", 2, "i2", 3), - - // Type declarations - // Identifiers - Run("type T int"), - CErr("type T x", "undefined"), - CErr("type T c", "constant"), - CErr("type T i", "variable"), - CErr("type T T", "recursive"), - CErr("type T x; type U T; var v U; v = 1", "undefined"), - // Pointer types - Run("type T *int"), - Run("type T *T"), - // Array types - Run("type T [5]int"), - Run("type T [c+42/2]int"), - Run("type T [2.0]int"), - CErr("type T [i]int", "constant expression"), - CErr("type T [2.5]int", constantTruncated), - CErr("type T [-1]int", "negative"), - CErr("type T [2]T", "recursive"), - // Struct types - Run("type T struct { a int; b int }"), - Run("type T struct { a int; int }"), - Run("type T struct { x *T }"), - Run("type T int; type U struct { T }"), - CErr("type T *int; type U struct { T }", "embedded.*pointer"), - CErr("type T *struct { T }", "embedded.*pointer"), - CErr("type T struct { a int; a int }", " a .*redeclared.*:1:17"), - CErr("type T struct { int; int }", "int .*redeclared.*:1:17"), - CErr("type T struct { int int; int }", "int .*redeclared.*:1:17"), - Run("type T struct { x *struct { T } }"), - CErr("type T struct { x struct { T } }", "recursive"), - CErr("type T struct { x }; type U struct { T }", "undefined"), - // Function types - Run("type T func()"), - Run("type T func(a, b int) int"), - Run("type T func(a, b int) (x int, y int)"), - Run("type T func(a, a int) (a int, a int)"), - Run("type T func(a, b int) (x, y int)"), - Run("type T func(int, int) (int, int)"), - CErr("type T func(x); type U T", "undefined"), - CErr("type T func(a T)", "recursive"), - // Interface types - Run("type T interface {x(a, b int) int}"), - Run("type T interface {x(a, b int) int}; type U interface {T; y(c int)}"), - CErr("type T interface {x(a int); x()}", "method x redeclared"), - CErr("type T interface {x()}; type U interface {T; x()}", "method x redeclared"), - CErr("type T int; type U interface {T}", "embedded type"), - // Parens - Run("type T (int)"), - - // Variable declarations - Val2("var x int", "i", 1, "x", 0), - Val1("var x = 1", "x", 1), - Val1("var x = 1.0", "x", 1.0), - Val1("var x int = 1.0", "x", 1), - // Placeholders - CErr("var x foo; x = 1", "undefined"), - CErr("var x foo = 1; x = 1", "undefined"), - // Redeclaration - CErr("var i, x int", " i .*redeclared"), - CErr("var x int; var x int", " x .*redeclared.*:1:5"), - - // Expression statements - CErr("x := func(){ 1-1 }", "expression statement"), - CErr("x := func(){ 1-1 }", "- expression"), - Val1("fn(2)", "i", 1), - - // IncDec statements - Val1("i++", "i", 2), - Val1("i--", "i", 0), - Val1("u++", "u", uint(2)), - Val1("u--", "u", uint(0)), - Val1("f++", "f", 2.0), - Val1("f--", "f", 0.0), - // Single evaluation - Val2("ai[func()int{i+=1;return 0}()]++; i2 = ai[0]", "i", 2, "i2", 2), - // Operand types - CErr("s++", opTypes), - CErr("s++", "'\\+\\+'"), - CErr("2++", "cannot assign"), - CErr("c++", "cannot assign"), - - // Function scoping - Val1("fn1 := func() { i=2 }; fn1()", "i", 2), - Val1("fn1 := func() { i:=2 }; fn1()", "i", 1), - Val2("fn1 := func() int { i=2; i:=3; i=4; return i }; x := fn1()", "i", 2, "x", 4), - - // Basic returns - CErr("fn1 := func() int {}", "return"), - Run("fn1 := func() {}"), - CErr("fn1 := func() (r int) {}", "return"), - Val1("fn1 := func() (r int) {return}; i = fn1()", "i", 0), - Val1("fn1 := func() (r int) {r = 2; return}; i = fn1()", "i", 2), - Val1("fn1 := func() (r int) {return 2}; i = fn1()", "i", 2), - Val1("fn1 := func(int) int {return 2}; i = fn1(1)", "i", 2), - - // Multi-valued returns - Val2("fn1 := func() (bool, int) {return true, 2}; x, y := fn1()", "x", true, "y", 2), - CErr("fn1 := func() int {return}", "not enough values"), - CErr("fn1 := func() int {return 1,2}", "too many values"), - CErr("fn1 := func() {return 1}", "too many values"), - CErr("fn1 := func() (int,int,int) {return 1,2}", "not enough values"), - Val2("fn1 := func() (int, int) {return oneTwo()}; x, y := fn1()", "x", 1, "y", 2), - CErr("fn1 := func() int {return oneTwo()}", "too many values"), - CErr("fn1 := func() (int,int,int) {return oneTwo()}", "not enough values"), - Val1("fn1 := func(x,y int) int {return x+y}; x := fn1(oneTwo())", "x", 3), - - // Return control flow - Val2("fn1 := func(x *int) bool { *x = 2; return true; *x = 3; }; x := fn1(&i)", "i", 2, "x", true), - - // Break/continue/goto/fallthrough - CErr("break", "outside"), - CErr("break foo", "break.*foo.*not defined"), - CErr("continue", "outside"), - CErr("continue foo", "continue.*foo.*not defined"), - CErr("fallthrough", "outside"), - CErr("goto foo", "foo.*not defined"), - CErr(" foo: foo:;", "foo.*redeclared.*:1:2"), - Val1("i+=2; goto L; i+=4; L: i+=8", "i", 1+2+8), - // Return checking - CErr("fn1 := func() int { goto L; return 1; L: }", "return"), - Run("fn1 := func() int { L: goto L; i = 2 }"), - Run("fn1 := func() int { return 1; L: goto L }"), - // Scope checking - Run("fn1 := func() { { L: x:=1 }; goto L }"), - CErr("fn1 := func() { { x:=1; L: }; goto L }", "into scope"), - CErr("fn1 := func() { goto L; x:=1; L: }", "into scope"), - Run("fn1 := func() { goto L; { L: x:=1 } }"), - CErr("fn1 := func() { goto L; { x:=1; L: } }", "into scope"), - - // Blocks - CErr("fn1 := func() int {{}}", "return"), - Val1("fn1 := func() bool { { return true } }; b := fn1()", "b", true), - - // If - Val2("if true { i = 2 } else { i = 3 }; i2 = 4", "i", 2, "i2", 4), - Val2("if false { i = 2 } else { i = 3 }; i2 = 4", "i", 3, "i2", 4), - Val2("if i == i2 { i = 2 } else { i = 3 }; i2 = 4", "i", 3, "i2", 4), - // Omit optional parts - Val2("if true { i = 2 } else { i = 3 }; i2 = 4", "i", 2, "i2", 4), - Val2("if true { i = 2 }; i2 = 4", "i", 2, "i2", 4), - Val2("if false { i = 2 }; i2 = 4", "i", 1, "i2", 4), - // Init - Val2("if x := true; x { i = 2 } else { i = 3 }; i2 = 4", "i", 2, "i2", 4), - Val2("if x := false; x { i = 2 } else { i = 3 }; i2 = 4", "i", 3, "i2", 4), - // Statement else - Val2("if true { i = 2 } else i = 3; i2 = 4", "i", 2, "i2", 4), - Val2("if false { i = 2 } else i = 3; i2 = 4", "i", 3, "i2", 4), - // Scoping - Val2("if true { i := 2 } else { i := 3 }; i2 = i", "i", 1, "i2", 1), - Val2("if false { i := 2 } else { i := 3 }; i2 = i", "i", 1, "i2", 1), - Val2("if false { i := 2 } else i := 3; i2 = i", "i", 1, "i2", 1), - CErr("if true { x := 2 }; x = 4", undefined), - Val2("if i := 2; true { i2 = i; i := 3 }", "i", 1, "i2", 2), - Val2("if i := 2; false {} else { i2 = i; i := 3 }", "i", 1, "i2", 2), - // Return checking - Run("fn1 := func() int { if true { return 1 } else { return 2 } }"), - Run("fn1 := func() int { if true { return 1 } else return 2 }"), - CErr("fn1 := func() int { if true { return 1 } else { } }", "return"), - CErr("fn1 := func() int { if true { } else { return 1 } }", "return"), - CErr("fn1 := func() int { if true { } else return 1 }", "return"), - CErr("fn1 := func() int { if true { } else { } }", "return"), - CErr("fn1 := func() int { if true { return 1 } }", "return"), - CErr("fn1 := func() int { if true { } }", "return"), - Run("fn1 := func() int { if true { }; return 1 }"), - CErr("fn1 := func() int { if true { } }", "return"), - CErr("fn1 := func() int { if true { } else { return 2 } }", "return"), - Run("fn1 := func() int { if true { return 1 }; return 0 }"), - Run("fn1 := func() int { if true { return 1 } else { }; return 0 }"), - Run("fn1 := func() int { if true { return 1 } else { }; return 0 }"), - - // Switch - Val1("switch { case false: i += 2; case true: i += 4; default: i += 8 }", "i", 1+4), - Val1("switch { default: i += 2; case false: i += 4; case true: i += 8 }", "i", 1+8), - CErr("switch { default: i += 2; default: i += 4 }", "more than one"), - Val1("switch false { case false: i += 2; case true: i += 4; default: i += 8 }", "i", 1+2), - CErr("switch s { case 1: }", opTypes), - CErr("switch ai { case ai: i += 2 }", opTypes), - Val1("switch 1.0 { case 1: i += 2; case 2: i += 4 }", "i", 1+2), - Val1("switch 1.5 { case 1: i += 2; case 2: i += 4 }", "i", 1), - CErr("switch oneTwo() {}", "multi-valued expression"), - Val1("switch 2 { case 1: i += 2; fallthrough; case 2: i += 4; fallthrough; case 3: i += 8; fallthrough }", "i", 1+4+8), - Val1("switch 5 { case 1: i += 2; fallthrough; default: i += 4; fallthrough; case 2: i += 8; fallthrough; case 3: i += 16; fallthrough }", "i", 1+4+8+16), - CErr("switch { case true: fallthrough; i += 2 }", "final statement"), - Val1("switch { case true: i += 2; fallthrough; ; ; case false: i += 4 }", "i", 1+2+4), - Val1("switch 2 { case 0, 1: i += 2; case 2, 3: i += 4 }", "i", 1+4), - Val2("switch func()int{i2++;return 5}() { case 1, 2: i += 2; case 4, 5: i += 4 }", "i", 1+4, "i2", 3), - Run("switch i { case i: }"), - // TODO(austin) Why doesn't this fail? - //CErr("case 1:", "XXX"), - - // For - Val2("for x := 1; x < 5; x++ { i+=x }; i2 = 4", "i", 11, "i2", 4), - Val2("for x := 1; x < 5; x++ { i+=x; break; i++ }; i2 = 4", "i", 2, "i2", 4), - Val2("for x := 1; x < 5; x++ { i+=x; continue; i++ }; i2 = 4", "i", 11, "i2", 4), - Val2("for i = 2; false; i = 3 { i = 4 }; i2 = 4", "i", 2, "i2", 4), - Val2("for i < 5 { i++ }; i2 = 4", "i", 5, "i2", 4), - Val2("for i < 0 { i++ }; i2 = 4", "i", 1, "i2", 4), - // Scoping - Val2("for i := 2; true; { i2 = i; i := 3; break }", "i", 1, "i2", 2), - // Labeled break/continue - Val1("L1: for { L2: for { i+=2; break L1; i+=4 }; i+=8 }", "i", 1+2), - Val1("L1: for { L2: for { i+=2; break L2; i+=4 }; i+=8; break; i+=16 }", "i", 1+2+8), - CErr("L1: { for { break L1 } }", "break.*not defined"), - CErr("L1: for {}; for { break L1 }", "break.*not defined"), - CErr("L1:; for { break L1 }", "break.*not defined"), - Val2("L1: for i = 0; i < 2; i++ { L2: for { i2++; continue L1; i2++ } }", "i", 2, "i2", 4), - CErr("L1: { for { continue L1 } }", "continue.*not defined"), - CErr("L1:; for { continue L1 }", "continue.*not defined"), - // Return checking - Run("fn1 := func() int{ for {} }"), - CErr("fn1 := func() int{ for true {} }", "return"), - CErr("fn1 := func() int{ for true {return 1} }", "return"), - CErr("fn1 := func() int{ for {break} }", "return"), - Run("fn1 := func() int{ for { for {break} } }"), - CErr("fn1 := func() int{ L1: for { for {break L1} } }", "return"), - Run("fn1 := func() int{ for true {}; return 1 }"), - - // Selectors - Val1("var x struct { a int; b int }; x.a = 42; i = x.a", "i", 42), - Val1("type T struct { x int }; var y struct { T }; y.x = 42; i = y.x", "i", 42), - Val2("type T struct { x int }; var y struct { T; x int }; y.x = 42; i = y.x; i2 = y.T.x", "i", 42, "i2", 0), - Run("type T struct { x int }; var y struct { *T }; a := func(){i=y.x}"), - CErr("type T struct { x int }; var x T; x.y = 42", "no field"), - CErr("type T struct { x int }; type U struct { x int }; var y struct { T; U }; y.x = 42", "ambiguous.*\tT\\.x\n\tU\\.x"), - CErr("type T struct { *T }; var x T; x.foo", "no field"), - - Val1("fib := func(int) int{return 0;}; fib = func(v int) int { if v < 2 { return 1 }; return fib(v-1)+fib(v-2) }; i = fib(20)", "i", 10946), - - // Make slice - Val2("x := make([]int, 2); x[0] = 42; i, i2 = x[0], x[1]", "i", 42, "i2", 0), - Val2("x := make([]int, 2); x[1] = 42; i, i2 = x[0], x[1]", "i", 0, "i2", 42), - RErr("x := make([]int, 2); x[-i] = 42", "negative index"), - RErr("x := make([]int, 2); x[2] = 42", "index 2 exceeds"), - Val2("x := make([]int, 2, 3); i, i2 = len(x), cap(x)", "i", 2, "i2", 3), - Val2("x := make([]int, 3, 2); i, i2 = len(x), cap(x)", "i", 3, "i2", 3), - RErr("x := make([]int, -i)", "negative length"), - RErr("x := make([]int, 2, -i)", "negative capacity"), - RErr("x := make([]int, 2, 3); x[2] = 42", "index 2 exceeds"), - CErr("x := make([]int, 2, 3, 4)", "too many"), - CErr("x := make([]int)", "not enough"), - - // TODO(austin) Test make map - - // Maps - Val1("x := make(map[int] int); x[1] = 42; i = x[1]", "i", 42), - Val2("x := make(map[int] int); x[1] = 42; i, y := x[1]", "i", 42, "y", true), - Val2("x := make(map[int] int); x[1] = 42; i, y := x[2]", "i", 0, "y", false), - // Not implemented - //Val1("x := make(map[int] int); x[1] = 42, true; i = x[1]", "i", 42), - //Val2("x := make(map[int] int); x[1] = 42; x[1] = 42, false; i, y := x[1]", "i", 0, "y", false), - Run("var x int; a := make(map[int] int); a[0], x = 1, 2"), - CErr("x := make(map[int] int); (func(a,b int){})(x[0])", "not enough"), - CErr("x := make(map[int] int); x[1] = oneTwo()", "too many"), - RErr("x := make(map[int] int); i = x[1]", "key '1' not found"), - - // Functions - Val2("func fib(n int) int { if n <= 2 { return n }; return fib(n-1) + fib(n-2) }", "fib(4)", 5, "fib(10)", 89), - Run("func f1(){}"), - Run2("func f1(){}", "f1()"), -} - -func TestStmt(t *testing.T) { runTests(t, "stmtTests", stmtTests) } diff --git a/src/pkg/exp/eval/test.bash b/src/pkg/exp/eval/test.bash deleted file mode 100755 index 50b61fd00..000000000 --- a/src/pkg/exp/eval/test.bash +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2009 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. - -# Run the interpreter against all the Go test programs -# that begin with the magic -# // $G $D/$F.go && $L $F.$A && ./$A.out -# line and do not contain imports. - -set -e - -gomake -6g main.go && 6l main.6 -( -for i in $(egrep -l '// \$G (\$D/)?\$F\.go \&\& \$L \$F\.\$A && \./\$A\.out' "$GOROOT"/test/*.go "$GOROOT"/test/*/*.go) -do - if grep '^import' $i >/dev/null 2>&1 - then - true - else - if "$GOROOT"/usr/austin/eval/6.out -f $i >/tmp/out 2>&1 && ! test -s /tmp/out - then - echo PASS $i - else - echo FAIL $i - ( - echo '>>> ' $i - cat /tmp/out - echo - ) 1>&3 - fi - fi -done | (tee /dev/fd/2 | awk '{print $1}' | sort | uniq -c) 2>&1 -) 3>test.log diff --git a/src/pkg/exp/eval/type.go b/src/pkg/exp/eval/type.go deleted file mode 100644 index 8a93d8a6c..000000000 --- a/src/pkg/exp/eval/type.go +++ /dev/null @@ -1,1252 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "big" - "go/ast" - "go/token" - "log" - "reflect" - "sort" - "unsafe" // For Sizeof -) - - -// XXX(Spec) The type compatibility section is very confusing because -// it makes it seem like there are three distinct types of -// compatibility: plain compatibility, assignment compatibility, and -// comparison compatibility. As I understand it, there's really only -// assignment compatibility and comparison and conversion have some -// restrictions and have special meaning in some cases where the types -// are not otherwise assignment compatible. The comparison -// compatibility section is almost all about the semantics of -// comparison, not the type checking of it, so it would make much more -// sense in the comparison operators section. The compatibility and -// assignment compatibility sections should be rolled into one. - -type Type interface { - // compat returns whether this type is compatible with another - // type. If conv is false, this is normal compatibility, - // where two named types are compatible only if they are the - // same named type. If conv if true, this is conversion - // compatibility, where two named types are conversion - // compatible if their definitions are conversion compatible. - // - // TODO(austin) Deal with recursive types - compat(o Type, conv bool) bool - // lit returns this type's literal. If this is a named type, - // this is the unnamed underlying type. Otherwise, this is an - // identity operation. - lit() Type - // isBoolean returns true if this is a boolean type. - isBoolean() bool - // isInteger returns true if this is an integer type. - isInteger() bool - // isFloat returns true if this is a floating type. - isFloat() bool - // isIdeal returns true if this is an ideal int or float. - isIdeal() bool - // Zero returns a new zero value of this type. - Zero() Value - // String returns the string representation of this type. - String() string - // The position where this type was defined, if any. - Pos() token.Pos -} - -type BoundedType interface { - Type - // minVal returns the smallest value of this type. - minVal() *big.Rat - // maxVal returns the largest value of this type. - maxVal() *big.Rat -} - -var universePos = token.NoPos - -/* - * Type array maps. These are used to memoize composite types. - */ - -type typeArrayMapEntry struct { - key []Type - v interface{} - next *typeArrayMapEntry -} - -type typeArrayMap map[uintptr]*typeArrayMapEntry - -func hashTypeArray(key []Type) uintptr { - hash := uintptr(0) - for _, t := range key { - hash = hash * 33 - if t == nil { - continue - } - addr := reflect.ValueOf(t).Pointer() - hash ^= addr - } - return hash -} - -func newTypeArrayMap() typeArrayMap { return make(map[uintptr]*typeArrayMapEntry) } - -func (m typeArrayMap) Get(key []Type) interface{} { - ent, ok := m[hashTypeArray(key)] - if !ok { - return nil - } - -nextEnt: - for ; ent != nil; ent = ent.next { - if len(key) != len(ent.key) { - continue - } - for i := 0; i < len(key); i++ { - if key[i] != ent.key[i] { - continue nextEnt - } - } - // Found it - return ent.v - } - - return nil -} - -func (m typeArrayMap) Put(key []Type, v interface{}) interface{} { - hash := hashTypeArray(key) - ent := m[hash] - - new := &typeArrayMapEntry{key, v, ent} - m[hash] = new - return v -} - -/* - * Common type - */ - -type commonType struct{} - -func (commonType) isBoolean() bool { return false } - -func (commonType) isInteger() bool { return false } - -func (commonType) isFloat() bool { return false } - -func (commonType) isIdeal() bool { return false } - -func (commonType) Pos() token.Pos { return token.NoPos } - -/* - * Bool - */ - -type boolType struct { - commonType -} - -var BoolType = universe.DefineType("bool", universePos, &boolType{}) - -func (t *boolType) compat(o Type, conv bool) bool { - _, ok := o.lit().(*boolType) - return ok -} - -func (t *boolType) lit() Type { return t } - -func (t *boolType) isBoolean() bool { return true } - -func (boolType) String() string { - // Use angle brackets as a convention for printing the - // underlying, unnamed type. This should only show up in - // debug output. - return "<bool>" -} - -func (t *boolType) Zero() Value { - res := boolV(false) - return &res -} - -/* - * Uint - */ - -type uintType struct { - commonType - - // 0 for architecture-dependent types - Bits uint - // true for uintptr, false for all others - Ptr bool - name string -} - -var ( - Uint8Type = universe.DefineType("uint8", universePos, &uintType{commonType{}, 8, false, "uint8"}) - Uint16Type = universe.DefineType("uint16", universePos, &uintType{commonType{}, 16, false, "uint16"}) - Uint32Type = universe.DefineType("uint32", universePos, &uintType{commonType{}, 32, false, "uint32"}) - Uint64Type = universe.DefineType("uint64", universePos, &uintType{commonType{}, 64, false, "uint64"}) - - UintType = universe.DefineType("uint", universePos, &uintType{commonType{}, 0, false, "uint"}) - UintptrType = universe.DefineType("uintptr", universePos, &uintType{commonType{}, 0, true, "uintptr"}) -) - -func (t *uintType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*uintType) - return ok && t == t2 -} - -func (t *uintType) lit() Type { return t } - -func (t *uintType) isInteger() bool { return true } - -func (t *uintType) String() string { return "<" + t.name + ">" } - -func (t *uintType) Zero() Value { - switch t.Bits { - case 0: - if t.Ptr { - res := uintptrV(0) - return &res - } else { - res := uintV(0) - return &res - } - case 8: - res := uint8V(0) - return &res - case 16: - res := uint16V(0) - return &res - case 32: - res := uint32V(0) - return &res - case 64: - res := uint64V(0) - return &res - } - panic("unexpected uint bit count") -} - -func (t *uintType) minVal() *big.Rat { return big.NewRat(0, 1) } - -func (t *uintType) maxVal() *big.Rat { - bits := t.Bits - if bits == 0 { - if t.Ptr { - bits = uint(8 * unsafe.Sizeof(uintptr(0))) - } else { - bits = uint(8 * unsafe.Sizeof(uint(0))) - } - } - numer := big.NewInt(1) - numer.Lsh(numer, bits) - numer.Sub(numer, idealOne) - return new(big.Rat).SetInt(numer) -} - -/* - * Int - */ - -type intType struct { - commonType - - // XXX(Spec) Numeric types: "There is also a set of - // architecture-independent basic numeric types whose size - // depends on the architecture." Should that be - // architecture-dependent? - - // 0 for architecture-dependent types - Bits uint - name string -} - -var ( - Int8Type = universe.DefineType("int8", universePos, &intType{commonType{}, 8, "int8"}) - Int16Type = universe.DefineType("int16", universePos, &intType{commonType{}, 16, "int16"}) - Int32Type = universe.DefineType("int32", universePos, &intType{commonType{}, 32, "int32"}) - Int64Type = universe.DefineType("int64", universePos, &intType{commonType{}, 64, "int64"}) - - IntType = universe.DefineType("int", universePos, &intType{commonType{}, 0, "int"}) -) - -func (t *intType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*intType) - return ok && t == t2 -} - -func (t *intType) lit() Type { return t } - -func (t *intType) isInteger() bool { return true } - -func (t *intType) String() string { return "<" + t.name + ">" } - -func (t *intType) Zero() Value { - switch t.Bits { - case 8: - res := int8V(0) - return &res - case 16: - res := int16V(0) - return &res - case 32: - res := int32V(0) - return &res - case 64: - res := int64V(0) - return &res - - case 0: - res := intV(0) - return &res - } - panic("unexpected int bit count") -} - -func (t *intType) minVal() *big.Rat { - bits := t.Bits - if bits == 0 { - bits = uint(8 * unsafe.Sizeof(int(0))) - } - numer := big.NewInt(-1) - numer.Lsh(numer, bits-1) - return new(big.Rat).SetInt(numer) -} - -func (t *intType) maxVal() *big.Rat { - bits := t.Bits - if bits == 0 { - bits = uint(8 * unsafe.Sizeof(int(0))) - } - numer := big.NewInt(1) - numer.Lsh(numer, bits-1) - numer.Sub(numer, idealOne) - return new(big.Rat).SetInt(numer) -} - -/* - * Ideal int - */ - -type idealIntType struct { - commonType -} - -var IdealIntType Type = &idealIntType{} - -func (t *idealIntType) compat(o Type, conv bool) bool { - _, ok := o.lit().(*idealIntType) - return ok -} - -func (t *idealIntType) lit() Type { return t } - -func (t *idealIntType) isInteger() bool { return true } - -func (t *idealIntType) isIdeal() bool { return true } - -func (t *idealIntType) String() string { return "ideal integer" } - -func (t *idealIntType) Zero() Value { return &idealIntV{idealZero} } - -/* - * Float - */ - -type floatType struct { - commonType - - // 0 for architecture-dependent type - Bits uint - - name string -} - -var ( - Float32Type = universe.DefineType("float32", universePos, &floatType{commonType{}, 32, "float32"}) - Float64Type = universe.DefineType("float64", universePos, &floatType{commonType{}, 64, "float64"}) -) - -func (t *floatType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*floatType) - return ok && t == t2 -} - -func (t *floatType) lit() Type { return t } - -func (t *floatType) isFloat() bool { return true } - -func (t *floatType) String() string { return "<" + t.name + ">" } - -func (t *floatType) Zero() Value { - switch t.Bits { - case 32: - res := float32V(0) - return &res - case 64: - res := float64V(0) - return &res - } - panic("unexpected float bit count") -} - -var maxFloat32Val *big.Rat -var maxFloat64Val *big.Rat -var minFloat32Val *big.Rat -var minFloat64Val *big.Rat - -func (t *floatType) minVal() *big.Rat { - bits := t.Bits - switch bits { - case 32: - return minFloat32Val - case 64: - return minFloat64Val - } - log.Panicf("unexpected floating point bit count: %d", bits) - panic("unreachable") -} - -func (t *floatType) maxVal() *big.Rat { - bits := t.Bits - switch bits { - case 32: - return maxFloat32Val - case 64: - return maxFloat64Val - } - log.Panicf("unexpected floating point bit count: %d", bits) - panic("unreachable") -} - -/* - * Ideal float - */ - -type idealFloatType struct { - commonType -} - -var IdealFloatType Type = &idealFloatType{} - -func (t *idealFloatType) compat(o Type, conv bool) bool { - _, ok := o.lit().(*idealFloatType) - return ok -} - -func (t *idealFloatType) lit() Type { return t } - -func (t *idealFloatType) isFloat() bool { return true } - -func (t *idealFloatType) isIdeal() bool { return true } - -func (t *idealFloatType) String() string { return "ideal float" } - -func (t *idealFloatType) Zero() Value { return &idealFloatV{big.NewRat(0, 1)} } - -/* - * String - */ - -type stringType struct { - commonType -} - -var StringType = universe.DefineType("string", universePos, &stringType{}) - -func (t *stringType) compat(o Type, conv bool) bool { - _, ok := o.lit().(*stringType) - return ok -} - -func (t *stringType) lit() Type { return t } - -func (t *stringType) String() string { return "<string>" } - -func (t *stringType) Zero() Value { - res := stringV("") - return &res -} - -/* - * Array - */ - -type ArrayType struct { - commonType - Len int64 - Elem Type -} - -var arrayTypes = make(map[int64]map[Type]*ArrayType) - -// Two array types are identical if they have identical element types -// and the same array length. - -func NewArrayType(len int64, elem Type) *ArrayType { - ts, ok := arrayTypes[len] - if !ok { - ts = make(map[Type]*ArrayType) - arrayTypes[len] = ts - } - t, ok := ts[elem] - if !ok { - t = &ArrayType{commonType{}, len, elem} - ts[elem] = t - } - return t -} - -func (t *ArrayType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*ArrayType) - if !ok { - return false - } - return t.Len == t2.Len && t.Elem.compat(t2.Elem, conv) -} - -func (t *ArrayType) lit() Type { return t } - -func (t *ArrayType) String() string { return "[]" + t.Elem.String() } - -func (t *ArrayType) Zero() Value { - res := arrayV(make([]Value, t.Len)) - // TODO(austin) It's unfortunate that each element is - // separately heap allocated. We could add ZeroArray to - // everything, though that doesn't help with multidimensional - // arrays. Or we could do something unsafe. We'll have this - // same problem with structs. - for i := int64(0); i < t.Len; i++ { - res[i] = t.Elem.Zero() - } - return &res -} - -/* - * Struct - */ - -type StructField struct { - Name string - Type Type - Anonymous bool -} - -type StructType struct { - commonType - Elems []StructField -} - -var structTypes = newTypeArrayMap() - -// Two struct types are identical if they have the same sequence of -// fields, and if corresponding fields have the same names and -// identical types. Two anonymous fields are considered to have the -// same name. - -func NewStructType(fields []StructField) *StructType { - // Start by looking up just the types - fts := make([]Type, len(fields)) - for i, f := range fields { - fts[i] = f.Type - } - tMapI := structTypes.Get(fts) - if tMapI == nil { - tMapI = structTypes.Put(fts, make(map[string]*StructType)) - } - tMap := tMapI.(map[string]*StructType) - - // Construct key for field names - key := "" - for _, f := range fields { - // XXX(Spec) It's not clear if struct { T } and struct - // { T T } are either identical or compatible. The - // "Struct Types" section says that the name of that - // field is "T", which suggests that they are - // identical, but it really means that it's the name - // for the purpose of selector expressions and nothing - // else. We decided that they should be neither - // identical or compatible. - if f.Anonymous { - key += "!" - } - key += f.Name + " " - } - - // XXX(Spec) Do the tags also have to be identical for the - // types to be identical? I certainly hope so, because - // otherwise, this is the only case where two distinct type - // objects can represent identical types. - - t, ok := tMap[key] - if !ok { - // Create new struct type - t = &StructType{commonType{}, fields} - tMap[key] = t - } - return t -} - -func (t *StructType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*StructType) - if !ok { - return false - } - if len(t.Elems) != len(t2.Elems) { - return false - } - for i, e := range t.Elems { - e2 := t2.Elems[i] - // XXX(Spec) An anonymous and a non-anonymous field - // are neither identical nor compatible. - if e.Anonymous != e2.Anonymous || - (!e.Anonymous && e.Name != e2.Name) || - !e.Type.compat(e2.Type, conv) { - return false - } - } - return true -} - -func (t *StructType) lit() Type { return t } - -func (t *StructType) String() string { - s := "struct {" - for i, f := range t.Elems { - if i > 0 { - s += "; " - } - if !f.Anonymous { - s += f.Name + " " - } - s += f.Type.String() - } - return s + "}" -} - -func (t *StructType) Zero() Value { - res := structV(make([]Value, len(t.Elems))) - for i, f := range t.Elems { - res[i] = f.Type.Zero() - } - return &res -} - -/* - * Pointer - */ - -type PtrType struct { - commonType - Elem Type -} - -var ptrTypes = make(map[Type]*PtrType) - -// Two pointer types are identical if they have identical base types. - -func NewPtrType(elem Type) *PtrType { - t, ok := ptrTypes[elem] - if !ok { - t = &PtrType{commonType{}, elem} - ptrTypes[elem] = t - } - return t -} - -func (t *PtrType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*PtrType) - if !ok { - return false - } - return t.Elem.compat(t2.Elem, conv) -} - -func (t *PtrType) lit() Type { return t } - -func (t *PtrType) String() string { return "*" + t.Elem.String() } - -func (t *PtrType) Zero() Value { return &ptrV{nil} } - -/* - * Function - */ - -type FuncType struct { - commonType - // TODO(austin) Separate receiver Type for methods? - In []Type - Variadic bool - Out []Type - builtin string -} - -var funcTypes = newTypeArrayMap() -var variadicFuncTypes = newTypeArrayMap() - -// Create singleton function types for magic built-in functions -var ( - capType = &FuncType{builtin: "cap"} - closeType = &FuncType{builtin: "close"} - closedType = &FuncType{builtin: "closed"} - lenType = &FuncType{builtin: "len"} - makeType = &FuncType{builtin: "make"} - newType = &FuncType{builtin: "new"} - panicType = &FuncType{builtin: "panic"} - printType = &FuncType{builtin: "print"} - printlnType = &FuncType{builtin: "println"} - copyType = &FuncType{builtin: "copy"} -) - -// Two function types are identical if they have the same number of -// parameters and result values and if corresponding parameter and -// result types are identical. All "..." parameters have identical -// type. Parameter and result names are not required to match. - -func NewFuncType(in []Type, variadic bool, out []Type) *FuncType { - inMap := funcTypes - if variadic { - inMap = variadicFuncTypes - } - - outMapI := inMap.Get(in) - if outMapI == nil { - outMapI = inMap.Put(in, newTypeArrayMap()) - } - outMap := outMapI.(typeArrayMap) - - tI := outMap.Get(out) - if tI != nil { - return tI.(*FuncType) - } - - t := &FuncType{commonType{}, in, variadic, out, ""} - outMap.Put(out, t) - return t -} - -func (t *FuncType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*FuncType) - if !ok { - return false - } - if len(t.In) != len(t2.In) || t.Variadic != t2.Variadic || len(t.Out) != len(t2.Out) { - return false - } - for i := range t.In { - if !t.In[i].compat(t2.In[i], conv) { - return false - } - } - for i := range t.Out { - if !t.Out[i].compat(t2.Out[i], conv) { - return false - } - } - return true -} - -func (t *FuncType) lit() Type { return t } - -func typeListString(ts []Type, ns []*ast.Ident) string { - s := "" - for i, t := range ts { - if i > 0 { - s += ", " - } - if ns != nil && ns[i] != nil { - s += ns[i].Name + " " - } - if t == nil { - // Some places use nil types to represent errors - s += "<none>" - } else { - s += t.String() - } - } - return s -} - -func (t *FuncType) String() string { - if t.builtin != "" { - return "built-in function " + t.builtin - } - args := typeListString(t.In, nil) - if t.Variadic { - if len(args) > 0 { - args += ", " - } - args += "..." - } - s := "func(" + args + ")" - if len(t.Out) > 0 { - s += " (" + typeListString(t.Out, nil) + ")" - } - return s -} - -func (t *FuncType) Zero() Value { return &funcV{nil} } - -type FuncDecl struct { - Type *FuncType - Name *ast.Ident // nil for function literals - // InNames will be one longer than Type.In if this function is - // variadic. - InNames []*ast.Ident - OutNames []*ast.Ident -} - -func (t *FuncDecl) String() string { - s := "func" - if t.Name != nil { - s += " " + t.Name.Name - } - s += funcTypeString(t.Type, t.InNames, t.OutNames) - return s -} - -func funcTypeString(ft *FuncType, ins []*ast.Ident, outs []*ast.Ident) string { - s := "(" - s += typeListString(ft.In, ins) - if ft.Variadic { - if len(ft.In) > 0 { - s += ", " - } - s += "..." - } - s += ")" - if len(ft.Out) > 0 { - s += " (" + typeListString(ft.Out, outs) + ")" - } - return s -} - -/* - * Interface - */ - -// TODO(austin) Interface values, types, and type compilation are -// implemented, but none of the type checking or semantics of -// interfaces are. - -type InterfaceType struct { - commonType - // TODO(austin) This should be a map from names to - // *FuncType's. We only need the sorted list for generating - // the type map key. It's detrimental for everything else. - methods []IMethod -} - -type IMethod struct { - Name string - Type *FuncType -} - -var interfaceTypes = newTypeArrayMap() - -func NewInterfaceType(methods []IMethod, embeds []*InterfaceType) *InterfaceType { - // Count methods of embedded interfaces - nMethods := len(methods) - for _, e := range embeds { - nMethods += len(e.methods) - } - - // Combine methods - allMethods := make([]IMethod, nMethods) - copy(allMethods, methods) - n := len(methods) - for _, e := range embeds { - for _, m := range e.methods { - allMethods[n] = m - n++ - } - } - - // Sort methods - sort.Sort(iMethodSorter(allMethods)) - - mts := make([]Type, len(allMethods)) - for i, m := range methods { - mts[i] = m.Type - } - tMapI := interfaceTypes.Get(mts) - if tMapI == nil { - tMapI = interfaceTypes.Put(mts, make(map[string]*InterfaceType)) - } - tMap := tMapI.(map[string]*InterfaceType) - - key := "" - for _, m := range allMethods { - key += m.Name + " " - } - - t, ok := tMap[key] - if !ok { - t = &InterfaceType{commonType{}, allMethods} - tMap[key] = t - } - return t -} - -type iMethodSorter []IMethod - -func (s iMethodSorter) Less(a, b int) bool { return s[a].Name < s[b].Name } - -func (s iMethodSorter) Swap(a, b int) { s[a], s[b] = s[b], s[a] } - -func (s iMethodSorter) Len() int { return len(s) } - -func (t *InterfaceType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*InterfaceType) - if !ok { - return false - } - if len(t.methods) != len(t2.methods) { - return false - } - for i, e := range t.methods { - e2 := t2.methods[i] - if e.Name != e2.Name || !e.Type.compat(e2.Type, conv) { - return false - } - } - return true -} - -func (t *InterfaceType) lit() Type { return t } - -func (t *InterfaceType) String() string { - // TODO(austin) Instead of showing embedded interfaces, this - // shows their methods. - s := "interface {" - for i, m := range t.methods { - if i > 0 { - s += "; " - } - s += m.Name + funcTypeString(m.Type, nil, nil) - } - return s + "}" -} - -// implementedBy tests if o implements t, returning nil, true if it does. -// Otherwise, it returns a method of t that o is missing and false. -func (t *InterfaceType) implementedBy(o Type) (*IMethod, bool) { - if len(t.methods) == 0 { - return nil, true - } - - // The methods of a named interface types are those of the - // underlying type. - if it, ok := o.lit().(*InterfaceType); ok { - o = it - } - - // XXX(Spec) Interface types: "A type implements any interface - // comprising any subset of its methods" It's unclear if - // methods must have identical or compatible types. 6g - // requires identical types. - - switch o := o.(type) { - case *NamedType: - for _, tm := range t.methods { - sm, ok := o.methods[tm.Name] - if !ok || sm.decl.Type != tm.Type { - return &tm, false - } - } - return nil, true - - case *InterfaceType: - var ti, oi int - for ti < len(t.methods) && oi < len(o.methods) { - tm, om := &t.methods[ti], &o.methods[oi] - switch { - case tm.Name == om.Name: - if tm.Type != om.Type { - return tm, false - } - ti++ - oi++ - case tm.Name > om.Name: - oi++ - default: - return tm, false - } - } - if ti < len(t.methods) { - return &t.methods[ti], false - } - return nil, true - } - - return &t.methods[0], false -} - -func (t *InterfaceType) Zero() Value { return &interfaceV{} } - -/* - * Slice - */ - -type SliceType struct { - commonType - Elem Type -} - -var sliceTypes = make(map[Type]*SliceType) - -// Two slice types are identical if they have identical element types. - -func NewSliceType(elem Type) *SliceType { - t, ok := sliceTypes[elem] - if !ok { - t = &SliceType{commonType{}, elem} - sliceTypes[elem] = t - } - return t -} - -func (t *SliceType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*SliceType) - if !ok { - return false - } - return t.Elem.compat(t2.Elem, conv) -} - -func (t *SliceType) lit() Type { return t } - -func (t *SliceType) String() string { return "[]" + t.Elem.String() } - -func (t *SliceType) Zero() Value { - // The value of an uninitialized slice is nil. The length and - // capacity of a nil slice are 0. - return &sliceV{Slice{nil, 0, 0}} -} - -/* - * Map type - */ - -type MapType struct { - commonType - Key Type - Elem Type -} - -var mapTypes = make(map[Type]map[Type]*MapType) - -func NewMapType(key Type, elem Type) *MapType { - ts, ok := mapTypes[key] - if !ok { - ts = make(map[Type]*MapType) - mapTypes[key] = ts - } - t, ok := ts[elem] - if !ok { - t = &MapType{commonType{}, key, elem} - ts[elem] = t - } - return t -} - -func (t *MapType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*MapType) - if !ok { - return false - } - return t.Elem.compat(t2.Elem, conv) && t.Key.compat(t2.Key, conv) -} - -func (t *MapType) lit() Type { return t } - -func (t *MapType) String() string { return "map[" + t.Key.String() + "] " + t.Elem.String() } - -func (t *MapType) Zero() Value { - // The value of an uninitialized map is nil. - return &mapV{nil} -} - -/* -type ChanType struct { - // TODO(austin) -} -*/ - -/* - * Named types - */ - -type Method struct { - decl *FuncDecl - fn Func -} - -type NamedType struct { - NamePos token.Pos - Name string - // Underlying type. If incomplete is true, this will be nil. - // If incomplete is false and this is still nil, then this is - // a placeholder type representing an error. - Def Type - // True while this type is being defined. - incomplete bool - methods map[string]Method -} - -// TODO(austin) This is temporarily needed by the debugger's remote -// type parser. This should only be possible with block.DefineType. -func NewNamedType(name string) *NamedType { - return &NamedType{token.NoPos, name, nil, true, make(map[string]Method)} -} - -func (t *NamedType) Pos() token.Pos { - return t.NamePos -} - -func (t *NamedType) Complete(def Type) { - if !t.incomplete { - log.Panicf("cannot complete already completed NamedType %+v", *t) - } - // We strip the name from def because multiple levels of - // naming are useless. - if ndef, ok := def.(*NamedType); ok { - def = ndef.Def - } - t.Def = def - t.incomplete = false -} - -func (t *NamedType) compat(o Type, conv bool) bool { - t2, ok := o.(*NamedType) - if ok { - if conv { - // Two named types are conversion compatible - // if their literals are conversion - // compatible. - return t.Def.compat(t2.Def, conv) - } else { - // Two named types are compatible if their - // type names originate in the same type - // declaration. - return t == t2 - } - } - // A named and an unnamed type are compatible if the - // respective type literals are compatible. - return o.compat(t.Def, conv) -} - -func (t *NamedType) lit() Type { return t.Def.lit() } - -func (t *NamedType) isBoolean() bool { return t.Def.isBoolean() } - -func (t *NamedType) isInteger() bool { return t.Def.isInteger() } - -func (t *NamedType) isFloat() bool { return t.Def.isFloat() } - -func (t *NamedType) isIdeal() bool { return false } - -func (t *NamedType) String() string { return t.Name } - -func (t *NamedType) Zero() Value { return t.Def.Zero() } - -/* - * Multi-valued type - */ - -// MultiType is a special type used for multi-valued expressions, akin -// to a tuple type. It's not generally accessible within the -// language. -type MultiType struct { - commonType - Elems []Type -} - -var multiTypes = newTypeArrayMap() - -func NewMultiType(elems []Type) *MultiType { - if t := multiTypes.Get(elems); t != nil { - return t.(*MultiType) - } - - t := &MultiType{commonType{}, elems} - multiTypes.Put(elems, t) - return t -} - -func (t *MultiType) compat(o Type, conv bool) bool { - t2, ok := o.lit().(*MultiType) - if !ok { - return false - } - if len(t.Elems) != len(t2.Elems) { - return false - } - for i := range t.Elems { - if !t.Elems[i].compat(t2.Elems[i], conv) { - return false - } - } - return true -} - -var EmptyType Type = NewMultiType([]Type{}) - -func (t *MultiType) lit() Type { return t } - -func (t *MultiType) String() string { - if len(t.Elems) == 0 { - return "<none>" - } - return typeListString(t.Elems, nil) -} - -func (t *MultiType) Zero() Value { - res := make([]Value, len(t.Elems)) - for i, t := range t.Elems { - res[i] = t.Zero() - } - return multiV(res) -} - -/* - * Initialize the universe - */ - -func init() { - numer := big.NewInt(0xffffff) - numer.Lsh(numer, 127-23) - maxFloat32Val = new(big.Rat).SetInt(numer) - numer.SetInt64(0x1fffffffffffff) - numer.Lsh(numer, 1023-52) - maxFloat64Val = new(big.Rat).SetInt(numer) - minFloat32Val = new(big.Rat).Neg(maxFloat32Val) - minFloat64Val = new(big.Rat).Neg(maxFloat64Val) - - // To avoid portability issues all numeric types are distinct - // except byte, which is an alias for uint8. - - // Make byte an alias for the named type uint8. Type aliases - // are otherwise impossible in Go, so just hack it here. - universe.defs["byte"] = universe.defs["uint8"] - - // Built-in functions - universe.DefineConst("cap", universePos, capType, nil) - universe.DefineConst("close", universePos, closeType, nil) - universe.DefineConst("closed", universePos, closedType, nil) - universe.DefineConst("copy", universePos, copyType, nil) - universe.DefineConst("len", universePos, lenType, nil) - universe.DefineConst("make", universePos, makeType, nil) - universe.DefineConst("new", universePos, newType, nil) - universe.DefineConst("panic", universePos, panicType, nil) - universe.DefineConst("print", universePos, printType, nil) - universe.DefineConst("println", universePos, printlnType, nil) -} diff --git a/src/pkg/exp/eval/typec.go b/src/pkg/exp/eval/typec.go deleted file mode 100644 index 0ed24a8d2..000000000 --- a/src/pkg/exp/eval/typec.go +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "go/ast" - "go/token" - "log" -) - - -/* - * Type compiler - */ - -type typeCompiler struct { - *compiler - block *block - // Check to be performed after a type declaration is compiled. - // - // TODO(austin) This will probably have to change after we - // eliminate forward declarations. - lateCheck func() bool -} - -func (a *typeCompiler) compileIdent(x *ast.Ident, allowRec bool) Type { - _, _, def := a.block.Lookup(x.Name) - if def == nil { - a.diagAt(x.Pos(), "%s: undefined", x.Name) - return nil - } - switch def := def.(type) { - case *Constant: - a.diagAt(x.Pos(), "constant %v used as type", x.Name) - return nil - case *Variable: - a.diagAt(x.Pos(), "variable %v used as type", x.Name) - return nil - case *NamedType: - if !allowRec && def.incomplete { - a.diagAt(x.Pos(), "illegal recursive type") - return nil - } - if !def.incomplete && def.Def == nil { - // Placeholder type from an earlier error - return nil - } - return def - case Type: - return def - } - log.Panicf("name %s has unknown type %T", x.Name, def) - return nil -} - -func (a *typeCompiler) compileArrayType(x *ast.ArrayType, allowRec bool) Type { - // Compile element type - elem := a.compileType(x.Elt, allowRec) - - // Compile length expression - if x.Len == nil { - if elem == nil { - return nil - } - return NewSliceType(elem) - } - - if _, ok := x.Len.(*ast.Ellipsis); ok { - a.diagAt(x.Len.Pos(), "... array initializers not implemented") - return nil - } - l, ok := a.compileArrayLen(a.block, x.Len) - if !ok { - return nil - } - if l < 0 { - a.diagAt(x.Len.Pos(), "array length must be non-negative") - return nil - } - if elem == nil { - return nil - } - - return NewArrayType(l, elem) -} - -func (a *typeCompiler) compileFields(fields *ast.FieldList, allowRec bool) ([]Type, []*ast.Ident, []token.Pos, bool) { - n := fields.NumFields() - ts := make([]Type, n) - ns := make([]*ast.Ident, n) - ps := make([]token.Pos, n) - bad := false - - if fields != nil { - i := 0 - for _, f := range fields.List { - t := a.compileType(f.Type, allowRec) - if t == nil { - bad = true - } - if f.Names == nil { - ns[i] = nil - ts[i] = t - ps[i] = f.Type.Pos() - i++ - continue - } - for _, n := range f.Names { - ns[i] = n - ts[i] = t - ps[i] = n.Pos() - i++ - } - } - } - - return ts, ns, ps, bad -} - -func (a *typeCompiler) compileStructType(x *ast.StructType, allowRec bool) Type { - ts, names, poss, bad := a.compileFields(x.Fields, allowRec) - - // XXX(Spec) The spec claims that field identifiers must be - // unique, but 6g only checks this when they are accessed. I - // think the spec is better in this regard: if I write two - // fields with the same name in the same struct type, clearly - // that's a mistake. This definition does *not* descend into - // anonymous fields, so it doesn't matter if those change. - // There's separate language in the spec about checking - // uniqueness of field names inherited from anonymous fields - // at use time. - fields := make([]StructField, len(ts)) - nameSet := make(map[string]token.Pos, len(ts)) - for i := range fields { - // Compute field name and check anonymous fields - var name string - if names[i] != nil { - name = names[i].Name - } else { - if ts[i] == nil { - continue - } - - var nt *NamedType - // [For anonymous fields,] the unqualified - // type name acts as the field identifier. - switch t := ts[i].(type) { - case *NamedType: - name = t.Name - nt = t - case *PtrType: - switch t := t.Elem.(type) { - case *NamedType: - name = t.Name - nt = t - } - } - // [An anonymous field] must be specified as a - // type name T or as a pointer to a type name - // *T, and T itself, may not be a pointer or - // interface type. - if nt == nil { - a.diagAt(poss[i], "embedded type must T or *T, where T is a named type") - bad = true - continue - } - // The check for embedded pointer types must - // be deferred because of things like - // type T *struct { T } - lateCheck := a.lateCheck - a.lateCheck = func() bool { - if _, ok := nt.lit().(*PtrType); ok { - a.diagAt(poss[i], "embedded type %v is a pointer type", nt) - return false - } - return lateCheck() - } - } - - // Check name uniqueness - if prev, ok := nameSet[name]; ok { - a.diagAt(poss[i], "field %s redeclared\n\tprevious declaration at %s", name, a.fset.Position(prev)) - bad = true - continue - } - nameSet[name] = poss[i] - - // Create field - fields[i].Name = name - fields[i].Type = ts[i] - fields[i].Anonymous = (names[i] == nil) - } - - if bad { - return nil - } - - return NewStructType(fields) -} - -func (a *typeCompiler) compilePtrType(x *ast.StarExpr) Type { - elem := a.compileType(x.X, true) - if elem == nil { - return nil - } - return NewPtrType(elem) -} - -func (a *typeCompiler) compileFuncType(x *ast.FuncType, allowRec bool) *FuncDecl { - // TODO(austin) Variadic function types - - // The types of parameters and results must be complete. - // - // TODO(austin) It's not clear they actually have to be complete. - in, inNames, _, inBad := a.compileFields(x.Params, allowRec) - out, outNames, _, outBad := a.compileFields(x.Results, allowRec) - - if inBad || outBad { - return nil - } - return &FuncDecl{NewFuncType(in, false, out), nil, inNames, outNames} -} - -func (a *typeCompiler) compileInterfaceType(x *ast.InterfaceType, allowRec bool) *InterfaceType { - ts, names, poss, bad := a.compileFields(x.Methods, allowRec) - - methods := make([]IMethod, len(ts)) - nameSet := make(map[string]token.Pos, len(ts)) - embeds := make([]*InterfaceType, len(ts)) - - var nm, ne int - for i := range ts { - if ts[i] == nil { - continue - } - - if names[i] != nil { - name := names[i].Name - methods[nm].Name = name - methods[nm].Type = ts[i].(*FuncType) - nm++ - if prev, ok := nameSet[name]; ok { - a.diagAt(poss[i], "method %s redeclared\n\tprevious declaration at %s", name, a.fset.Position(prev)) - bad = true - continue - } - nameSet[name] = poss[i] - } else { - // Embedded interface - it, ok := ts[i].lit().(*InterfaceType) - if !ok { - a.diagAt(poss[i], "embedded type must be an interface") - bad = true - continue - } - embeds[ne] = it - ne++ - for _, m := range it.methods { - if prev, ok := nameSet[m.Name]; ok { - a.diagAt(poss[i], "method %s redeclared\n\tprevious declaration at %s", m.Name, a.fset.Position(prev)) - bad = true - continue - } - nameSet[m.Name] = poss[i] - } - } - } - - if bad { - return nil - } - - methods = methods[0:nm] - embeds = embeds[0:ne] - - return NewInterfaceType(methods, embeds) -} - -func (a *typeCompiler) compileMapType(x *ast.MapType) Type { - key := a.compileType(x.Key, true) - val := a.compileType(x.Value, true) - if key == nil || val == nil { - return nil - } - // XXX(Spec) The Map types section explicitly lists all types - // that can be map keys except for function types. - switch key.lit().(type) { - case *StructType: - a.diagAt(x.Pos(), "map key cannot be a struct type") - return nil - case *ArrayType: - a.diagAt(x.Pos(), "map key cannot be an array type") - return nil - case *SliceType: - a.diagAt(x.Pos(), "map key cannot be a slice type") - return nil - } - return NewMapType(key, val) -} - -func (a *typeCompiler) compileType(x ast.Expr, allowRec bool) Type { - switch x := x.(type) { - case *ast.BadExpr: - // Error already reported by parser - a.silentErrors++ - return nil - - case *ast.Ident: - return a.compileIdent(x, allowRec) - - case *ast.ArrayType: - return a.compileArrayType(x, allowRec) - - case *ast.StructType: - return a.compileStructType(x, allowRec) - - case *ast.StarExpr: - return a.compilePtrType(x) - - case *ast.FuncType: - fd := a.compileFuncType(x, allowRec) - if fd == nil { - return nil - } - return fd.Type - - case *ast.InterfaceType: - return a.compileInterfaceType(x, allowRec) - - case *ast.MapType: - return a.compileMapType(x) - - case *ast.ChanType: - goto notimpl - - case *ast.ParenExpr: - return a.compileType(x.X, allowRec) - - case *ast.Ellipsis: - a.diagAt(x.Pos(), "illegal use of ellipsis") - return nil - } - a.diagAt(x.Pos(), "expression used as type") - return nil - -notimpl: - a.diagAt(x.Pos(), "compileType: %T not implemented", x) - return nil -} - -/* - * Type compiler interface - */ - -func noLateCheck() bool { return true } - -func (a *compiler) compileType(b *block, typ ast.Expr) Type { - tc := &typeCompiler{a, b, noLateCheck} - t := tc.compileType(typ, false) - if !tc.lateCheck() { - t = nil - } - return t -} - -func (a *compiler) compileTypeDecl(b *block, decl *ast.GenDecl) bool { - ok := true - for _, spec := range decl.Specs { - spec := spec.(*ast.TypeSpec) - // Create incomplete type for this type - nt := b.DefineType(spec.Name.Name, spec.Name.Pos(), nil) - if nt != nil { - nt.(*NamedType).incomplete = true - } - // Compile type - tc := &typeCompiler{a, b, noLateCheck} - t := tc.compileType(spec.Type, false) - if t == nil { - // Create a placeholder type - ok = false - } - // Fill incomplete type - if nt != nil { - nt.(*NamedType).Complete(t) - } - // Perform late type checking with complete type - if !tc.lateCheck() { - ok = false - if nt != nil { - // Make the type a placeholder - nt.(*NamedType).Def = nil - } - } - } - return ok -} - -func (a *compiler) compileFuncType(b *block, typ *ast.FuncType) *FuncDecl { - tc := &typeCompiler{a, b, noLateCheck} - res := tc.compileFuncType(typ, false) - if res != nil { - if !tc.lateCheck() { - res = nil - } - } - return res -} diff --git a/src/pkg/exp/eval/value.go b/src/pkg/exp/eval/value.go deleted file mode 100644 index daa691897..000000000 --- a/src/pkg/exp/eval/value.go +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright 2009 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. - -package eval - -import ( - "big" - "fmt" -) - -type Value interface { - String() string - // Assign copies another value into this one. It should - // assume that the other value satisfies the same specific - // value interface (BoolValue, etc.), but must not assume - // anything about its specific type. - Assign(t *Thread, o Value) -} - -type BoolValue interface { - Value - Get(*Thread) bool - Set(*Thread, bool) -} - -type UintValue interface { - Value - Get(*Thread) uint64 - Set(*Thread, uint64) -} - -type IntValue interface { - Value - Get(*Thread) int64 - Set(*Thread, int64) -} - -// TODO(austin) IdealIntValue and IdealFloatValue should not exist -// because ideals are not l-values. -type IdealIntValue interface { - Value - Get() *big.Int -} - -type FloatValue interface { - Value - Get(*Thread) float64 - Set(*Thread, float64) -} - -type IdealFloatValue interface { - Value - Get() *big.Rat -} - -type StringValue interface { - Value - Get(*Thread) string - Set(*Thread, string) -} - -type ArrayValue interface { - Value - // TODO(austin) Get() is here for uniformity, but is - // completely useless. If a lot of other types have similarly - // useless Get methods, just special-case these uses. - Get(*Thread) ArrayValue - Elem(*Thread, int64) Value - // Sub returns an ArrayValue backed by the same array that - // starts from element i and has length len. - Sub(i int64, len int64) ArrayValue -} - -type StructValue interface { - Value - // TODO(austin) This is another useless Get() - Get(*Thread) StructValue - Field(*Thread, int) Value -} - -type PtrValue interface { - Value - Get(*Thread) Value - Set(*Thread, Value) -} - -type Func interface { - NewFrame() *Frame - Call(*Thread) -} - -type FuncValue interface { - Value - Get(*Thread) Func - Set(*Thread, Func) -} - -type Interface struct { - Type Type - Value Value -} - -type InterfaceValue interface { - Value - Get(*Thread) Interface - Set(*Thread, Interface) -} - -type Slice struct { - Base ArrayValue - Len, Cap int64 -} - -type SliceValue interface { - Value - Get(*Thread) Slice - Set(*Thread, Slice) -} - -type Map interface { - Len(*Thread) int64 - // Retrieve an element from the map, returning nil if it does - // not exist. - Elem(t *Thread, key interface{}) Value - // Set an entry in the map. If val is nil, delete the entry. - SetElem(t *Thread, key interface{}, val Value) - // TODO(austin) Perhaps there should be an iterator interface instead. - Iter(func(key interface{}, val Value) bool) -} - -type MapValue interface { - Value - Get(*Thread) Map - Set(*Thread, Map) -} - -/* - * Bool - */ - -type boolV bool - -func (v *boolV) String() string { return fmt.Sprint(*v) } - -func (v *boolV) Assign(t *Thread, o Value) { *v = boolV(o.(BoolValue).Get(t)) } - -func (v *boolV) Get(*Thread) bool { return bool(*v) } - -func (v *boolV) Set(t *Thread, x bool) { *v = boolV(x) } - -/* - * Uint - */ - -type uint8V uint8 - -func (v *uint8V) String() string { return fmt.Sprint(*v) } - -func (v *uint8V) Assign(t *Thread, o Value) { *v = uint8V(o.(UintValue).Get(t)) } - -func (v *uint8V) Get(*Thread) uint64 { return uint64(*v) } - -func (v *uint8V) Set(t *Thread, x uint64) { *v = uint8V(x) } - -type uint16V uint16 - -func (v *uint16V) String() string { return fmt.Sprint(*v) } - -func (v *uint16V) Assign(t *Thread, o Value) { *v = uint16V(o.(UintValue).Get(t)) } - -func (v *uint16V) Get(*Thread) uint64 { return uint64(*v) } - -func (v *uint16V) Set(t *Thread, x uint64) { *v = uint16V(x) } - -type uint32V uint32 - -func (v *uint32V) String() string { return fmt.Sprint(*v) } - -func (v *uint32V) Assign(t *Thread, o Value) { *v = uint32V(o.(UintValue).Get(t)) } - -func (v *uint32V) Get(*Thread) uint64 { return uint64(*v) } - -func (v *uint32V) Set(t *Thread, x uint64) { *v = uint32V(x) } - -type uint64V uint64 - -func (v *uint64V) String() string { return fmt.Sprint(*v) } - -func (v *uint64V) Assign(t *Thread, o Value) { *v = uint64V(o.(UintValue).Get(t)) } - -func (v *uint64V) Get(*Thread) uint64 { return uint64(*v) } - -func (v *uint64V) Set(t *Thread, x uint64) { *v = uint64V(x) } - -type uintV uint - -func (v *uintV) String() string { return fmt.Sprint(*v) } - -func (v *uintV) Assign(t *Thread, o Value) { *v = uintV(o.(UintValue).Get(t)) } - -func (v *uintV) Get(*Thread) uint64 { return uint64(*v) } - -func (v *uintV) Set(t *Thread, x uint64) { *v = uintV(x) } - -type uintptrV uintptr - -func (v *uintptrV) String() string { return fmt.Sprint(*v) } - -func (v *uintptrV) Assign(t *Thread, o Value) { *v = uintptrV(o.(UintValue).Get(t)) } - -func (v *uintptrV) Get(*Thread) uint64 { return uint64(*v) } - -func (v *uintptrV) Set(t *Thread, x uint64) { *v = uintptrV(x) } - -/* - * Int - */ - -type int8V int8 - -func (v *int8V) String() string { return fmt.Sprint(*v) } - -func (v *int8V) Assign(t *Thread, o Value) { *v = int8V(o.(IntValue).Get(t)) } - -func (v *int8V) Get(*Thread) int64 { return int64(*v) } - -func (v *int8V) Set(t *Thread, x int64) { *v = int8V(x) } - -type int16V int16 - -func (v *int16V) String() string { return fmt.Sprint(*v) } - -func (v *int16V) Assign(t *Thread, o Value) { *v = int16V(o.(IntValue).Get(t)) } - -func (v *int16V) Get(*Thread) int64 { return int64(*v) } - -func (v *int16V) Set(t *Thread, x int64) { *v = int16V(x) } - -type int32V int32 - -func (v *int32V) String() string { return fmt.Sprint(*v) } - -func (v *int32V) Assign(t *Thread, o Value) { *v = int32V(o.(IntValue).Get(t)) } - -func (v *int32V) Get(*Thread) int64 { return int64(*v) } - -func (v *int32V) Set(t *Thread, x int64) { *v = int32V(x) } - -type int64V int64 - -func (v *int64V) String() string { return fmt.Sprint(*v) } - -func (v *int64V) Assign(t *Thread, o Value) { *v = int64V(o.(IntValue).Get(t)) } - -func (v *int64V) Get(*Thread) int64 { return int64(*v) } - -func (v *int64V) Set(t *Thread, x int64) { *v = int64V(x) } - -type intV int - -func (v *intV) String() string { return fmt.Sprint(*v) } - -func (v *intV) Assign(t *Thread, o Value) { *v = intV(o.(IntValue).Get(t)) } - -func (v *intV) Get(*Thread) int64 { return int64(*v) } - -func (v *intV) Set(t *Thread, x int64) { *v = intV(x) } - -/* - * Ideal int - */ - -type idealIntV struct { - V *big.Int -} - -func (v *idealIntV) String() string { return v.V.String() } - -func (v *idealIntV) Assign(t *Thread, o Value) { - v.V = o.(IdealIntValue).Get() -} - -func (v *idealIntV) Get() *big.Int { return v.V } - -/* - * Float - */ - -type float32V float32 - -func (v *float32V) String() string { return fmt.Sprint(*v) } - -func (v *float32V) Assign(t *Thread, o Value) { *v = float32V(o.(FloatValue).Get(t)) } - -func (v *float32V) Get(*Thread) float64 { return float64(*v) } - -func (v *float32V) Set(t *Thread, x float64) { *v = float32V(x) } - -type float64V float64 - -func (v *float64V) String() string { return fmt.Sprint(*v) } - -func (v *float64V) Assign(t *Thread, o Value) { *v = float64V(o.(FloatValue).Get(t)) } - -func (v *float64V) Get(*Thread) float64 { return float64(*v) } - -func (v *float64V) Set(t *Thread, x float64) { *v = float64V(x) } - -/* - * Ideal float - */ - -type idealFloatV struct { - V *big.Rat -} - -func (v *idealFloatV) String() string { return v.V.FloatString(6) } - -func (v *idealFloatV) Assign(t *Thread, o Value) { - v.V = o.(IdealFloatValue).Get() -} - -func (v *idealFloatV) Get() *big.Rat { return v.V } - -/* - * String - */ - -type stringV string - -func (v *stringV) String() string { return fmt.Sprint(*v) } - -func (v *stringV) Assign(t *Thread, o Value) { *v = stringV(o.(StringValue).Get(t)) } - -func (v *stringV) Get(*Thread) string { return string(*v) } - -func (v *stringV) Set(t *Thread, x string) { *v = stringV(x) } - -/* - * Array - */ - -type arrayV []Value - -func (v *arrayV) String() string { - res := "{" - for i, e := range *v { - if i > 0 { - res += ", " - } - res += e.String() - } - return res + "}" -} - -func (v *arrayV) Assign(t *Thread, o Value) { - oa := o.(ArrayValue) - l := int64(len(*v)) - for i := int64(0); i < l; i++ { - (*v)[i].Assign(t, oa.Elem(t, i)) - } -} - -func (v *arrayV) Get(*Thread) ArrayValue { return v } - -func (v *arrayV) Elem(t *Thread, i int64) Value { - return (*v)[i] -} - -func (v *arrayV) Sub(i int64, len int64) ArrayValue { - res := (*v)[i : i+len] - return &res -} - -/* - * Struct - */ - -type structV []Value - -// TODO(austin) Should these methods (and arrayV's) be on structV -// instead of *structV? -func (v *structV) String() string { - res := "{" - for i, v := range *v { - if i > 0 { - res += ", " - } - res += v.String() - } - return res + "}" -} - -func (v *structV) Assign(t *Thread, o Value) { - oa := o.(StructValue) - l := len(*v) - for i := 0; i < l; i++ { - (*v)[i].Assign(t, oa.Field(t, i)) - } -} - -func (v *structV) Get(*Thread) StructValue { return v } - -func (v *structV) Field(t *Thread, i int) Value { - return (*v)[i] -} - -/* - * Pointer - */ - -type ptrV struct { - // nil if the pointer is nil - target Value -} - -func (v *ptrV) String() string { - if v.target == nil { - return "<nil>" - } - return "&" + v.target.String() -} - -func (v *ptrV) Assign(t *Thread, o Value) { v.target = o.(PtrValue).Get(t) } - -func (v *ptrV) Get(*Thread) Value { return v.target } - -func (v *ptrV) Set(t *Thread, x Value) { v.target = x } - -/* - * Functions - */ - -type funcV struct { - target Func -} - -func (v *funcV) String() string { - // TODO(austin) Rob wants to see the definition - return "func {...}" -} - -func (v *funcV) Assign(t *Thread, o Value) { v.target = o.(FuncValue).Get(t) } - -func (v *funcV) Get(*Thread) Func { return v.target } - -func (v *funcV) Set(t *Thread, x Func) { v.target = x } - -/* - * Interfaces - */ - -type interfaceV struct { - Interface -} - -func (v *interfaceV) String() string { - if v.Type == nil || v.Value == nil { - return "<nil>" - } - return v.Value.String() -} - -func (v *interfaceV) Assign(t *Thread, o Value) { - v.Interface = o.(InterfaceValue).Get(t) -} - -func (v *interfaceV) Get(*Thread) Interface { return v.Interface } - -func (v *interfaceV) Set(t *Thread, x Interface) { - v.Interface = x -} - -/* - * Slices - */ - -type sliceV struct { - Slice -} - -func (v *sliceV) String() string { - if v.Base == nil { - return "<nil>" - } - return v.Base.Sub(0, v.Len).String() -} - -func (v *sliceV) Assign(t *Thread, o Value) { v.Slice = o.(SliceValue).Get(t) } - -func (v *sliceV) Get(*Thread) Slice { return v.Slice } - -func (v *sliceV) Set(t *Thread, x Slice) { v.Slice = x } - -/* - * Maps - */ - -type mapV struct { - target Map -} - -func (v *mapV) String() string { - if v.target == nil { - return "<nil>" - } - res := "map[" - i := 0 - v.target.Iter(func(key interface{}, val Value) bool { - if i > 0 { - res += ", " - } - i++ - res += fmt.Sprint(key) + ":" + val.String() - return true - }) - return res + "]" -} - -func (v *mapV) Assign(t *Thread, o Value) { v.target = o.(MapValue).Get(t) } - -func (v *mapV) Get(*Thread) Map { return v.target } - -func (v *mapV) Set(t *Thread, x Map) { v.target = x } - -type evalMap map[interface{}]Value - -func (m evalMap) Len(t *Thread) int64 { return int64(len(m)) } - -func (m evalMap) Elem(t *Thread, key interface{}) Value { - return m[key] -} - -func (m evalMap) SetElem(t *Thread, key interface{}, val Value) { - if val == nil { - m[key] = nil, false - } else { - m[key] = val - } -} - -func (m evalMap) Iter(cb func(key interface{}, val Value) bool) { - for k, v := range m { - if !cb(k, v) { - break - } - } -} - -/* - * Multi-values - */ - -type multiV []Value - -func (v multiV) String() string { - res := "(" - for i, v := range v { - if i > 0 { - res += ", " - } - res += v.String() - } - return res + ")" -} - -func (v multiV) Assign(t *Thread, o Value) { - omv := o.(multiV) - for i := range v { - v[i].Assign(t, omv[i]) - } -} - -/* - * Universal constants - */ - -func init() { - s := universe - - true := boolV(true) - s.DefineConst("true", universePos, BoolType, &true) - false := boolV(false) - s.DefineConst("false", universePos, BoolType, &false) -} diff --git a/src/pkg/exp/eval/world.go b/src/pkg/exp/eval/world.go deleted file mode 100644 index a5f6ac7e5..000000000 --- a/src/pkg/exp/eval/world.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2009 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. - -// Package eval is the beginning of an interpreter for Go. -// It can run simple Go programs but does not implement -// interface values or packages. -package eval - -import ( - "go/ast" - "go/parser" - "go/scanner" - "go/token" - "os" -) - -type World struct { - scope *Scope - frame *Frame -} - -func NewWorld() *World { - w := new(World) - w.scope = universe.ChildScope() - w.scope.global = true // this block's vars allocate directly - return w -} - -type Code interface { - // The type of the value Run returns, or nil if Run returns nil. - Type() Type - - // Run runs the code; if the code is a single expression - // with a value, it returns the value; otherwise it returns nil. - Run() (Value, os.Error) -} - -type stmtCode struct { - w *World - code code -} - -func (w *World) CompileStmtList(fset *token.FileSet, stmts []ast.Stmt) (Code, os.Error) { - if len(stmts) == 1 { - if s, ok := stmts[0].(*ast.ExprStmt); ok { - return w.CompileExpr(fset, s.X) - } - } - errors := new(scanner.ErrorVector) - cc := &compiler{fset, errors, 0, 0} - cb := newCodeBuf() - fc := &funcCompiler{ - compiler: cc, - fnType: nil, - outVarsNamed: false, - codeBuf: cb, - flow: newFlowBuf(cb), - labels: make(map[string]*label), - } - bc := &blockCompiler{ - funcCompiler: fc, - block: w.scope.block, - } - nerr := cc.numError() - for _, stmt := range stmts { - bc.compileStmt(stmt) - } - fc.checkLabels() - if nerr != cc.numError() { - return nil, errors.GetError(scanner.Sorted) - } - return &stmtCode{w, fc.get()}, nil -} - -func (w *World) CompileDeclList(fset *token.FileSet, decls []ast.Decl) (Code, os.Error) { - stmts := make([]ast.Stmt, len(decls)) - for i, d := range decls { - stmts[i] = &ast.DeclStmt{d} - } - return w.CompileStmtList(fset, stmts) -} - -func (s *stmtCode) Type() Type { return nil } - -func (s *stmtCode) Run() (Value, os.Error) { - t := new(Thread) - t.f = s.w.scope.NewFrame(nil) - return nil, t.Try(func(t *Thread) { s.code.exec(t) }) -} - -type exprCode struct { - w *World - e *expr - eval func(Value, *Thread) -} - -func (w *World) CompileExpr(fset *token.FileSet, e ast.Expr) (Code, os.Error) { - errors := new(scanner.ErrorVector) - cc := &compiler{fset, errors, 0, 0} - - ec := cc.compileExpr(w.scope.block, false, e) - if ec == nil { - return nil, errors.GetError(scanner.Sorted) - } - var eval func(Value, *Thread) - switch t := ec.t.(type) { - case *idealIntType: - // nothing - case *idealFloatType: - // nothing - default: - if tm, ok := t.(*MultiType); ok && len(tm.Elems) == 0 { - return &stmtCode{w, code{ec.exec}}, nil - } - eval = genAssign(ec.t, ec) - } - return &exprCode{w, ec, eval}, nil -} - -func (e *exprCode) Type() Type { return e.e.t } - -func (e *exprCode) Run() (Value, os.Error) { - t := new(Thread) - t.f = e.w.scope.NewFrame(nil) - switch e.e.t.(type) { - case *idealIntType: - return &idealIntV{e.e.asIdealInt()()}, nil - case *idealFloatType: - return &idealFloatV{e.e.asIdealFloat()()}, nil - } - v := e.e.t.Zero() - eval := e.eval - err := t.Try(func(t *Thread) { eval(v, t) }) - return v, err -} - -func (w *World) Compile(fset *token.FileSet, text string) (Code, os.Error) { - stmts, err := parser.ParseStmtList(fset, "input", text) - if err == nil { - return w.CompileStmtList(fset, stmts) - } - - // Otherwise try as DeclList. - decls, err1 := parser.ParseDeclList(fset, "input", text) - if err1 == nil { - return w.CompileDeclList(fset, decls) - } - - // Have to pick an error. - // Parsing as statement list admits more forms, - // its error is more likely to be useful. - return nil, err -} - -type RedefinitionError struct { - Name string - Prev Def -} - -func (e *RedefinitionError) String() string { - res := "identifier " + e.Name + " redeclared" - pos := e.Prev.Pos() - if pos.IsValid() { - // TODO: fix this - currently this code is not reached by the tests - // need to get a file set (fset) from somewhere - //res += "; previous declaration at " + fset.Position(pos).String() - panic(0) - } - return res -} - -func (w *World) DefineConst(name string, t Type, val Value) os.Error { - _, prev := w.scope.DefineConst(name, token.NoPos, t, val) - if prev != nil { - return &RedefinitionError{name, prev} - } - return nil -} - -func (w *World) DefineVar(name string, t Type, val Value) os.Error { - v, prev := w.scope.DefineVar(name, token.NoPos, t) - if prev != nil { - return &RedefinitionError{name, prev} - } - v.Init = val - return nil -} diff --git a/src/pkg/exp/gui/Makefile b/src/pkg/exp/gui/Makefile deleted file mode 100644 index af065e4a5..000000000 --- a/src/pkg/exp/gui/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 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. - -include ../../../Make.inc - -TARG=exp/gui -GOFILES=\ - gui.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/gui/gui.go b/src/pkg/exp/gui/gui.go deleted file mode 100644 index 171499186..000000000 --- a/src/pkg/exp/gui/gui.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2009 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. - -// Package gui defines a basic graphical user interface programming model. -package gui - -import ( - "image" - "image/draw" - "os" -) - -// A Window represents a single graphics window. -type Window interface { - // Screen returns an editable Image for the window. - Screen() draw.Image - // FlushImage flushes changes made to Screen() back to screen. - FlushImage() - // EventChan returns a channel carrying UI events such as key presses, - // mouse movements and window resizes. - EventChan() <-chan interface{} - // Close closes the window. - Close() os.Error -} - -// A KeyEvent is sent for a key press or release. -type KeyEvent struct { - // The value k represents key k being pressed. - // The value -k represents key k being released. - // The specific set of key values is not specified, - // but ordinary characters represent themselves. - Key int -} - -// A MouseEvent is sent for a button press or release or for a mouse movement. -type MouseEvent struct { - // Buttons is a bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right. - // It represents button state and not necessarily the state delta: bit 0 - // being on means that the left mouse button is down, but does not imply - // that the same button was up in the previous MouseEvent. - Buttons int - // Loc is the location of the cursor. - Loc image.Point - // Nsec is the event's timestamp. - Nsec int64 -} - -// A ConfigEvent is sent each time the window's color model or size changes. -// The client should respond by calling Window.Screen to obtain a new image. -type ConfigEvent struct { - Config image.Config -} - -// An ErrEvent is sent when an error occurs. -type ErrEvent struct { - Err os.Error -} diff --git a/src/pkg/exp/gui/x11/Makefile b/src/pkg/exp/gui/x11/Makefile deleted file mode 100644 index 88cc1e23b..000000000 --- a/src/pkg/exp/gui/x11/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 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. - -include ../../../../Make.inc - -TARG=exp/gui/x11 -GOFILES=\ - auth.go\ - conn.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/exp/gui/x11/auth.go b/src/pkg/exp/gui/x11/auth.go deleted file mode 100644 index d48936ac1..000000000 --- a/src/pkg/exp/gui/x11/auth.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2009 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. - -package x11 - -import ( - "bufio" - "io" - "os" -) - -// readU16BE reads a big-endian uint16 from r, using b as a scratch buffer. -func readU16BE(r io.Reader, b []byte) (uint16, os.Error) { - _, err := io.ReadFull(r, b[0:2]) - if err != nil { - return 0, err - } - return uint16(b[0])<<8 + uint16(b[1]), nil -} - -// readStr reads a length-prefixed string from r, using b as a scratch buffer. -func readStr(r io.Reader, b []byte) (string, os.Error) { - n, err := readU16BE(r, b) - if err != nil { - return "", err - } - if int(n) > len(b) { - return "", os.NewError("Xauthority entry too long for buffer") - } - _, err = io.ReadFull(r, b[0:n]) - if err != nil { - return "", err - } - return string(b[0:n]), nil -} - -// readAuth reads the X authority file and returns the name/data pair for the display. -// displayStr is the "12" out of a $DISPLAY like ":12.0". -func readAuth(displayStr string) (name, data string, err os.Error) { - // b is a scratch buffer to use and should be at least 256 bytes long - // (i.e. it should be able to hold a hostname). - var b [256]byte - // As per /usr/include/X11/Xauth.h. - const familyLocal = 256 - - fn := os.Getenv("XAUTHORITY") - if fn == "" { - home := os.Getenv("HOME") - if home == "" { - err = os.NewError("Xauthority not found: $XAUTHORITY, $HOME not set") - return - } - fn = home + "/.Xauthority" - } - r, err := os.Open(fn) - if err != nil { - return - } - defer r.Close() - br := bufio.NewReader(r) - - hostname, err := os.Hostname() - if err != nil { - return - } - for { - family, err := readU16BE(br, b[0:2]) - if err != nil { - return - } - addr, err := readStr(br, b[0:]) - if err != nil { - return - } - disp, err := readStr(br, b[0:]) - if err != nil { - return - } - name0, err := readStr(br, b[0:]) - if err != nil { - return - } - data0, err := readStr(br, b[0:]) - if err != nil { - return - } - if family == familyLocal && addr == hostname && disp == displayStr { - return name0, data0, nil - } - } - panic("unreachable") -} diff --git a/src/pkg/exp/gui/x11/conn.go b/src/pkg/exp/gui/x11/conn.go deleted file mode 100644 index bc7ca63db..000000000 --- a/src/pkg/exp/gui/x11/conn.go +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright 2009 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. - -// Package x11 implements an X11 backend for the exp/gui package. -// -// The X protocol specification is at ftp://ftp.x.org/pub/X11R7.0/doc/PDF/proto.pdf. -// A summary of the wire format can be found in XCB's xproto.xml. -package x11 - -import ( - "bufio" - "exp/gui" - "image" - "image/draw" - "io" - "log" - "net" - "os" - "strconv" - "strings" - "time" -) - -type resID uint32 // X resource IDs. - -// TODO(nigeltao): Handle window resizes. -const ( - windowHeight = 600 - windowWidth = 800 -) - -const ( - keymapLo = 8 - keymapHi = 255 -) - -type conn struct { - c io.Closer - r *bufio.Reader - w *bufio.Writer - - gc, window, root, visual resID - - img *image.RGBA - eventc chan interface{} - mouseState gui.MouseEvent - - buf [256]byte // General purpose scratch buffer. - - flush chan bool - flushBuf0 [24]byte - flushBuf1 [4 * 1024]byte -} - -// writeSocket runs in its own goroutine, serving both FlushImage calls -// directly from the exp/gui client and indirectly from X expose events. -// It paints c.img to the X server via PutImage requests. -func (c *conn) writeSocket() { - defer c.c.Close() - for _ = range c.flush { - b := c.img.Bounds() - if b.Empty() { - continue - } - // Each X request has a 16-bit length (in terms of 4-byte units). To avoid going over - // this limit, we send PutImage for each row of the image, rather than trying to paint - // the entire image in one X request. This approach could easily be optimized (or the - // X protocol may have an escape sequence to delimit very large requests). - // TODO(nigeltao): See what XCB's xcb_put_image does in this situation. - units := 6 + b.Dx() - if units > 0xffff || b.Dy() > 0xffff { - log.Print("x11: window is too large for PutImage") - return - } - - c.flushBuf0[0] = 0x48 // PutImage opcode. - c.flushBuf0[1] = 0x02 // XCB_IMAGE_FORMAT_Z_PIXMAP. - c.flushBuf0[2] = uint8(units) - c.flushBuf0[3] = uint8(units >> 8) - setU32LE(c.flushBuf0[4:8], uint32(c.window)) - setU32LE(c.flushBuf0[8:12], uint32(c.gc)) - setU32LE(c.flushBuf0[12:16], 1<<16|uint32(b.Dx())) - c.flushBuf0[21] = 0x18 // depth = 24 bits. - - for y := b.Min.Y; y < b.Max.Y; y++ { - setU32LE(c.flushBuf0[16:20], uint32(y<<16)) - if _, err := c.w.Write(c.flushBuf0[0:24]); err != nil { - if err != os.EOF { - log.Println("x11:", err.String()) - } - return - } - p := c.img.Pix[y*c.img.Stride : (y+1)*c.img.Stride] - for x := b.Min.X; x < b.Max.X; { - nx := b.Max.X - x - if nx > len(c.flushBuf1)/4 { - nx = len(c.flushBuf1) / 4 - } - for i, rgba := range p[x : x+nx] { - c.flushBuf1[4*i+0] = rgba.B - c.flushBuf1[4*i+1] = rgba.G - c.flushBuf1[4*i+2] = rgba.R - } - x += nx - if _, err := c.w.Write(c.flushBuf1[0 : 4*nx]); err != nil { - if err != os.EOF { - log.Println("x11:", err.String()) - } - return - } - } - } - if err := c.w.Flush(); err != nil { - if err != os.EOF { - log.Println("x11:", err.String()) - } - return - } - } -} - -func (c *conn) Screen() draw.Image { return c.img } - -func (c *conn) FlushImage() { - select { - case c.flush <- false: - // Flush notification sent. - default: - // Could not send. - // Flush notification must be pending already. - } -} - -func (c *conn) Close() os.Error { - // Shut down the writeSocket goroutine. This will close the socket to the - // X11 server, which will cause c.eventc to close. - close(c.flush) - for _ = range c.eventc { - // Drain the channel to allow the readSocket goroutine to shut down. - } - return nil -} - -func (c *conn) EventChan() <-chan interface{} { return c.eventc } - -// readSocket runs in its own goroutine, reading X events and sending gui -// events on c's EventChan. -func (c *conn) readSocket() { - var ( - keymap [256][]int - keysymsPerKeycode int - ) - defer close(c.eventc) - for { - // X events are always 32 bytes long. - if _, err := io.ReadFull(c.r, c.buf[0:32]); err != nil { - if err != os.EOF { - c.eventc <- gui.ErrEvent{err} - } - return - } - switch c.buf[0] { - case 0x01: // Reply from a request (e.g. GetKeyboardMapping). - cookie := int(c.buf[3])<<8 | int(c.buf[2]) - if cookie != 1 { - // We issued only one request (GetKeyboardMapping) with a cookie of 1, - // so we shouldn't get any other reply from the X server. - c.eventc <- gui.ErrEvent{os.NewError("x11: unexpected cookie")} - return - } - keysymsPerKeycode = int(c.buf[1]) - b := make([]int, 256*keysymsPerKeycode) - for i := range keymap { - keymap[i] = b[i*keysymsPerKeycode : (i+1)*keysymsPerKeycode] - } - for i := keymapLo; i <= keymapHi; i++ { - m := keymap[i] - for j := range m { - u, err := readU32LE(c.r, c.buf[0:4]) - if err != nil { - if err != os.EOF { - c.eventc <- gui.ErrEvent{err} - } - return - } - m[j] = int(u) - } - } - case 0x02, 0x03: // Key press, key release. - // X Keyboard Encoding is documented at http://tronche.com/gui/x/xlib/input/keyboard-encoding.html - // TODO(nigeltao): Do we need to implement the "MODE SWITCH / group modifier" feature - // or is that some no-longer-used X construct? - if keysymsPerKeycode < 2 { - // Either we haven't yet received the GetKeyboardMapping reply or - // the X server has sent one that's too short. - continue - } - keycode := int(c.buf[1]) - shift := int(c.buf[28]) & 0x01 - keysym := keymap[keycode][shift] - if keysym == 0 { - keysym = keymap[keycode][0] - } - // TODO(nigeltao): Should we send KeyEvents for Shift/Ctrl/Alt? Should Shift-A send - // the same int down the channel as the sent on just the A key? - // TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or - // is that outside the scope of the gui.Window interface? - if c.buf[0] == 0x03 { - keysym = -keysym - } - c.eventc <- gui.KeyEvent{keysym} - case 0x04, 0x05: // Button press, button release. - mask := 1 << (c.buf[1] - 1) - if c.buf[0] == 0x04 { - c.mouseState.Buttons |= mask - } else { - c.mouseState.Buttons &^= mask - } - c.mouseState.Nsec = time.Nanoseconds() - c.eventc <- c.mouseState - case 0x06: // Motion notify. - c.mouseState.Loc.X = int(int16(c.buf[25])<<8 | int16(c.buf[24])) - c.mouseState.Loc.Y = int(int16(c.buf[27])<<8 | int16(c.buf[26])) - c.mouseState.Nsec = time.Nanoseconds() - c.eventc <- c.mouseState - case 0x0c: // Expose. - // A single user action could trigger multiple expose events (e.g. if moving another - // window with XShape'd rounded corners over our window). In that case, the X server will - // send a uint16 count (in bytes 16-17) of the number of additional expose events coming. - // We could parse each event for the (x, y, width, height) and maintain a minimal dirty - // rectangle, but for now, the simplest approach is to paint the entire window, when - // receiving the final event in the series. - if c.buf[17] == 0 && c.buf[16] == 0 { - // TODO(nigeltao): Should we ignore the very first expose event? A freshly mapped window - // will trigger expose, but until the first c.FlushImage call, there's probably nothing to - // paint but black. For an 800x600 window, at 4 bytes per pixel, each repaint writes about - // 2MB over the socket. - c.FlushImage() - } - // TODO(nigeltao): Should we listen to DestroyNotify (0x11) and ResizeRequest (0x19) events? - // What about EnterNotify (0x07) and LeaveNotify (0x08)? - } - } -} - -// connect connects to the X server given by the full X11 display name (e.g. -// ":12.0") and returns the connection as well as the portion of the full name -// that is the display number (e.g. "12"). -// Examples: -// connect(":1") // calls net.Dial("unix", "", "/tmp/.X11-unix/X1"), displayStr="1" -// connect("/tmp/launch-123/:0") // calls net.Dial("unix", "", "/tmp/launch-123/:0"), displayStr="0" -// connect("hostname:2.1") // calls net.Dial("tcp", "", "hostname:6002"), displayStr="2" -// connect("tcp/hostname:1.0") // calls net.Dial("tcp", "", "hostname:6001"), displayStr="1" -func connect(display string) (conn net.Conn, displayStr string, err os.Error) { - colonIdx := strings.LastIndex(display, ":") - if colonIdx < 0 { - return nil, "", os.NewError("bad display: " + display) - } - // Parse the section before the colon. - var protocol, host, socket string - if display[0] == '/' { - socket = display[0:colonIdx] - } else { - if i := strings.LastIndex(display, "/"); i < 0 { - // The default protocol is TCP. - protocol = "tcp" - host = display[0:colonIdx] - } else { - protocol = display[0:i] - host = display[i+1 : colonIdx] - } - } - // Parse the section after the colon. - after := display[colonIdx+1:] - if after == "" { - return nil, "", os.NewError("bad display: " + display) - } - if i := strings.LastIndex(after, "."); i < 0 { - displayStr = after - } else { - displayStr = after[0:i] - } - displayInt, err := strconv.Atoi(displayStr) - if err != nil || displayInt < 0 { - return nil, "", os.NewError("bad display: " + display) - } - // Make the connection. - if socket != "" { - conn, err = net.Dial("unix", socket+":"+displayStr) - } else if host != "" { - conn, err = net.Dial(protocol, host+":"+strconv.Itoa(6000+displayInt)) - } else { - conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+displayStr) - } - if err != nil { - return nil, "", os.NewError("cannot connect to " + display + ": " + err.String()) - } - return -} - -// authenticate authenticates ourselves with the X server. -// displayStr is the "12" out of ":12.0". -func authenticate(w *bufio.Writer, displayStr string) os.Error { - key, value, err := readAuth(displayStr) - if err != nil { - return err - } - // Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1". - if len(key) != 18 || len(value) != 16 { - return os.NewError("unsupported Xauth") - } - // 0x006c means little-endian. 0x000b, 0x0000 means X major version 11, minor version 0. - // 0x0012 and 0x0010 means the auth key and value have lengths 18 and 16. - // The final 0x0000 is padding, so that the string length is a multiple of 4. - _, err = io.WriteString(w, "\x6c\x00\x0b\x00\x00\x00\x12\x00\x10\x00\x00\x00") - if err != nil { - return err - } - _, err = io.WriteString(w, key) - if err != nil { - return err - } - // Again, the 0x0000 is padding. - _, err = io.WriteString(w, "\x00\x00") - if err != nil { - return err - } - _, err = io.WriteString(w, value) - if err != nil { - return err - } - err = w.Flush() - if err != nil { - return err - } - return nil -} - -// readU8 reads a uint8 from r, using b as a scratch buffer. -func readU8(r io.Reader, b []byte) (uint8, os.Error) { - _, err := io.ReadFull(r, b[0:1]) - if err != nil { - return 0, err - } - return uint8(b[0]), nil -} - -// readU16LE reads a little-endian uint16 from r, using b as a scratch buffer. -func readU16LE(r io.Reader, b []byte) (uint16, os.Error) { - _, err := io.ReadFull(r, b[0:2]) - if err != nil { - return 0, err - } - return uint16(b[0]) | uint16(b[1])<<8, nil -} - -// readU32LE reads a little-endian uint32 from r, using b as a scratch buffer. -func readU32LE(r io.Reader, b []byte) (uint32, os.Error) { - _, err := io.ReadFull(r, b[0:4]) - if err != nil { - return 0, err - } - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil -} - -// setU32LE sets b[0:4] to be the little-endian representation of u. -func setU32LE(b []byte, u uint32) { - b[0] = byte((u >> 0) & 0xff) - b[1] = byte((u >> 8) & 0xff) - b[2] = byte((u >> 16) & 0xff) - b[3] = byte((u >> 24) & 0xff) -} - -// checkPixmapFormats checks that we have an agreeable X pixmap Format. -func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err os.Error) { - for i := 0; i < n; i++ { - _, err = io.ReadFull(r, b[0:8]) - if err != nil { - return - } - // Byte 0 is depth, byte 1 is bits-per-pixel, byte 2 is scanline-pad, the rest (5) is padding. - if b[0] == 24 && b[1] == 32 { - agree = true - } - } - return -} - -// checkDepths checks that we have an agreeable X Depth (i.e. one that has an agreeable X VisualType). -func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err os.Error) { - for i := 0; i < n; i++ { - depth, err := readU16LE(r, b) - if err != nil { - return - } - depth &= 0xff - visualsLen, err := readU16LE(r, b) - if err != nil { - return - } - // Ignore 4 bytes of padding. - _, err = io.ReadFull(r, b[0:4]) - if err != nil { - return - } - for j := 0; j < int(visualsLen); j++ { - // Read 24 bytes: visual(4), class(1), bits per rgb value(1), colormap entries(2), - // red mask(4), green mask(4), blue mask(4), padding(4). - v, err := readU32LE(r, b) - _, err = readU32LE(r, b) - rm, err := readU32LE(r, b) - gm, err := readU32LE(r, b) - bm, err := readU32LE(r, b) - _, err = readU32LE(r, b) - if err != nil { - return - } - if v == visual && rm == 0xff0000 && gm == 0xff00 && bm == 0xff && depth == 24 { - agree = true - } - } - } - return -} - -// checkScreens checks that we have an agreeable X Screen. -func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err os.Error) { - for i := 0; i < n; i++ { - root0, err := readU32LE(r, b) - if err != nil { - return - } - // Ignore the next 7x4 bytes, which is: colormap, whitepixel, blackpixel, current input masks, - // width and height (pixels), width and height (mm), min and max installed maps. - _, err = io.ReadFull(r, b[0:28]) - if err != nil { - return - } - visual0, err := readU32LE(r, b) - if err != nil { - return - } - // Next 4 bytes: backing stores, save unders, root depth, allowed depths length. - x, err := readU32LE(r, b) - if err != nil { - return - } - nDepths := int(x >> 24) - agree, err := checkDepths(r, b, nDepths, visual0) - if err != nil { - return - } - if agree && root == 0 { - root = root0 - visual = visual0 - } - } - return -} - -// handshake performs the protocol handshake with the X server, and ensures -// that the server provides a compatible Screen, Depth, etc. -func (c *conn) handshake() os.Error { - _, err := io.ReadFull(c.r, c.buf[0:8]) - if err != nil { - return err - } - // Byte 0:1 should be 1 (success), bytes 2:6 should be 0xb0000000 (major/minor version 11.0). - if c.buf[0] != 1 || c.buf[2] != 11 || c.buf[3] != 0 || c.buf[4] != 0 || c.buf[5] != 0 { - return os.NewError("unsupported X version") - } - // Ignore the release number. - _, err = io.ReadFull(c.r, c.buf[0:4]) - if err != nil { - return err - } - // Read the resource ID base. - resourceIdBase, err := readU32LE(c.r, c.buf[0:4]) - if err != nil { - return err - } - // Read the resource ID mask. - resourceIdMask, err := readU32LE(c.r, c.buf[0:4]) - if err != nil { - return err - } - if resourceIdMask < 256 { - return os.NewError("X resource ID mask is too small") - } - // Ignore the motion buffer size. - _, err = io.ReadFull(c.r, c.buf[0:4]) - if err != nil { - return err - } - // Read the vendor length and round it up to a multiple of 4, - // for X11 protocol alignment reasons. - vendorLen, err := readU16LE(c.r, c.buf[0:2]) - if err != nil { - return err - } - vendorLen = (vendorLen + 3) &^ 3 - // Read the maximum request length. - maxReqLen, err := readU16LE(c.r, c.buf[0:2]) - if err != nil { - return err - } - if maxReqLen != 0xffff { - return os.NewError("unsupported X maximum request length") - } - // Read the roots length. - rootsLen, err := readU8(c.r, c.buf[0:1]) - if err != nil { - return err - } - // Read the pixmap formats length. - pixmapFormatsLen, err := readU8(c.r, c.buf[0:1]) - if err != nil { - return err - } - // Ignore some things that we don't care about (totaling 10 + vendorLen bytes): - // imageByteOrder(1), bitmapFormatBitOrder(1), bitmapFormatScanlineUnit(1) bitmapFormatScanlinePad(1), - // minKeycode(1), maxKeycode(1), padding(4), vendor (vendorLen). - if 10+int(vendorLen) > cap(c.buf) { - return os.NewError("unsupported X vendor") - } - _, err = io.ReadFull(c.r, c.buf[0:10+int(vendorLen)]) - if err != nil { - return err - } - // Check that we have an agreeable pixmap format. - agree, err := checkPixmapFormats(c.r, c.buf[0:8], int(pixmapFormatsLen)) - if err != nil { - return err - } - if !agree { - return os.NewError("unsupported X pixmap formats") - } - // Check that we have an agreeable screen. - root, visual, err := checkScreens(c.r, c.buf[0:24], int(rootsLen)) - if err != nil { - return err - } - if root == 0 || visual == 0 { - return os.NewError("unsupported X screen") - } - c.gc = resID(resourceIdBase) - c.window = resID(resourceIdBase + 1) - c.root = resID(root) - c.visual = resID(visual) - return nil -} - -// NewWindow calls NewWindowDisplay with $DISPLAY. -func NewWindow() (gui.Window, os.Error) { - display := os.Getenv("DISPLAY") - if len(display) == 0 { - return nil, os.NewError("$DISPLAY not set") - } - return NewWindowDisplay(display) -} - -// NewWindowDisplay returns a new gui.Window, backed by a newly created and -// mapped X11 window. The X server to connect to is specified by the display -// string, such as ":1". -func NewWindowDisplay(display string) (gui.Window, os.Error) { - socket, displayStr, err := connect(display) - if err != nil { - return nil, err - } - c := new(conn) - c.c = socket - c.r = bufio.NewReader(socket) - c.w = bufio.NewWriter(socket) - err = authenticate(c.w, displayStr) - if err != nil { - return nil, err - } - err = c.handshake() - if err != nil { - return nil, err - } - - // Now that we're connected, show a window, via three X protocol messages. - // First, issue a GetKeyboardMapping request. This is the first request, and - // will be associated with a cookie of 1. - setU32LE(c.buf[0:4], 0x00020065) // 0x65 is the GetKeyboardMapping opcode, and the message is 2 x 4 bytes long. - setU32LE(c.buf[4:8], uint32((keymapHi-keymapLo+1)<<8|keymapLo)) - // Second, create a graphics context (GC). - setU32LE(c.buf[8:12], 0x00060037) // 0x37 is the CreateGC opcode, and the message is 6 x 4 bytes long. - setU32LE(c.buf[12:16], uint32(c.gc)) - setU32LE(c.buf[16:20], uint32(c.root)) - setU32LE(c.buf[20:24], 0x00010004) // Bit 2 is XCB_GC_FOREGROUND, bit 16 is XCB_GC_GRAPHICS_EXPOSURES. - setU32LE(c.buf[24:28], 0x00000000) // The Foreground is black. - setU32LE(c.buf[28:32], 0x00000000) // GraphicsExposures' value is unused. - // Third, create the window. - setU32LE(c.buf[32:36], 0x000a0001) // 0x01 is the CreateWindow opcode, and the message is 10 x 4 bytes long. - setU32LE(c.buf[36:40], uint32(c.window)) - setU32LE(c.buf[40:44], uint32(c.root)) - setU32LE(c.buf[44:48], 0x00000000) // Initial (x, y) is (0, 0). - setU32LE(c.buf[48:52], windowHeight<<16|windowWidth) - setU32LE(c.buf[52:56], 0x00010000) // Border width is 0, XCB_WINDOW_CLASS_INPUT_OUTPUT is 1. - setU32LE(c.buf[56:60], uint32(c.visual)) - setU32LE(c.buf[60:64], 0x00000802) // Bit 1 is XCB_CW_BACK_PIXEL, bit 11 is XCB_CW_EVENT_MASK. - setU32LE(c.buf[64:68], 0x00000000) // The Back-Pixel is black. - setU32LE(c.buf[68:72], 0x0000804f) // Key/button press and release, pointer motion, and expose event masks. - // Fourth, map the window. - setU32LE(c.buf[72:76], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long. - setU32LE(c.buf[76:80], uint32(c.window)) - // Write the bytes. - _, err = c.w.Write(c.buf[0:80]) - if err != nil { - return nil, err - } - err = c.w.Flush() - if err != nil { - return nil, err - } - - c.img = image.NewRGBA(windowWidth, windowHeight) - c.eventc = make(chan interface{}, 16) - c.flush = make(chan bool, 1) - go c.readSocket() - go c.writeSocket() - return c, nil -} diff --git a/src/pkg/exp/ogle/Makefile b/src/pkg/exp/ogle/Makefile deleted file mode 100644 index ef65d36c8..000000000 --- a/src/pkg/exp/ogle/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2009 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. - -include ../../../Make.inc - -TARG=exp/ogle -GOFILES=\ - abort.go\ - arch.go\ - cmd.go\ - event.go\ - frame.go\ - goroutine.go\ - rruntime.go\ - rtype.go\ - rvalue.go\ - process.go\ - vars.go\ - -CLEANFILES+=ogle - -include ../../../Make.pkg - -main.$O: main.go package - $(GC) -I_obj $< - -ogle: main.$O - $(LD) -L_obj -o $@ $< diff --git a/src/pkg/exp/ogle/abort.go b/src/pkg/exp/ogle/abort.go deleted file mode 100644 index 311a7b38e..000000000 --- a/src/pkg/exp/ogle/abort.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "os" - "runtime" -) - -// An aborter aborts the thread's current computation, usually -// passing the error to a waiting thread. -type aborter interface { - Abort(err os.Error) -} - -type ogleAborter chan os.Error - -func (a ogleAborter) Abort(err os.Error) { - a <- err - runtime.Goexit() -} - -// try executes a computation; if the computation Aborts, try returns -// the error passed to abort. -func try(f func(a aborter)) os.Error { - a := make(ogleAborter) - go func() { - f(a) - a <- nil - }() - err := <-a - return err -} diff --git a/src/pkg/exp/ogle/arch.go b/src/pkg/exp/ogle/arch.go deleted file mode 100644 index 52b1c9757..000000000 --- a/src/pkg/exp/ogle/arch.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/proc" - "math" -) - -type Arch interface { - // ToWord converts an array of up to 8 bytes in memory order - // to a word. - ToWord(data []byte) proc.Word - // FromWord converts a word to an array of up to 8 bytes in - // memory order. - FromWord(v proc.Word, out []byte) - // ToFloat32 converts a word to a float. The order of this - // word will be the order returned by ToWord on the memory - // representation of a float, and thus may require reversing. - ToFloat32(bits uint32) float32 - // FromFloat32 converts a float to a word. This should return - // a word that can be passed to FromWord to get the memory - // representation of a float on this architecture. - FromFloat32(f float32) uint32 - // ToFloat64 is to float64 as ToFloat32 is to float32. - ToFloat64(bits uint64) float64 - // FromFloat64 is to float64 as FromFloat32 is to float32. - FromFloat64(f float64) uint64 - - // IntSize returns the number of bytes in an 'int'. - IntSize() int - // PtrSize returns the number of bytes in a 'uintptr'. - PtrSize() int - // FloatSize returns the number of bytes in a 'float'. - FloatSize() int - // Align rounds offset up to the appropriate offset for a - // basic type with the given width. - Align(offset, width int) int - - // G returns the current G pointer. - G(regs proc.Regs) proc.Word - - // ClosureSize returns the number of bytes expected by - // ParseClosure. - ClosureSize() int - // ParseClosure takes ClosureSize bytes read from a return PC - // in a remote process, determines if the code is a closure, - // and returns the frame size of the closure if it is. - ParseClosure(data []byte) (frame int, ok bool) -} - -type ArchLSB struct{} - -func (ArchLSB) ToWord(data []byte) proc.Word { - var v proc.Word - for i, b := range data { - v |= proc.Word(b) << (uint(i) * 8) - } - return v -} - -func (ArchLSB) FromWord(v proc.Word, out []byte) { - for i := range out { - out[i] = byte(v) - v >>= 8 - } -} - -func (ArchLSB) ToFloat32(bits uint32) float32 { - // TODO(austin) Do these definitions depend on my current - // architecture? - return math.Float32frombits(bits) -} - -func (ArchLSB) FromFloat32(f float32) uint32 { return math.Float32bits(f) } - -func (ArchLSB) ToFloat64(bits uint64) float64 { return math.Float64frombits(bits) } - -func (ArchLSB) FromFloat64(f float64) uint64 { return math.Float64bits(f) } - -type ArchAlignedMultiple struct{} - -func (ArchAlignedMultiple) Align(offset, width int) int { - return ((offset - 1) | (width - 1)) + 1 -} - -type amd64 struct { - ArchLSB - ArchAlignedMultiple - gReg int -} - -func (a *amd64) IntSize() int { return 4 } - -func (a *amd64) PtrSize() int { return 8 } - -func (a *amd64) FloatSize() int { return 4 } - -func (a *amd64) G(regs proc.Regs) proc.Word { - // See src/pkg/runtime/mkasmh - if a.gReg == -1 { - ns := regs.Names() - for i, n := range ns { - if n == "r15" { - a.gReg = i - break - } - } - } - - return regs.Get(a.gReg) -} - -func (a *amd64) ClosureSize() int { return 8 } - -func (a *amd64) ParseClosure(data []byte) (int, bool) { - if data[0] == 0x48 && data[1] == 0x81 && data[2] == 0xc4 && data[7] == 0xc3 { - return int(a.ToWord(data[3:7]) + 8), true - } - return 0, false -} - -var Amd64 = &amd64{gReg: -1} diff --git a/src/pkg/exp/ogle/cmd.go b/src/pkg/exp/ogle/cmd.go deleted file mode 100644 index ff0d24c69..000000000 --- a/src/pkg/exp/ogle/cmd.go +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2009 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. - -// Package ogle is the beginning of a debugger for Go. -package ogle - -import ( - "bufio" - "debug/elf" - "debug/proc" - "exp/eval" - "fmt" - "go/scanner" - "go/token" - "os" - "strconv" - "strings" -) - -var fset = token.NewFileSet() -var world *eval.World -var curProc *Process - -func Main() { - world = eval.NewWorld() - defineFuncs() - r := bufio.NewReader(os.Stdin) - for { - print("; ") - line, err := r.ReadSlice('\n') - if err != nil { - break - } - - // Try line as a command - cmd, rest := getCmd(line) - if cmd != nil { - err := cmd.handler(rest) - if err != nil { - scanner.PrintError(os.Stderr, err) - } - continue - } - - // Try line as code - code, err := world.Compile(fset, string(line)) - if err != nil { - scanner.PrintError(os.Stderr, err) - continue - } - v, err := code.Run() - if err != nil { - fmt.Fprintf(os.Stderr, err.String()) - continue - } - if v != nil { - println(v.String()) - } - } -} - -// newScanner creates a new scanner that scans that given input bytes. -func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) { - sc := new(scanner.Scanner) - ev := new(scanner.ErrorVector) - file := fset.AddFile("input", fset.Base(), len(input)) - sc.Init(file, input, ev, 0) - return sc, ev -} - -/* - * Commands - */ - -// A UsageError occurs when a command is called with illegal arguments. -type UsageError string - -func (e UsageError) String() string { return string(e) } - -// A cmd represents a single command with a handler. -type cmd struct { - cmd string - handler func([]byte) os.Error -} - -var cmds = []cmd{ - {"load", cmdLoad}, - {"bt", cmdBt}, -} - -// getCmd attempts to parse an input line as a registered command. If -// successful, it returns the command and the bytes remaining after -// the command, which should be passed to the command. -func getCmd(line []byte) (*cmd, []byte) { - sc, _ := newScanner(line) - pos, tok, lit := sc.Scan() - if sc.ErrorCount != 0 || tok != token.IDENT { - return nil, nil - } - - slit := string(lit) - for i := range cmds { - if cmds[i].cmd == slit { - return &cmds[i], line[fset.Position(pos).Offset+len(lit):] - } - } - return nil, nil -} - -// cmdLoad starts or attaches to a process. Its form is similar to -// import: -// -// load [sym] "path" [;] -// -// sym specifies the name to give to the process. If not given, the -// name is derived from the path of the process. If ".", then the -// packages from the remote process are defined into the current -// namespace. If given, this symbol is defined as a package -// containing the process' packages. -// -// path gives the path of the process to start or attach to. If it is -// "pid:<num>", then attach to the given PID. Otherwise, treat it as -// a file path and space-separated arguments and start a new process. -// -// load always sets the current process to the loaded process. -func cmdLoad(args []byte) os.Error { - ident, path, err := parseLoad(args) - if err != nil { - return err - } - if curProc != nil { - return UsageError("multiple processes not implemented") - } - if ident != "." { - return UsageError("process identifiers not implemented") - } - - // Parse argument and start or attach to process - var fname string - var tproc proc.Process - if len(path) >= 4 && path[0:4] == "pid:" { - pid, err := strconv.Atoi(path[4:]) - if err != nil { - return err - } - fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid)) - if err != nil { - return err - } - tproc, err = proc.Attach(pid) - if err != nil { - return err - } - println("Attached to", pid) - } else { - parts := strings.Split(path, " ") - if len(parts) == 0 { - fname = "" - } else { - fname = parts[0] - } - tproc, err = proc.StartProcess(fname, parts, &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}}) - if err != nil { - return err - } - println("Started", path) - // TODO(austin) If we fail after this point, kill tproc - // before detaching. - } - - // Get symbols - f, err := os.Open(fname) - if err != nil { - tproc.Detach() - return err - } - defer f.Close() - elf, err := elf.NewFile(f) - if err != nil { - tproc.Detach() - return err - } - curProc, err = NewProcessElf(tproc, elf) - if err != nil { - tproc.Detach() - return err - } - - // Prepare new process - curProc.OnGoroutineCreate().AddHandler(EventPrint) - curProc.OnGoroutineExit().AddHandler(EventPrint) - - err = curProc.populateWorld(world) - if err != nil { - tproc.Detach() - return err - } - - return nil -} - -func parseLoad(args []byte) (ident string, path string, err os.Error) { - err = UsageError("Usage: load [sym] \"path\"") - sc, ev := newScanner(args) - - var toks [4]token.Token - var lits [4]string - for i := range toks { - _, toks[i], lits[i] = sc.Scan() - } - if sc.ErrorCount != 0 { - err = ev.GetError(scanner.NoMultiples) - return - } - - i := 0 - switch toks[i] { - case token.PERIOD, token.IDENT: - ident = string(lits[i]) - i++ - } - - if toks[i] != token.STRING { - return - } - path, uerr := strconv.Unquote(string(lits[i])) - if uerr != nil { - err = uerr - return - } - i++ - - if toks[i] == token.SEMICOLON { - i++ - } - if toks[i] != token.EOF { - return - } - - return ident, path, nil -} - -// cmdBt prints a backtrace for the current goroutine. It takes no -// arguments. -func cmdBt(args []byte) os.Error { - err := parseNoArgs(args, "Usage: bt") - if err != nil { - return err - } - - if curProc == nil || curProc.curGoroutine == nil { - return NoCurrentGoroutine{} - } - - f := curProc.curGoroutine.frame - if f == nil { - fmt.Println("No frames on stack") - return nil - } - - for f.Inner() != nil { - f = f.Inner() - } - - for i := 0; i < 100; i++ { - if f == curProc.curGoroutine.frame { - fmt.Printf("=> ") - } else { - fmt.Printf(" ") - } - fmt.Printf("%8x %v\n", f.pc, f) - f, err = f.Outer() - if err != nil { - return err - } - if f == nil { - return nil - } - } - - fmt.Println("...") - return nil -} - -func parseNoArgs(args []byte, usage string) os.Error { - sc, ev := newScanner(args) - _, tok, _ := sc.Scan() - if sc.ErrorCount != 0 { - return ev.GetError(scanner.NoMultiples) - } - if tok != token.EOF { - return UsageError(usage) - } - return nil -} - -/* - * Functions - */ - -// defineFuncs populates world with the built-in functions. -func defineFuncs() { - t, v := eval.FuncFromNativeTyped(fnOut, fnOutSig) - world.DefineConst("Out", t, v) - t, v = eval.FuncFromNativeTyped(fnContWait, fnContWaitSig) - world.DefineConst("ContWait", t, v) - t, v = eval.FuncFromNativeTyped(fnBpSet, fnBpSetSig) - world.DefineConst("BpSet", t, v) -} - -// printCurFrame prints the current stack frame, as it would appear in -// a backtrace. -func printCurFrame() { - if curProc == nil || curProc.curGoroutine == nil { - return - } - f := curProc.curGoroutine.frame - if f == nil { - return - } - fmt.Printf("=> %8x %v\n", f.pc, f) -} - -// fnOut moves the current frame to the caller of the current frame. -func fnOutSig() {} -func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) { - if curProc == nil { - t.Abort(NoCurrentGoroutine{}) - } - err := curProc.Out() - if err != nil { - t.Abort(err) - } - // TODO(austin) Only in the command form - printCurFrame() -} - -// fnContWait continues the current process and waits for a stopping event. -func fnContWaitSig() {} -func fnContWait(t *eval.Thread, args []eval.Value, res []eval.Value) { - if curProc == nil { - t.Abort(NoCurrentGoroutine{}) - } - err := curProc.ContWait() - if err != nil { - t.Abort(err) - } - // TODO(austin) Only in the command form - ev := curProc.Event() - if ev != nil { - fmt.Printf("%v\n", ev) - } - printCurFrame() -} - -// fnBpSet sets a breakpoint at the entry to the named function. -func fnBpSetSig(string) {} -func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) { - // TODO(austin) This probably shouldn't take a symbol name. - // Perhaps it should take an interface that provides PC's. - // Functions and instructions can implement that interface and - // we can have something to translate file:line pairs. - if curProc == nil { - t.Abort(NoCurrentGoroutine{}) - } - name := args[0].(eval.StringValue).Get(t) - fn := curProc.syms.LookupFunc(name) - if fn == nil { - t.Abort(UsageError("no such function " + name)) - } - curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop) -} diff --git a/src/pkg/exp/ogle/event.go b/src/pkg/exp/ogle/event.go deleted file mode 100644 index d7092ded3..000000000 --- a/src/pkg/exp/ogle/event.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/proc" - "fmt" - "os" -) - -/* - * Hooks and events - */ - -// An EventHandler is a function that takes an event and returns a -// response to that event and possibly an error. If an event handler -// returns an error, the process stops and no other handlers for that -// event are executed. -type EventHandler func(e Event) (EventAction, os.Error) - -// An EventAction is an event handler's response to an event. If all -// of an event's handlers execute without returning errors, their -// results are combined as follows: If any handler returned -// EAContinue, then the process resumes (without returning from -// WaitStop); otherwise, if any handler returned EAStop, the process -// remains stopped; otherwise, if all handlers returned EADefault, the -// process resumes. A handler may return EARemoveSelf bit-wise or'd -// with any other action to indicate that the handler should be -// removed from the hook. -type EventAction int - -const ( - EARemoveSelf EventAction = 0x100 - EADefault EventAction = iota - EAStop - EAContinue -) - -// A EventHook allows event handlers to be added and removed. -type EventHook interface { - AddHandler(EventHandler) - RemoveHandler(EventHandler) - NumHandler() int - handle(e Event) (EventAction, os.Error) - String() string -} - -// EventHook is almost, but not quite, suitable for user-defined -// events. If we want user-defined events, make EventHook a struct, -// special-case adding and removing handlers in breakpoint hooks, and -// provide a public interface for posting events to hooks. - -type Event interface { - Process() *Process - Goroutine() *Goroutine - String() string -} - -type commonHook struct { - // Head of handler chain - head *handler - // Number of non-internal handlers - len int -} - -type handler struct { - eh EventHandler - // True if this handler must be run before user-defined - // handlers in order to ensure correctness. - internal bool - // True if this handler has been removed from the chain. - removed bool - next *handler -} - -func (h *commonHook) AddHandler(eh EventHandler) { - h.addHandler(eh, false) -} - -func (h *commonHook) addHandler(eh EventHandler, internal bool) { - // Ensure uniqueness of handlers - h.RemoveHandler(eh) - - if !internal { - h.len++ - } - // Add internal handlers to the beginning - if internal || h.head == nil { - h.head = &handler{eh, internal, false, h.head} - return - } - // Add handler after internal handlers - // TODO(austin) This should probably go on the end instead - prev := h.head - for prev.next != nil && prev.internal { - prev = prev.next - } - prev.next = &handler{eh, internal, false, prev.next} -} - -func (h *commonHook) RemoveHandler(eh EventHandler) { - plink := &h.head - for l := *plink; l != nil; plink, l = &l.next, l.next { - if l.eh == eh { - if !l.internal { - h.len-- - } - l.removed = true - *plink = l.next - break - } - } -} - -func (h *commonHook) NumHandler() int { return h.len } - -func (h *commonHook) handle(e Event) (EventAction, os.Error) { - action := EADefault - plink := &h.head - for l := *plink; l != nil; plink, l = &l.next, l.next { - if l.removed { - continue - } - a, err := l.eh(e) - if a&EARemoveSelf == EARemoveSelf { - if !l.internal { - h.len-- - } - l.removed = true - *plink = l.next - a &^= EARemoveSelf - } - if err != nil { - return EAStop, err - } - if a > action { - action = a - } - } - return action, nil -} - -type commonEvent struct { - // The process of this event - p *Process - // The goroutine of this event. - t *Goroutine -} - -func (e *commonEvent) Process() *Process { return e.p } - -func (e *commonEvent) Goroutine() *Goroutine { return e.t } - -/* - * Standard event handlers - */ - -// EventPrint is a standard event handler that prints events as they -// occur. It will not cause the process to stop. -func EventPrint(ev Event) (EventAction, os.Error) { - // TODO(austin) Include process name here? - fmt.Fprintf(os.Stderr, "*** %v\n", ev.String()) - return EADefault, nil -} - -// EventStop is a standard event handler that causes the process to stop. -func EventStop(ev Event) (EventAction, os.Error) { - return EAStop, nil -} - -/* - * Breakpoints - */ - -type breakpointHook struct { - commonHook - p *Process - pc proc.Word -} - -// A Breakpoint event occurs when a process reaches a particular -// program counter. When this event is handled, the current goroutine -// will be the goroutine that reached the program counter. -type Breakpoint struct { - commonEvent - osThread proc.Thread - pc proc.Word -} - -func (h *breakpointHook) AddHandler(eh EventHandler) { - h.addHandler(eh, false) -} - -func (h *breakpointHook) addHandler(eh EventHandler, internal bool) { - // We register breakpoint events lazily to avoid holding - // references to breakpoints without handlers. Be sure to use - // the "canonical" breakpoint if there is one. - if cur, ok := h.p.breakpointHooks[h.pc]; ok { - h = cur - } - oldhead := h.head - h.commonHook.addHandler(eh, internal) - if oldhead == nil && h.head != nil { - h.p.proc.AddBreakpoint(h.pc) - h.p.breakpointHooks[h.pc] = h - } -} - -func (h *breakpointHook) RemoveHandler(eh EventHandler) { - oldhead := h.head - h.commonHook.RemoveHandler(eh) - if oldhead != nil && h.head == nil { - h.p.proc.RemoveBreakpoint(h.pc) - h.p.breakpointHooks[h.pc] = nil, false - } -} - -func (h *breakpointHook) String() string { - // TODO(austin) Include process name? - // TODO(austin) Use line:pc or at least sym+%#x - return fmt.Sprintf("breakpoint at %#x", h.pc) -} - -func (b *Breakpoint) PC() proc.Word { return b.pc } - -func (b *Breakpoint) String() string { - // TODO(austin) Include process name and goroutine - // TODO(austin) Use line:pc or at least sym+%#x - return fmt.Sprintf("breakpoint at %#x", b.pc) -} - -/* - * Goroutine create/exit - */ - -type goroutineCreateHook struct { - commonHook -} - -func (h *goroutineCreateHook) String() string { return "goroutine create" } - -// A GoroutineCreate event occurs when a process creates a new -// goroutine. When this event is handled, the current goroutine will -// be the newly created goroutine. -type GoroutineCreate struct { - commonEvent - parent *Goroutine -} - -// Parent returns the goroutine that created this goroutine. May be -// nil if this event is the creation of the first goroutine. -func (e *GoroutineCreate) Parent() *Goroutine { return e.parent } - -func (e *GoroutineCreate) String() string { - // TODO(austin) Include process name - if e.parent == nil { - return fmt.Sprintf("%v created", e.t) - } - return fmt.Sprintf("%v created by %v", e.t, e.parent) -} - -type goroutineExitHook struct { - commonHook -} - -func (h *goroutineExitHook) String() string { return "goroutine exit" } - -// A GoroutineExit event occurs when a Go goroutine exits. -type GoroutineExit struct { - commonEvent -} - -func (e *GoroutineExit) String() string { - // TODO(austin) Include process name - //return fmt.Sprintf("%v exited", e.t); - // For debugging purposes - return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base) -} diff --git a/src/pkg/exp/ogle/frame.go b/src/pkg/exp/ogle/frame.go deleted file mode 100644 index 1538362ba..000000000 --- a/src/pkg/exp/ogle/frame.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/gosym" - "debug/proc" - "fmt" - "os" -) - -// A Frame represents a single frame on a remote call stack. -type Frame struct { - // pc is the PC of the next instruction that will execute in - // this frame. For lower frames, this is the instruction - // following the CALL instruction. - pc, sp, fp proc.Word - // The runtime.Stktop of the active stack segment - stk remoteStruct - // The function this stack frame is in - fn *gosym.Func - // The path and line of the CALL or current instruction. Note - // that this differs slightly from the meaning of Frame.pc. - path string - line int - // The inner and outer frames of this frame. outer is filled - // in lazily. - inner, outer *Frame -} - -// newFrame returns the top-most Frame of the given g's thread. -func newFrame(g remoteStruct) (*Frame, os.Error) { - var f *Frame - err := try(func(a aborter) { f = aNewFrame(a, g) }) - return f, err -} - -func aNewFrame(a aborter, g remoteStruct) *Frame { - p := g.r.p - var pc, sp proc.Word - - // Is this G alive? - switch g.field(p.f.G.Status).(remoteInt).aGet(a) { - case p.runtime.Gidle, p.runtime.Gmoribund, p.runtime.Gdead: - return nil - } - - // Find the OS thread for this G - - // TODO(austin) Ideally, we could look at the G's state and - // figure out if it's on an OS thread or not. However, this - // is difficult because the state isn't updated atomically - // with scheduling changes. - for _, t := range p.proc.Threads() { - regs, err := t.Regs() - if err != nil { - // TODO(austin) What to do? - continue - } - thisg := p.G(regs) - if thisg == g.addr().base { - // Found this G's OS thread - pc = regs.PC() - sp = regs.SP() - - // If this thread crashed, try to recover it - if pc == 0 { - pc = p.peekUintptr(a, pc) - sp += 8 - } - - break - } - } - - if pc == 0 && sp == 0 { - // G is not mapped to an OS thread. Use the - // scheduler's stored PC and SP. - sched := g.field(p.f.G.Sched).(remoteStruct) - pc = proc.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a)) - sp = proc.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a)) - } - - // Get Stktop - stk := g.field(p.f.G.Stackbase).(remotePtr).aGet(a).(remoteStruct) - - return prepareFrame(a, pc, sp, stk, nil) -} - -// prepareFrame creates a Frame from the PC and SP within that frame, -// as well as the active stack segment. This function takes care of -// traversing stack breaks and unwinding closures. -func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *Frame { - // Based on src/pkg/runtime/amd64/traceback.c:traceback - p := stk.r.p - top := inner == nil - - // Get function - var path string - var line int - var fn *gosym.Func - - for i := 0; i < 100; i++ { - // Traverse segmented stack breaks - if p.sys.lessstack != nil && pc == proc.Word(p.sys.lessstack.Value) { - // Get stk->gobuf.pc - pc = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a)) - // Get stk->gobuf.sp - sp = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Sp).(remoteUint).aGet(a)) - // Get stk->stackbase - stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct) - continue - } - - // Get the PC of the call instruction - callpc := pc - if !top && (p.sys.goexit == nil || pc != proc.Word(p.sys.goexit.Value)) { - callpc-- - } - - // Look up function - path, line, fn = p.syms.PCToLine(uint64(callpc)) - if fn != nil { - break - } - - // Closure? - var buf = make([]byte, p.ClosureSize()) - if _, err := p.Peek(pc, buf); err != nil { - break - } - spdelta, ok := p.ParseClosure(buf) - if ok { - sp += proc.Word(spdelta) - pc = p.peekUintptr(a, sp-proc.Word(p.PtrSize())) - } - } - if fn == nil { - return nil - } - - // Compute frame pointer - var fp proc.Word - if fn.FrameSize < p.PtrSize() { - fp = sp + proc.Word(p.PtrSize()) - } else { - fp = sp + proc.Word(fn.FrameSize) - } - // TODO(austin) To really figure out if we're in the prologue, - // we need to disassemble the function and look for the call - // to morestack. For now, just special case the entry point. - // - // TODO(austin) What if we're in the call to morestack in the - // prologue? Then top == false. - if top && pc == proc.Word(fn.Entry) { - // We're in the function prologue, before SP - // has been adjusted for the frame. - fp -= proc.Word(fn.FrameSize - p.PtrSize()) - } - - return &Frame{pc, sp, fp, stk, fn, path, line, inner, nil} -} - -// Outer returns the Frame that called this Frame, or nil if this is -// the outermost frame. -func (f *Frame) Outer() (*Frame, os.Error) { - var fr *Frame - err := try(func(a aborter) { fr = f.aOuter(a) }) - return fr, err -} - -func (f *Frame) aOuter(a aborter) *Frame { - // Is there a cached outer frame - if f.outer != nil { - return f.outer - } - - p := f.stk.r.p - - sp := f.fp - if f.fn == p.sys.newproc && f.fn == p.sys.deferproc { - // TODO(rsc) The compiler inserts two push/pop's - // around calls to go and defer. Russ says this - // should get fixed in the compiler, but we account - // for it for now. - sp += proc.Word(2 * p.PtrSize()) - } - - pc := p.peekUintptr(a, f.fp-proc.Word(p.PtrSize())) - if pc < 0x1000 { - return nil - } - - // TODO(austin) Register this frame for shoot-down. - - f.outer = prepareFrame(a, pc, sp, f.stk, f) - return f.outer -} - -// Inner returns the Frame called by this Frame, or nil if this is the -// innermost frame. -func (f *Frame) Inner() *Frame { return f.inner } - -func (f *Frame) String() string { - res := f.fn.Name - if f.pc > proc.Word(f.fn.Value) { - res += fmt.Sprintf("+%#x", f.pc-proc.Word(f.fn.Entry)) - } - return res + fmt.Sprintf(" %s:%d", f.path, f.line) -} diff --git a/src/pkg/exp/ogle/goroutine.go b/src/pkg/exp/ogle/goroutine.go deleted file mode 100644 index 5104ec6d4..000000000 --- a/src/pkg/exp/ogle/goroutine.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/proc" - "exp/eval" - "fmt" - "os" -) - -// A Goroutine represents a goroutine in a remote process. -type Goroutine struct { - g remoteStruct - frame *Frame - dead bool -} - -func (t *Goroutine) String() string { - if t.dead { - return "<dead thread>" - } - // TODO(austin) Give threads friendly ID's, possibly including - // the name of the entry function. - return fmt.Sprintf("thread %#x", t.g.addr().base) -} - -// isG0 returns true if this thread if the internal idle thread -func (t *Goroutine) isG0() bool { return t.g.addr().base == t.g.r.p.sys.g0.addr().base } - -func (t *Goroutine) resetFrame() (err os.Error) { - // TODO(austin) Reuse any live part of the current frame stack - // so existing references to Frame's keep working. - t.frame, err = newFrame(t.g) - return -} - -// Out selects the caller frame of the current frame. -func (t *Goroutine) Out() os.Error { - f, err := t.frame.Outer() - if f != nil { - t.frame = f - } - return err -} - -// In selects the frame called by the current frame. -func (t *Goroutine) In() os.Error { - f := t.frame.Inner() - if f != nil { - t.frame = f - } - return nil -} - -func readylockedBP(ev Event) (EventAction, os.Error) { - b := ev.(*Breakpoint) - p := b.Process() - - // The new g is the only argument to this function, so the - // stack will have the return address, then the G*. - regs, err := b.osThread.Regs() - if err != nil { - return EAStop, err - } - sp := regs.SP() - addr := sp + proc.Word(p.PtrSize()) - arg := remotePtr{remote{addr, p}, p.runtime.G} - var gp eval.Value - err = try(func(a aborter) { gp = arg.aGet(a) }) - if err != nil { - return EAStop, err - } - if gp == nil { - return EAStop, UnknownGoroutine{b.osThread, 0} - } - gs := gp.(remoteStruct) - g := &Goroutine{gs, nil, false} - p.goroutines[gs.addr().base] = g - - // Enqueue goroutine creation event - parent := b.Goroutine() - if parent.isG0() { - parent = nil - } - p.postEvent(&GoroutineCreate{commonEvent{p, g}, parent}) - - // If we don't have any thread selected, select this one - if p.curGoroutine == nil { - p.curGoroutine = g - } - - return EADefault, nil -} - -func goexitBP(ev Event) (EventAction, os.Error) { - b := ev.(*Breakpoint) - p := b.Process() - - g := b.Goroutine() - g.dead = true - - addr := g.g.addr().base - p.goroutines[addr] = nil, false - - // Enqueue thread exit event - p.postEvent(&GoroutineExit{commonEvent{p, g}}) - - // If we just exited our selected goroutine, selected another - if p.curGoroutine == g { - p.selectSomeGoroutine() - } - - return EADefault, nil -} diff --git a/src/pkg/exp/ogle/main.go b/src/pkg/exp/ogle/main.go deleted file mode 100644 index 1999eccca..000000000 --- a/src/pkg/exp/ogle/main.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2009 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. - -package main - -import "exp/ogle" - -func main() { ogle.Main() } diff --git a/src/pkg/exp/ogle/process.go b/src/pkg/exp/ogle/process.go deleted file mode 100644 index 7c803b3a2..000000000 --- a/src/pkg/exp/ogle/process.go +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/elf" - "debug/gosym" - "debug/proc" - "exp/eval" - "fmt" - "log" - "os" - "reflect" -) - -// A FormatError indicates a failure to process information in or -// about a remote process, such as unexpected or missing information -// in the object file or runtime structures. -type FormatError string - -func (e FormatError) String() string { return string(e) } - -// An UnknownArchitecture occurs when trying to load an object file -// that indicates an architecture not supported by the debugger. -type UnknownArchitecture elf.Machine - -func (e UnknownArchitecture) String() string { - return "unknown architecture: " + elf.Machine(e).String() -} - -// A ProcessNotStopped error occurs when attempting to read or write -// memory or registers of a process that is not stopped. -type ProcessNotStopped struct{} - -func (e ProcessNotStopped) String() string { return "process not stopped" } - -// An UnknownGoroutine error is an internal error representing an -// unrecognized G structure pointer. -type UnknownGoroutine struct { - OSThread proc.Thread - Goroutine proc.Word -} - -func (e UnknownGoroutine) String() string { - return fmt.Sprintf("internal error: unknown goroutine (G %#x)", e.Goroutine) -} - -// A NoCurrentGoroutine error occurs when no goroutine is currently -// selected in a process (or when there are no goroutines in a -// process). -type NoCurrentGoroutine struct{} - -func (e NoCurrentGoroutine) String() string { return "no current goroutine" } - -// A Process represents a remote attached process. -type Process struct { - Arch - proc proc.Process - - // The symbol table of this process - syms *gosym.Table - - // A possibly-stopped OS thread, or nil - threadCache proc.Thread - - // Types parsed from the remote process - types map[proc.Word]*remoteType - - // Types and values from the remote runtime package - runtime runtimeValues - - // Runtime field indexes - f runtimeIndexes - - // Globals from the sys package (or from no package) - sys struct { - lessstack, goexit, newproc, deferproc, newprocreadylocked *gosym.Func - allg remotePtr - g0 remoteStruct - } - - // Event queue - posted []Event - pending []Event - event Event - - // Event hooks - breakpointHooks map[proc.Word]*breakpointHook - goroutineCreateHook *goroutineCreateHook - goroutineExitHook *goroutineExitHook - - // Current goroutine, or nil if there are no goroutines - curGoroutine *Goroutine - - // Goroutines by the address of their G structure - goroutines map[proc.Word]*Goroutine -} - -/* - * Process creation - */ - -// NewProcess constructs a new remote process around a traced -// process, an architecture, and a symbol table. -func NewProcess(tproc proc.Process, arch Arch, syms *gosym.Table) (*Process, os.Error) { - p := &Process{ - Arch: arch, - proc: tproc, - syms: syms, - types: make(map[proc.Word]*remoteType), - breakpointHooks: make(map[proc.Word]*breakpointHook), - goroutineCreateHook: new(goroutineCreateHook), - goroutineExitHook: new(goroutineExitHook), - goroutines: make(map[proc.Word]*Goroutine), - } - - // Fill in remote runtime - p.bootstrap() - - switch { - case p.sys.allg.addr().base == 0: - return nil, FormatError("failed to find runtime symbol 'allg'") - case p.sys.g0.addr().base == 0: - return nil, FormatError("failed to find runtime symbol 'g0'") - case p.sys.newprocreadylocked == nil: - return nil, FormatError("failed to find runtime symbol 'newprocreadylocked'") - case p.sys.goexit == nil: - return nil, FormatError("failed to find runtime symbol 'sys.goexit'") - } - - // Get current goroutines - p.goroutines[p.sys.g0.addr().base] = &Goroutine{p.sys.g0, nil, false} - err := try(func(a aborter) { - g := p.sys.allg.aGet(a) - for g != nil { - gs := g.(remoteStruct) - fmt.Printf("*** Found goroutine at %#x\n", gs.addr().base) - p.goroutines[gs.addr().base] = &Goroutine{gs, nil, false} - g = gs.field(p.f.G.Alllink).(remotePtr).aGet(a) - } - }) - if err != nil { - return nil, err - } - - // Create internal breakpoints to catch new and exited goroutines - p.OnBreakpoint(proc.Word(p.sys.newprocreadylocked.Entry)).(*breakpointHook).addHandler(readylockedBP, true) - p.OnBreakpoint(proc.Word(p.sys.goexit.Entry)).(*breakpointHook).addHandler(goexitBP, true) - - // Select current frames - for _, g := range p.goroutines { - g.resetFrame() - } - - p.selectSomeGoroutine() - - return p, nil -} - -func elfGoSyms(f *elf.File) (*gosym.Table, os.Error) { - text := f.Section(".text") - symtab := f.Section(".gosymtab") - pclntab := f.Section(".gopclntab") - if text == nil || symtab == nil || pclntab == nil { - return nil, nil - } - - symdat, err := symtab.Data() - if err != nil { - return nil, err - } - pclndat, err := pclntab.Data() - if err != nil { - return nil, err - } - - pcln := gosym.NewLineTable(pclndat, text.Addr) - tab, err := gosym.NewTable(symdat, pcln) - if err != nil { - return nil, err - } - - return tab, nil -} - -// NewProcessElf constructs a new remote process around a traced -// process and the process' ELF object. -func NewProcessElf(tproc proc.Process, f *elf.File) (*Process, os.Error) { - syms, err := elfGoSyms(f) - if err != nil { - return nil, err - } - if syms == nil { - return nil, FormatError("Failed to find symbol table") - } - var arch Arch - switch f.Machine { - case elf.EM_X86_64: - arch = Amd64 - default: - return nil, UnknownArchitecture(f.Machine) - } - return NewProcess(tproc, arch, syms) -} - -// bootstrap constructs the runtime structure of a remote process. -func (p *Process) bootstrap() { - // Manually construct runtime types - p.runtime.String = newManualType(eval.TypeOfNative(rt1String{}), p.Arch) - p.runtime.Slice = newManualType(eval.TypeOfNative(rt1Slice{}), p.Arch) - p.runtime.Eface = newManualType(eval.TypeOfNative(rt1Eface{}), p.Arch) - - p.runtime.Type = newManualType(eval.TypeOfNative(rt1Type{}), p.Arch) - p.runtime.CommonType = newManualType(eval.TypeOfNative(rt1CommonType{}), p.Arch) - p.runtime.UncommonType = newManualType(eval.TypeOfNative(rt1UncommonType{}), p.Arch) - p.runtime.StructField = newManualType(eval.TypeOfNative(rt1StructField{}), p.Arch) - p.runtime.StructType = newManualType(eval.TypeOfNative(rt1StructType{}), p.Arch) - p.runtime.PtrType = newManualType(eval.TypeOfNative(rt1PtrType{}), p.Arch) - p.runtime.ArrayType = newManualType(eval.TypeOfNative(rt1ArrayType{}), p.Arch) - p.runtime.SliceType = newManualType(eval.TypeOfNative(rt1SliceType{}), p.Arch) - - p.runtime.Stktop = newManualType(eval.TypeOfNative(rt1Stktop{}), p.Arch) - p.runtime.Gobuf = newManualType(eval.TypeOfNative(rt1Gobuf{}), p.Arch) - p.runtime.G = newManualType(eval.TypeOfNative(rt1G{}), p.Arch) - - // Get addresses of type.*runtime.XType for discrimination. - rtv := reflect.Indirect(reflect.ValueOf(&p.runtime)) - rtvt := rtv.Type() - for i := 0; i < rtv.NumField(); i++ { - n := rtvt.Field(i).Name - if n[0] != 'P' || n[1] < 'A' || n[1] > 'Z' { - continue - } - sym := p.syms.LookupSym("type.*runtime." + n[1:]) - if sym == nil { - continue - } - rtv.Field(i).SetUint(sym.Value) - } - - // Get runtime field indexes - fillRuntimeIndexes(&p.runtime, &p.f) - - // Fill G status - p.runtime.runtimeGStatus = rt1GStatus - - // Get globals - p.sys.lessstack = p.syms.LookupFunc("sys.lessstack") - p.sys.goexit = p.syms.LookupFunc("goexit") - p.sys.newproc = p.syms.LookupFunc("sys.newproc") - p.sys.deferproc = p.syms.LookupFunc("sys.deferproc") - p.sys.newprocreadylocked = p.syms.LookupFunc("newprocreadylocked") - if allg := p.syms.LookupSym("allg"); allg != nil { - p.sys.allg = remotePtr{remote{proc.Word(allg.Value), p}, p.runtime.G} - } - if g0 := p.syms.LookupSym("g0"); g0 != nil { - p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Value), p}).(remoteStruct) - } -} - -func (p *Process) selectSomeGoroutine() { - // Once we have friendly goroutine ID's, there might be a more - // reasonable behavior for this. - p.curGoroutine = nil - for _, g := range p.goroutines { - if !g.isG0() && g.frame != nil { - p.curGoroutine = g - return - } - } -} - -/* - * Process memory - */ - -func (p *Process) someStoppedOSThread() proc.Thread { - if p.threadCache != nil { - if _, err := p.threadCache.Stopped(); err == nil { - return p.threadCache - } - } - - for _, t := range p.proc.Threads() { - if _, err := t.Stopped(); err == nil { - p.threadCache = t - return t - } - } - return nil -} - -func (p *Process) Peek(addr proc.Word, out []byte) (int, os.Error) { - thr := p.someStoppedOSThread() - if thr == nil { - return 0, ProcessNotStopped{} - } - return thr.Peek(addr, out) -} - -func (p *Process) Poke(addr proc.Word, b []byte) (int, os.Error) { - thr := p.someStoppedOSThread() - if thr == nil { - return 0, ProcessNotStopped{} - } - return thr.Poke(addr, b) -} - -func (p *Process) peekUintptr(a aborter, addr proc.Word) proc.Word { - return proc.Word(mkUintptr(remote{addr, p}).(remoteUint).aGet(a)) -} - -/* - * Events - */ - -// OnBreakpoint returns the hook that is run when the program reaches -// the given program counter. -func (p *Process) OnBreakpoint(pc proc.Word) EventHook { - if bp, ok := p.breakpointHooks[pc]; ok { - return bp - } - // The breakpoint will register itself when a handler is added - return &breakpointHook{commonHook{nil, 0}, p, pc} -} - -// OnGoroutineCreate returns the hook that is run when a goroutine is created. -func (p *Process) OnGoroutineCreate() EventHook { - return p.goroutineCreateHook -} - -// OnGoroutineExit returns the hook that is run when a goroutine exits. -func (p *Process) OnGoroutineExit() EventHook { return p.goroutineExitHook } - -// osThreadToGoroutine looks up the goroutine running on an OS thread. -func (p *Process) osThreadToGoroutine(t proc.Thread) (*Goroutine, os.Error) { - regs, err := t.Regs() - if err != nil { - return nil, err - } - g := p.G(regs) - gt, ok := p.goroutines[g] - if !ok { - return nil, UnknownGoroutine{t, g} - } - return gt, nil -} - -// causesToEvents translates the stop causes of the underlying process -// into an event queue. -func (p *Process) causesToEvents() ([]Event, os.Error) { - // Count causes we're interested in - nev := 0 - for _, t := range p.proc.Threads() { - if c, err := t.Stopped(); err == nil { - switch c := c.(type) { - case proc.Breakpoint: - nev++ - case proc.Signal: - // TODO(austin) - //nev++; - } - } - } - - // Translate causes to events - events := make([]Event, nev) - i := 0 - for _, t := range p.proc.Threads() { - if c, err := t.Stopped(); err == nil { - switch c := c.(type) { - case proc.Breakpoint: - gt, err := p.osThreadToGoroutine(t) - if err != nil { - return nil, err - } - events[i] = &Breakpoint{commonEvent{p, gt}, t, proc.Word(c)} - i++ - case proc.Signal: - // TODO(austin) - } - } - } - - return events, nil -} - -// postEvent appends an event to the posted queue. These events will -// be processed before any currently pending events. -func (p *Process) postEvent(ev Event) { - p.posted = append(p.posted, ev) -} - -// processEvents processes events in the event queue until no events -// remain, a handler returns EAStop, or a handler returns an error. -// It returns either EAStop or EAContinue and possibly an error. -func (p *Process) processEvents() (EventAction, os.Error) { - var ev Event - for len(p.posted) > 0 { - ev, p.posted = p.posted[0], p.posted[1:] - action, err := p.processEvent(ev) - if action == EAStop { - return action, err - } - } - - for len(p.pending) > 0 { - ev, p.pending = p.pending[0], p.pending[1:] - action, err := p.processEvent(ev) - if action == EAStop { - return action, err - } - } - - return EAContinue, nil -} - -// processEvent processes a single event, without manipulating the -// event queues. It returns either EAStop or EAContinue and possibly -// an error. -func (p *Process) processEvent(ev Event) (EventAction, os.Error) { - p.event = ev - - var action EventAction - var err os.Error - switch ev := p.event.(type) { - case *Breakpoint: - hook, ok := p.breakpointHooks[ev.pc] - if !ok { - break - } - p.curGoroutine = ev.Goroutine() - action, err = hook.handle(ev) - - case *GoroutineCreate: - p.curGoroutine = ev.Goroutine() - action, err = p.goroutineCreateHook.handle(ev) - - case *GoroutineExit: - action, err = p.goroutineExitHook.handle(ev) - - default: - log.Panicf("Unknown event type %T in queue", p.event) - } - - if err != nil { - return EAStop, err - } else if action == EAStop { - return EAStop, nil - } - return EAContinue, nil -} - -// Event returns the last event that caused the process to stop. This -// may return nil if the process has never been stopped by an event. -// -// TODO(austin) Return nil if the user calls p.Stop()? -func (p *Process) Event() Event { return p.event } - -/* - * Process control - */ - -// TODO(austin) Cont, WaitStop, and Stop. Need to figure out how -// event handling works with these. Originally I did it only in -// WaitStop, but if you Cont and there are pending events, then you -// have to not actually continue and wait until a WaitStop to process -// them, even if the event handlers will tell you to continue. We -// could handle them in both Cont and WaitStop to avoid this problem, -// but it's still weird if an event happens after the Cont and before -// the WaitStop that the handlers say to continue from. Or we could -// handle them on a separate thread. Then obviously you get weird -// asynchronous things, like prints while the user it typing a command, -// but that's not necessarily a bad thing. - -// ContWait resumes process execution and waits for an event to occur -// that stops the process. -func (p *Process) ContWait() os.Error { - for { - a, err := p.processEvents() - if err != nil { - return err - } else if a == EAStop { - break - } - err = p.proc.Continue() - if err != nil { - return err - } - err = p.proc.WaitStop() - if err != nil { - return err - } - for _, g := range p.goroutines { - g.resetFrame() - } - p.pending, err = p.causesToEvents() - if err != nil { - return err - } - } - return nil -} - -// Out selects the caller frame of the current frame. -func (p *Process) Out() os.Error { - if p.curGoroutine == nil { - return NoCurrentGoroutine{} - } - return p.curGoroutine.Out() -} - -// In selects the frame called by the current frame. -func (p *Process) In() os.Error { - if p.curGoroutine == nil { - return NoCurrentGoroutine{} - } - return p.curGoroutine.In() -} diff --git a/src/pkg/exp/ogle/rruntime.go b/src/pkg/exp/ogle/rruntime.go deleted file mode 100644 index 950418b53..000000000 --- a/src/pkg/exp/ogle/rruntime.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/proc" - "exp/eval" - "reflect" -) - -// This file contains remote runtime definitions. Using reflection, -// we convert all of these to interpreter types and layout their -// remote representations using the architecture rules. -// -// We could get most of these definitions from our own runtime -// package; however, some of them differ in convenient ways, some of -// them are not defined or exported by the runtime, and having our own -// definitions makes it easy to support multiple remote runtime -// versions. This may turn out to be overkill. -// -// All of these structures are prefixed with rt1 to indicate the -// runtime version and to mark them as types used only as templates -// for remote types. - -/* - * Runtime data headers - * - * See $GOROOT/src/pkg/runtime/runtime.h - */ - -type rt1String struct { - str uintptr - len int -} - -type rt1Slice struct { - array uintptr - len int - cap int -} - -type rt1Eface struct { - typ uintptr - ptr uintptr -} - -/* - * Runtime type structures - * - * See $GOROOT/src/pkg/runtime/type.h and $GOROOT/src/pkg/runtime/type.go - */ - -type rt1UncommonType struct { - name *string - pkgPath *string - //methods []method; -} - -type rt1CommonType struct { - size uintptr - hash uint32 - alg, align, fieldAlign uint8 - string *string - uncommonType *rt1UncommonType -} - -type rt1Type struct { - // While Type is technically an Eface, treating the - // discriminator as an opaque pointer and taking advantage of - // the commonType prologue on all Type's makes type parsing - // much simpler. - typ uintptr - ptr *rt1CommonType -} - -type rt1StructField struct { - name *string - pkgPath *string - typ *rt1Type - tag *string - offset uintptr -} - -type rt1StructType struct { - rt1CommonType - fields []rt1StructField -} - -type rt1PtrType struct { - rt1CommonType - elem *rt1Type -} - -type rt1SliceType struct { - rt1CommonType - elem *rt1Type -} - -type rt1ArrayType struct { - rt1CommonType - elem *rt1Type - len uintptr -} - -/* - * Runtime scheduler structures - * - * See $GOROOT/src/pkg/runtime/runtime.h - */ - -// Fields beginning with _ are only for padding - -type rt1Stktop struct { - stackguard uintptr - stackbase *rt1Stktop - gobuf rt1Gobuf - _args uint32 - _fp uintptr -} - -type rt1Gobuf struct { - sp uintptr - pc uintptr - g *rt1G - r0 uintptr -} - -type rt1G struct { - _stackguard uintptr - stackbase *rt1Stktop - _defer uintptr - sched rt1Gobuf - _stack0 uintptr - _entry uintptr - alllink *rt1G - _param uintptr - status int16 - // Incomplete -} - -var rt1GStatus = runtimeGStatus{ - Gidle: 0, - Grunnable: 1, - Grunning: 2, - Gsyscall: 3, - Gwaiting: 4, - Gmoribund: 5, - Gdead: 6, -} - -// runtimeIndexes stores the indexes of fields in the runtime -// structures. It is filled in using reflection, so the name of the -// fields must match the names of the remoteType's in runtimeValues -// exactly and the names of the index fields must be the capitalized -// version of the names of the fields in the runtime structures above. -type runtimeIndexes struct { - String struct { - Str, Len int - } - Slice struct { - Array, Len, Cap int - } - Eface struct { - Typ, Ptr int - } - - UncommonType struct { - Name, PkgPath int - } - CommonType struct { - Size, Hash, Alg, Align, FieldAlign, String, UncommonType int - } - Type struct { - Typ, Ptr int - } - StructField struct { - Name, PkgPath, Typ, Tag, Offset int - } - StructType struct { - Fields int - } - PtrType struct { - Elem int - } - SliceType struct { - Elem int - } - ArrayType struct { - Elem, Len int - } - - Stktop struct { - Stackguard, Stackbase, Gobuf int - } - Gobuf struct { - Sp, Pc, G int - } - G struct { - Stackbase, Sched, Status, Alllink int - } -} - -// Values of G status codes -type runtimeGStatus struct { - Gidle, Grunnable, Grunning, Gsyscall, Gwaiting, Gmoribund, Gdead int64 -} - -// runtimeValues stores the types and values that correspond to those -// in the remote runtime package. -type runtimeValues struct { - // Runtime data headers - String, Slice, Eface *remoteType - // Runtime type structures - Type, CommonType, UncommonType, StructField, StructType, PtrType, - ArrayType, SliceType *remoteType - // Runtime scheduler structures - Stktop, Gobuf, G *remoteType - // Addresses of *runtime.XType types. These are the - // discriminators on the runtime.Type interface. We use local - // reflection to fill these in from the remote symbol table, - // so the names must match the runtime names. - PBoolType, - PUint8Type, PUint16Type, PUint32Type, PUint64Type, PUintType, PUintptrType, - PInt8Type, PInt16Type, PInt32Type, PInt64Type, PIntType, - PFloat32Type, PFloat64Type, PFloatType, - PArrayType, PStringType, PStructType, PPtrType, PFuncType, - PInterfaceType, PSliceType, PMapType, PChanType, - PDotDotDotType, PUnsafePointerType proc.Word - // G status values - runtimeGStatus -} - -// fillRuntimeIndexes fills a runtimeIndexes structure will the field -// indexes gathered from the remoteTypes recorded in a runtimeValues -// structure. -func fillRuntimeIndexes(runtime *runtimeValues, out *runtimeIndexes) { - outv := reflect.Indirect(reflect.ValueOf(out)) - outt := outv.Type() - runtimev := reflect.Indirect(reflect.ValueOf(runtime)) - - // out contains fields corresponding to each runtime type - for i := 0; i < outt.NumField(); i++ { - // Find the interpreter type for this runtime type - name := outt.Field(i).Name - et := runtimev.FieldByName(name).Interface().(*remoteType).Type.(*eval.StructType) - - // Get the field indexes of the interpreter struct type - indexes := make(map[string]int, len(et.Elems)) - for j, f := range et.Elems { - if f.Anonymous { - continue - } - name := f.Name - if name[0] >= 'a' && name[0] <= 'z' { - name = string(name[0]+'A'-'a') + name[1:] - } - indexes[name] = j - } - - // Fill this field of out - outStructv := outv.Field(i) - outStructt := outStructv.Type() - for j := 0; j < outStructt.NumField(); j++ { - f := outStructv.Field(j) - name := outStructt.Field(j).Name - f.SetInt(int64(indexes[name])) - } - } -} diff --git a/src/pkg/exp/ogle/rtype.go b/src/pkg/exp/ogle/rtype.go deleted file mode 100644 index b3c35575a..000000000 --- a/src/pkg/exp/ogle/rtype.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/proc" - "exp/eval" - "fmt" - "log" -) - -const debugParseRemoteType = false - -// A remoteType is the local representation of a type in a remote process. -type remoteType struct { - eval.Type - // The size of values of this type in bytes. - size int - // The field alignment of this type. Only used for - // manually-constructed types. - fieldAlign int - // The maker function to turn a remote address of a value of - // this type into an interpreter Value. - mk maker -} - -var manualTypes = make(map[Arch]map[eval.Type]*remoteType) - -// newManualType constructs a remote type from an interpreter Type -// using the size and alignment properties of the given architecture. -// Most types are parsed directly out of the remote process, but to do -// so we need to layout the structures that describe those types ourselves. -func newManualType(t eval.Type, arch Arch) *remoteType { - if nt, ok := t.(*eval.NamedType); ok { - t = nt.Def - } - - // Get the type map for this architecture - typeMap := manualTypes[arch] - if typeMap == nil { - typeMap = make(map[eval.Type]*remoteType) - manualTypes[arch] = typeMap - - // Construct basic types for this architecture - basicType := func(t eval.Type, mk maker, size int, fieldAlign int) { - t = t.(*eval.NamedType).Def - if fieldAlign == 0 { - fieldAlign = size - } - typeMap[t] = &remoteType{t, size, fieldAlign, mk} - } - basicType(eval.Uint8Type, mkUint8, 1, 0) - basicType(eval.Uint32Type, mkUint32, 4, 0) - basicType(eval.UintptrType, mkUintptr, arch.PtrSize(), 0) - basicType(eval.Int16Type, mkInt16, 2, 0) - basicType(eval.Int32Type, mkInt32, 4, 0) - basicType(eval.IntType, mkInt, arch.IntSize(), 0) - basicType(eval.StringType, mkString, arch.PtrSize()+arch.IntSize(), arch.PtrSize()) - } - - if rt, ok := typeMap[t]; ok { - return rt - } - - var rt *remoteType - switch t := t.(type) { - case *eval.PtrType: - var elem *remoteType - mk := func(r remote) eval.Value { return remotePtr{r, elem} } - rt = &remoteType{t, arch.PtrSize(), arch.PtrSize(), mk} - // Construct the element type after registering the - // type to break cycles. - typeMap[eval.Type(t)] = rt - elem = newManualType(t.Elem, arch) - - case *eval.ArrayType: - elem := newManualType(t.Elem, arch) - mk := func(r remote) eval.Value { return remoteArray{r, t.Len, elem} } - rt = &remoteType{t, elem.size * int(t.Len), elem.fieldAlign, mk} - - case *eval.SliceType: - elem := newManualType(t.Elem, arch) - mk := func(r remote) eval.Value { return remoteSlice{r, elem} } - rt = &remoteType{t, arch.PtrSize() + 2*arch.IntSize(), arch.PtrSize(), mk} - - case *eval.StructType: - layout := make([]remoteStructField, len(t.Elems)) - offset := 0 - fieldAlign := 0 - for i, f := range t.Elems { - elem := newManualType(f.Type, arch) - if fieldAlign == 0 { - fieldAlign = elem.fieldAlign - } - offset = arch.Align(offset, elem.fieldAlign) - layout[i].offset = offset - layout[i].fieldType = elem - offset += elem.size - } - mk := func(r remote) eval.Value { return remoteStruct{r, layout} } - rt = &remoteType{t, offset, fieldAlign, mk} - - default: - log.Panicf("cannot manually construct type %T", t) - } - - typeMap[t] = rt - return rt -} - -var prtIndent = "" - -// parseRemoteType parses a Type structure in a remote process to -// construct the corresponding interpreter type and remote type. -func parseRemoteType(a aborter, rs remoteStruct) *remoteType { - addr := rs.addr().base - p := rs.addr().p - - // We deal with circular types by discovering cycles at - // NamedTypes. If a type cycles back to something other than - // a named type, we're guaranteed that there will be a named - // type somewhere in that cycle. Thus, we continue down, - // re-parsing types until we reach the named type in the - // cycle. In order to still create one remoteType per remote - // type, we insert an empty remoteType in the type map the - // first time we encounter the type and re-use that structure - // the second time we encounter it. - - rt, ok := p.types[addr] - if ok && rt.Type != nil { - return rt - } else if !ok { - rt = &remoteType{} - p.types[addr] = rt - } - - if debugParseRemoteType { - sym := p.syms.SymByAddr(uint64(addr)) - name := "<unknown>" - if sym != nil { - name = sym.Name - } - log.Printf("%sParsing type at %#x (%s)", prtIndent, addr, name) - prtIndent += " " - defer func() { prtIndent = prtIndent[0 : len(prtIndent)-1] }() - } - - // Get Type header - itype := proc.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a)) - typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct) - - // Is this a named type? - var nt *eval.NamedType - uncommon := typ.field(p.f.CommonType.UncommonType).(remotePtr).aGet(a) - if uncommon != nil { - name := uncommon.(remoteStruct).field(p.f.UncommonType.Name).(remotePtr).aGet(a) - if name != nil { - // TODO(austin) Declare type in appropriate remote package - nt = eval.NewNamedType(name.(remoteString).aGet(a)) - rt.Type = nt - } - } - - // Create type - var t eval.Type - var mk maker - switch itype { - case p.runtime.PBoolType: - t = eval.BoolType - mk = mkBool - case p.runtime.PUint8Type: - t = eval.Uint8Type - mk = mkUint8 - case p.runtime.PUint16Type: - t = eval.Uint16Type - mk = mkUint16 - case p.runtime.PUint32Type: - t = eval.Uint32Type - mk = mkUint32 - case p.runtime.PUint64Type: - t = eval.Uint64Type - mk = mkUint64 - case p.runtime.PUintType: - t = eval.UintType - mk = mkUint - case p.runtime.PUintptrType: - t = eval.UintptrType - mk = mkUintptr - case p.runtime.PInt8Type: - t = eval.Int8Type - mk = mkInt8 - case p.runtime.PInt16Type: - t = eval.Int16Type - mk = mkInt16 - case p.runtime.PInt32Type: - t = eval.Int32Type - mk = mkInt32 - case p.runtime.PInt64Type: - t = eval.Int64Type - mk = mkInt64 - case p.runtime.PIntType: - t = eval.IntType - mk = mkInt - case p.runtime.PFloat32Type: - t = eval.Float32Type - mk = mkFloat32 - case p.runtime.PFloat64Type: - t = eval.Float64Type - mk = mkFloat64 - case p.runtime.PStringType: - t = eval.StringType - mk = mkString - - case p.runtime.PArrayType: - // Cast to an ArrayType - typ := p.runtime.ArrayType.mk(typ.addr()).(remoteStruct) - len := int64(typ.field(p.f.ArrayType.Len).(remoteUint).aGet(a)) - elem := parseRemoteType(a, typ.field(p.f.ArrayType.Elem).(remotePtr).aGet(a).(remoteStruct)) - t = eval.NewArrayType(len, elem.Type) - mk = func(r remote) eval.Value { return remoteArray{r, len, elem} } - - case p.runtime.PStructType: - // Cast to a StructType - typ := p.runtime.StructType.mk(typ.addr()).(remoteStruct) - fs := typ.field(p.f.StructType.Fields).(remoteSlice).aGet(a) - - fields := make([]eval.StructField, fs.Len) - layout := make([]remoteStructField, fs.Len) - for i := range fields { - f := fs.Base.(remoteArray).elem(int64(i)).(remoteStruct) - elemrs := f.field(p.f.StructField.Typ).(remotePtr).aGet(a).(remoteStruct) - elem := parseRemoteType(a, elemrs) - fields[i].Type = elem.Type - name := f.field(p.f.StructField.Name).(remotePtr).aGet(a) - if name == nil { - fields[i].Anonymous = true - } else { - fields[i].Name = name.(remoteString).aGet(a) - } - layout[i].offset = int(f.field(p.f.StructField.Offset).(remoteUint).aGet(a)) - layout[i].fieldType = elem - } - - t = eval.NewStructType(fields) - mk = func(r remote) eval.Value { return remoteStruct{r, layout} } - - case p.runtime.PPtrType: - // Cast to a PtrType - typ := p.runtime.PtrType.mk(typ.addr()).(remoteStruct) - elem := parseRemoteType(a, typ.field(p.f.PtrType.Elem).(remotePtr).aGet(a).(remoteStruct)) - t = eval.NewPtrType(elem.Type) - mk = func(r remote) eval.Value { return remotePtr{r, elem} } - - case p.runtime.PSliceType: - // Cast to a SliceType - typ := p.runtime.SliceType.mk(typ.addr()).(remoteStruct) - elem := parseRemoteType(a, typ.field(p.f.SliceType.Elem).(remotePtr).aGet(a).(remoteStruct)) - t = eval.NewSliceType(elem.Type) - mk = func(r remote) eval.Value { return remoteSlice{r, elem} } - - case p.runtime.PMapType, p.runtime.PChanType, p.runtime.PFuncType, p.runtime.PInterfaceType, p.runtime.PUnsafePointerType, p.runtime.PDotDotDotType: - // TODO(austin) - t = eval.UintptrType - mk = mkUintptr - - default: - sym := p.syms.SymByAddr(uint64(itype)) - name := "<unknown symbol>" - if sym != nil { - name = sym.Name - } - err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name) - a.Abort(FormatError(err)) - } - - // Fill in the remote type - if nt != nil { - nt.Complete(t) - } else { - rt.Type = t - } - rt.size = int(typ.field(p.f.CommonType.Size).(remoteUint).aGet(a)) - rt.mk = mk - - return rt -} diff --git a/src/pkg/exp/ogle/rvalue.go b/src/pkg/exp/ogle/rvalue.go deleted file mode 100644 index 3d630f936..000000000 --- a/src/pkg/exp/ogle/rvalue.go +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/proc" - "exp/eval" - "fmt" -) - -// A RemoteMismatchError occurs when an operation that requires two -// identical remote processes is given different process. For -// example, this occurs when trying to set a pointer in one process to -// point to something in another process. -type RemoteMismatchError string - -func (e RemoteMismatchError) String() string { return string(e) } - -// A ReadOnlyError occurs when attempting to set or assign to a -// read-only value. -type ReadOnlyError string - -func (e ReadOnlyError) String() string { return string(e) } - -// A maker is a function that converts a remote address into an -// interpreter Value. -type maker func(remote) eval.Value - -type remoteValue interface { - addr() remote -} - -// remote represents an address in a remote process. -type remote struct { - base proc.Word - p *Process -} - -func (v remote) Get(a aborter, size int) uint64 { - // TODO(austin) This variable might temporarily be in a - // register. We could trace the assembly back from the - // current PC, looking for the beginning of the function or a - // call (both of which guarantee that the variable is in - // memory), or an instruction that loads the variable into a - // register. - // - // TODO(austin) If this is a local variable, it might not be - // live at this PC. In fact, because the compiler reuses - // slots, there might even be a different local variable at - // this location right now. A simple solution to both - // problems is to include the range of PC's over which a local - // variable is live in the symbol table. - // - // TODO(austin) We need to prevent the remote garbage - // collector from collecting objects out from under us. - var arr [8]byte - buf := arr[0:size] - _, err := v.p.Peek(v.base, buf) - if err != nil { - a.Abort(err) - } - return uint64(v.p.ToWord(buf)) -} - -func (v remote) Set(a aborter, size int, x uint64) { - var arr [8]byte - buf := arr[0:size] - v.p.FromWord(proc.Word(x), buf) - _, err := v.p.Poke(v.base, buf) - if err != nil { - a.Abort(err) - } -} - -func (v remote) plus(x proc.Word) remote { return remote{v.base + x, v.p} } - -func tryRVString(f func(a aborter) string) string { - var s string - err := try(func(a aborter) { s = f(a) }) - if err != nil { - return fmt.Sprintf("<error: %v>", err) - } - return s -} - -/* - * Bool - */ - -type remoteBool struct { - r remote -} - -func (v remoteBool) String() string { - return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) -} - -func (v remoteBool) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.BoolValue).Get(t)) -} - -func (v remoteBool) Get(t *eval.Thread) bool { return v.aGet(t) } - -func (v remoteBool) aGet(a aborter) bool { return v.r.Get(a, 1) != 0 } - -func (v remoteBool) Set(t *eval.Thread, x bool) { - v.aSet(t, x) -} - -func (v remoteBool) aSet(a aborter, x bool) { - if x { - v.r.Set(a, 1, 1) - } else { - v.r.Set(a, 1, 0) - } -} - -func (v remoteBool) addr() remote { return v.r } - -func mkBool(r remote) eval.Value { return remoteBool{r} } - -/* - * Uint - */ - -type remoteUint struct { - r remote - size int -} - -func (v remoteUint) String() string { - return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) -} - -func (v remoteUint) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.UintValue).Get(t)) -} - -func (v remoteUint) Get(t *eval.Thread) uint64 { - return v.aGet(t) -} - -func (v remoteUint) aGet(a aborter) uint64 { return v.r.Get(a, v.size) } - -func (v remoteUint) Set(t *eval.Thread, x uint64) { - v.aSet(t, x) -} - -func (v remoteUint) aSet(a aborter, x uint64) { v.r.Set(a, v.size, x) } - -func (v remoteUint) addr() remote { return v.r } - -func mkUint8(r remote) eval.Value { return remoteUint{r, 1} } - -func mkUint16(r remote) eval.Value { return remoteUint{r, 2} } - -func mkUint32(r remote) eval.Value { return remoteUint{r, 4} } - -func mkUint64(r remote) eval.Value { return remoteUint{r, 8} } - -func mkUint(r remote) eval.Value { return remoteUint{r, r.p.IntSize()} } - -func mkUintptr(r remote) eval.Value { return remoteUint{r, r.p.PtrSize()} } - -/* - * Int - */ - -type remoteInt struct { - r remote - size int -} - -func (v remoteInt) String() string { - return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) -} - -func (v remoteInt) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.IntValue).Get(t)) -} - -func (v remoteInt) Get(t *eval.Thread) int64 { return v.aGet(t) } - -func (v remoteInt) aGet(a aborter) int64 { return int64(v.r.Get(a, v.size)) } - -func (v remoteInt) Set(t *eval.Thread, x int64) { - v.aSet(t, x) -} - -func (v remoteInt) aSet(a aborter, x int64) { v.r.Set(a, v.size, uint64(x)) } - -func (v remoteInt) addr() remote { return v.r } - -func mkInt8(r remote) eval.Value { return remoteInt{r, 1} } - -func mkInt16(r remote) eval.Value { return remoteInt{r, 2} } - -func mkInt32(r remote) eval.Value { return remoteInt{r, 4} } - -func mkInt64(r remote) eval.Value { return remoteInt{r, 8} } - -func mkInt(r remote) eval.Value { return remoteInt{r, r.p.IntSize()} } - -/* - * Float - */ - -type remoteFloat struct { - r remote - size int -} - -func (v remoteFloat) String() string { - return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) -} - -func (v remoteFloat) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.FloatValue).Get(t)) -} - -func (v remoteFloat) Get(t *eval.Thread) float64 { - return v.aGet(t) -} - -func (v remoteFloat) aGet(a aborter) float64 { - bits := v.r.Get(a, v.size) - switch v.size { - case 4: - return float64(v.r.p.ToFloat32(uint32(bits))) - case 8: - return v.r.p.ToFloat64(bits) - } - panic("Unexpected float size") -} - -func (v remoteFloat) Set(t *eval.Thread, x float64) { - v.aSet(t, x) -} - -func (v remoteFloat) aSet(a aborter, x float64) { - var bits uint64 - switch v.size { - case 4: - bits = uint64(v.r.p.FromFloat32(float32(x))) - case 8: - bits = v.r.p.FromFloat64(x) - default: - panic("Unexpected float size") - } - v.r.Set(a, v.size, bits) -} - -func (v remoteFloat) addr() remote { return v.r } - -func mkFloat32(r remote) eval.Value { return remoteFloat{r, 4} } - -func mkFloat64(r remote) eval.Value { return remoteFloat{r, 8} } - -func mkFloat(r remote) eval.Value { return remoteFloat{r, r.p.FloatSize()} } - -/* - * String - */ - -type remoteString struct { - r remote -} - -func (v remoteString) String() string { - return tryRVString(func(a aborter) string { return v.aGet(a) }) -} - -func (v remoteString) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.StringValue).Get(t)) -} - -func (v remoteString) Get(t *eval.Thread) string { - return v.aGet(t) -} - -func (v remoteString) aGet(a aborter) string { - rs := v.r.p.runtime.String.mk(v.r).(remoteStruct) - str := proc.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)) - len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a) - - bytes := make([]uint8, len) - _, err := v.r.p.Peek(str, bytes) - if err != nil { - a.Abort(err) - } - return string(bytes) -} - -func (v remoteString) Set(t *eval.Thread, x string) { - v.aSet(t, x) -} - -func (v remoteString) aSet(a aborter, x string) { - // TODO(austin) This isn't generally possible without the - // ability to allocate remote memory. - a.Abort(ReadOnlyError("remote strings cannot be assigned to")) -} - -func mkString(r remote) eval.Value { return remoteString{r} } - -/* - * Array - */ - -type remoteArray struct { - r remote - len int64 - elemType *remoteType -} - -func (v remoteArray) String() string { - res := "{" - for i := int64(0); i < v.len; i++ { - if i > 0 { - res += ", " - } - res += v.elem(i).String() - } - return res + "}" -} - -func (v remoteArray) Assign(t *eval.Thread, o eval.Value) { - // TODO(austin) Could do a bigger memcpy if o is a - // remoteArray in the same Process. - oa := o.(eval.ArrayValue) - for i := int64(0); i < v.len; i++ { - v.Elem(t, i).Assign(t, oa.Elem(t, i)) - } -} - -func (v remoteArray) Get(t *eval.Thread) eval.ArrayValue { - return v -} - -func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value { - return v.elem(i) -} - -func (v remoteArray) elem(i int64) eval.Value { - return v.elemType.mk(v.r.plus(proc.Word(int64(v.elemType.size) * i))) -} - -func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { - return remoteArray{v.r.plus(proc.Word(int64(v.elemType.size) * i)), len, v.elemType} -} - -/* - * Struct - */ - -type remoteStruct struct { - r remote - layout []remoteStructField -} - -type remoteStructField struct { - offset int - fieldType *remoteType -} - -func (v remoteStruct) String() string { - res := "{" - for i := range v.layout { - if i > 0 { - res += ", " - } - res += v.field(i).String() - } - return res + "}" -} - -func (v remoteStruct) Assign(t *eval.Thread, o eval.Value) { - // TODO(austin) Could do a bigger memcpy. - oa := o.(eval.StructValue) - l := len(v.layout) - for i := 0; i < l; i++ { - v.Field(t, i).Assign(t, oa.Field(t, i)) - } -} - -func (v remoteStruct) Get(t *eval.Thread) eval.StructValue { - return v -} - -func (v remoteStruct) Field(t *eval.Thread, i int) eval.Value { - return v.field(i) -} - -func (v remoteStruct) field(i int) eval.Value { - f := &v.layout[i] - return f.fieldType.mk(v.r.plus(proc.Word(f.offset))) -} - -func (v remoteStruct) addr() remote { return v.r } - -/* - * Pointer - */ - -// TODO(austin) Comparing two remote pointers for equality in the -// interpreter will crash it because the Value's returned from -// remotePtr.Get() will be structs. - -type remotePtr struct { - r remote - elemType *remoteType -} - -func (v remotePtr) String() string { - return tryRVString(func(a aborter) string { - e := v.aGet(a) - if e == nil { - return "<nil>" - } - return "&" + e.String() - }) -} - -func (v remotePtr) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.PtrValue).Get(t)) -} - -func (v remotePtr) Get(t *eval.Thread) eval.Value { - return v.aGet(t) -} - -func (v remotePtr) aGet(a aborter) eval.Value { - addr := proc.Word(v.r.Get(a, v.r.p.PtrSize())) - if addr == 0 { - return nil - } - return v.elemType.mk(remote{addr, v.r.p}) -} - -func (v remotePtr) Set(t *eval.Thread, x eval.Value) { - v.aSet(t, x) -} - -func (v remotePtr) aSet(a aborter, x eval.Value) { - if x == nil { - v.r.Set(a, v.r.p.PtrSize(), 0) - return - } - xr, ok := x.(remoteValue) - if !ok || v.r.p != xr.addr().p { - a.Abort(RemoteMismatchError("remote pointer must point within the same process")) - } - v.r.Set(a, v.r.p.PtrSize(), uint64(xr.addr().base)) -} - -func (v remotePtr) addr() remote { return v.r } - -/* - * Slice - */ - -type remoteSlice struct { - r remote - elemType *remoteType -} - -func (v remoteSlice) String() string { - return tryRVString(func(a aborter) string { - b := v.aGet(a).Base - if b == nil { - return "<nil>" - } - return b.String() - }) -} - -func (v remoteSlice) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.SliceValue).Get(t)) -} - -func (v remoteSlice) Get(t *eval.Thread) eval.Slice { - return v.aGet(t) -} - -func (v remoteSlice) aGet(a aborter) eval.Slice { - rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct) - base := proc.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)) - nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a) - cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a) - if base == 0 { - return eval.Slice{nil, nel, cap} - } - return eval.Slice{remoteArray{remote{base, v.r.p}, nel, v.elemType}, nel, cap} -} - -func (v remoteSlice) Set(t *eval.Thread, x eval.Slice) { - v.aSet(t, x) -} - -func (v remoteSlice) aSet(a aborter, x eval.Slice) { - rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct) - if x.Base == nil { - rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, 0) - } else { - ar, ok := x.Base.(remoteArray) - if !ok || v.r.p != ar.r.p { - a.Abort(RemoteMismatchError("remote slice must point within the same process")) - } - rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, uint64(ar.r.base)) - } - rs.field(v.r.p.f.Slice.Len).(remoteInt).aSet(a, x.Len) - rs.field(v.r.p.f.Slice.Cap).(remoteInt).aSet(a, x.Cap) -} diff --git a/src/pkg/exp/ogle/vars.go b/src/pkg/exp/ogle/vars.go deleted file mode 100644 index 8a3a14791..000000000 --- a/src/pkg/exp/ogle/vars.go +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2009 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. - -package ogle - -import ( - "debug/gosym" - "debug/proc" - "exp/eval" - "log" - "os" -) - -/* - * Remote frame pointers - */ - -// A NotOnStack error occurs when attempting to access a variable in a -// remote frame where that remote frame is not on the current stack. -type NotOnStack struct { - Fn *gosym.Func - Goroutine *Goroutine -} - -func (e NotOnStack) String() string { - return "function " + e.Fn.Name + " not on " + e.Goroutine.String() + "'s stack" -} - -// A remoteFramePtr is an implementation of eval.PtrValue that -// represents a pointer to a function frame in a remote process. When -// accessed, this locates the function on the current goroutine's -// stack and returns a structure containing the local variables of -// that function. -type remoteFramePtr struct { - p *Process - fn *gosym.Func - rt *remoteType -} - -func (v remoteFramePtr) String() string { - // TODO(austin): This could be a really awesome string method - return "<remote frame>" -} - -func (v remoteFramePtr) Assign(t *eval.Thread, o eval.Value) { - v.Set(t, o.(eval.PtrValue).Get(t)) -} - -func (v remoteFramePtr) Get(t *eval.Thread) eval.Value { - g := v.p.curGoroutine - if g == nil || g.frame == nil { - t.Abort(NoCurrentGoroutine{}) - } - - for f := g.frame; f != nil; f = f.aOuter(t) { - if f.fn != v.fn { - continue - } - - // TODO(austin): Register for shootdown with f - return v.rt.mk(remote{f.fp, v.p}) - } - - t.Abort(NotOnStack{v.fn, g}) - panic("fail") -} - -func (v remoteFramePtr) Set(t *eval.Thread, x eval.Value) { - // Theoretically this could be a static error. If remote - // packages were packages, remote frames could just be defined - // as constants. - t.Abort(ReadOnlyError("remote frames cannot be assigned to")) -} - -/* - * Remote packages - */ - -// TODO(austin): Remote packages are implemented as structs right now, -// which has some weird consequences. You can attempt to assign to a -// remote package. It also produces terrible error messages. -// Ideally, these would actually be packages, but somehow first-class -// so they could be assigned to other names. - -// A remotePackage is an implementation of eval.StructValue that -// represents a package in a remote process. It's essentially a -// regular struct, except it cannot be assigned to. -type remotePackage struct { - defs []eval.Value -} - -func (v remotePackage) String() string { return "<remote package>" } - -func (v remotePackage) Assign(t *eval.Thread, o eval.Value) { - t.Abort(ReadOnlyError("remote packages cannot be assigned to")) -} - -func (v remotePackage) Get(t *eval.Thread) eval.StructValue { - return v -} - -func (v remotePackage) Field(t *eval.Thread, i int) eval.Value { - return v.defs[i] -} - -/* - * Remote variables - */ - -// populateWorld defines constants in the given world for each package -// in this process. These packages are structs that, in turn, contain -// fields for each global and function in that package. -func (p *Process) populateWorld(w *eval.World) os.Error { - type def struct { - t eval.Type - v eval.Value - } - packages := make(map[string]map[string]def) - - for _, s := range p.syms.Syms { - if s.ReceiverName() != "" { - // TODO(austin) - continue - } - - // Package - pkgName := s.PackageName() - switch pkgName { - case "", "type", "extratype", "string", "go": - // "go" is really "go.string" - continue - } - pkg, ok := packages[pkgName] - if !ok { - pkg = make(map[string]def) - packages[pkgName] = pkg - } - - // Symbol name - name := s.BaseName() - if _, ok := pkg[name]; ok { - log.Printf("Multiple definitions of symbol %s", s.Name) - continue - } - - // Symbol type - rt, err := p.typeOfSym(&s) - if err != nil { - return err - } - - // Definition - switch s.Type { - case 'D', 'd', 'B', 'b': - // Global variable - if rt == nil { - continue - } - pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(s.Value), p})} - - case 'T', 't', 'L', 'l': - // Function - s := s.Func - // TODO(austin): Ideally, this would *also* be - // callable. How does that interact with type - // conversion syntax? - rt, err := p.makeFrameType(s) - if err != nil { - return err - } - pkg[name] = def{eval.NewPtrType(rt.Type), remoteFramePtr{p, s, rt}} - } - } - - // TODO(austin): Define remote types - - // Define packages - for pkgName, defs := range packages { - fields := make([]eval.StructField, len(defs)) - vals := make([]eval.Value, len(defs)) - i := 0 - for name, def := range defs { - fields[i].Name = name - fields[i].Type = def.t - vals[i] = def.v - i++ - } - pkgType := eval.NewStructType(fields) - pkgVal := remotePackage{vals} - - err := w.DefineConst(pkgName, pkgType, pkgVal) - if err != nil { - log.Printf("while defining package %s: %v", pkgName, err) - } - } - - return nil -} - -// typeOfSym returns the type associated with a symbol. If the symbol -// has no type, returns nil. -func (p *Process) typeOfSym(s *gosym.Sym) (*remoteType, os.Error) { - if s.GoType == 0 { - return nil, nil - } - addr := proc.Word(s.GoType) - var rt *remoteType - err := try(func(a aborter) { rt = parseRemoteType(a, p.runtime.Type.mk(remote{addr, p}).(remoteStruct)) }) - if err != nil { - return nil, err - } - return rt, nil -} - -// makeFrameType constructs a struct type for the frame of a function. -// The offsets in this struct type are such that the struct can be -// instantiated at this function's frame pointer. -func (p *Process) makeFrameType(s *gosym.Func) (*remoteType, os.Error) { - n := len(s.Params) + len(s.Locals) - fields := make([]eval.StructField, n) - layout := make([]remoteStructField, n) - i := 0 - - // TODO(austin): There can be multiple locals/parameters with - // the same name. We probably need liveness information to do - // anything about this. Once we have that, perhaps we give - // such fields interface{} type? Or perhaps we disambiguate - // the names with numbers. Disambiguation is annoying for - // things like "i", where there's an obvious right answer. - - for _, param := range s.Params { - rt, err := p.typeOfSym(param) - if err != nil { - return nil, err - } - if rt == nil { - //fmt.Printf(" (no type)\n"); - continue - } - // TODO(austin): Why do local variables carry their - // package name? - fields[i].Name = param.BaseName() - fields[i].Type = rt.Type - // Parameters have positive offsets from FP - layout[i].offset = int(param.Value) - layout[i].fieldType = rt - i++ - } - - for _, local := range s.Locals { - rt, err := p.typeOfSym(local) - if err != nil { - return nil, err - } - if rt == nil { - continue - } - fields[i].Name = local.BaseName() - fields[i].Type = rt.Type - // Locals have negative offsets from FP - PtrSize - layout[i].offset = -int(local.Value) - p.PtrSize() - layout[i].fieldType = rt - i++ - } - - fields = fields[0:i] - layout = layout[0:i] - t := eval.NewStructType(fields) - mk := func(r remote) eval.Value { return remoteStruct{r, layout} } - return &remoteType{t, 0, 0, mk}, nil -} diff --git a/src/pkg/exp/regexp/syntax/Makefile b/src/pkg/exp/regexp/syntax/Makefile deleted file mode 100644 index 97d4ad6ca..000000000 --- a/src/pkg/exp/regexp/syntax/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2011 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. - -include ../../../../Make.inc - -TARG=exp/regexp/syntax -GOFILES=\ - compile.go\ - parse.go\ - perl_groups.go\ - prog.go\ - regexp.go\ - simplify.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/exp/regexp/syntax/compile.go b/src/pkg/exp/regexp/syntax/compile.go deleted file mode 100644 index ec9556fde..000000000 --- a/src/pkg/exp/regexp/syntax/compile.go +++ /dev/null @@ -1,264 +0,0 @@ -package syntax - -import ( - "os" - "unicode" -) - -// A patchList is a list of instruction pointers that need to be filled in (patched). -// Because the pointers haven't been filled in yet, we can reuse their storage -// to hold the list. It's kind of sleazy, but works well in practice. -// See http://swtch.com/~rsc/regexp/regexp1.html for inspiration. -// -// These aren't really pointers: they're integers, so we can reinterpret them -// this way without using package unsafe. A value l denotes -// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1). -// l == 0 denotes the empty list, okay because we start every program -// with a fail instruction, so we'll never want to point at its output link. -type patchList uint32 - -func (l patchList) next(p *Prog) patchList { - i := &p.Inst[l>>1] - if l&1 == 0 { - return patchList(i.Out) - } - return patchList(i.Arg) -} - -func (l patchList) patch(p *Prog, val uint32) { - for l != 0 { - i := &p.Inst[l>>1] - if l&1 == 0 { - l = patchList(i.Out) - i.Out = val - } else { - l = patchList(i.Arg) - i.Arg = val - } - } -} - -func (l1 patchList) append(p *Prog, l2 patchList) patchList { - if l1 == 0 { - return l2 - } - if l2 == 0 { - return l1 - } - - last := l1 - for { - next := last.next(p) - if next == 0 { - break - } - last = next - } - - i := &p.Inst[last>>1] - if last&1 == 0 { - i.Out = uint32(l2) - } else { - i.Arg = uint32(l2) - } - return l1 -} - -// A frag represents a compiled program fragment. -type frag struct { - i uint32 // index of first instruction - out patchList // where to record end instruction -} - -type compiler struct { - p *Prog -} - -// Compile compiles the regexp into a program to be executed. -func Compile(re *Regexp) (*Prog, os.Error) { - var c compiler - c.init() - f := c.compile(re) - f.out.patch(c.p, c.inst(InstMatch).i) - c.p.Start = int(f.i) - return c.p, nil -} - -func (c *compiler) init() { - c.p = new(Prog) - c.inst(InstFail) -} - -var anyRuneNotNL = []int{0, '\n' - 1, '\n' - 1, unicode.MaxRune} -var anyRune = []int{0, unicode.MaxRune} - -func (c *compiler) compile(re *Regexp) frag { - switch re.Op { - case OpNoMatch: - return c.fail() - case OpEmptyMatch: - return c.nop() - case OpLiteral: - if len(re.Rune) == 0 { - return c.nop() - } - var f frag - for j := range re.Rune { - f1 := c.rune(re.Rune[j : j+1]) - if j == 0 { - f = f1 - } else { - f = c.cat(f, f1) - } - } - return f - case OpCharClass: - return c.rune(re.Rune) - case OpAnyCharNotNL: - return c.rune(anyRuneNotNL) - case OpAnyChar: - return c.rune(anyRune) - case OpBeginLine: - return c.empty(EmptyBeginLine) - case OpEndLine: - return c.empty(EmptyEndLine) - case OpBeginText: - return c.empty(EmptyBeginText) - case OpEndText: - return c.empty(EmptyEndText) - case OpWordBoundary: - return c.empty(EmptyWordBoundary) - case OpNoWordBoundary: - return c.empty(EmptyNoWordBoundary) - case OpCapture: - bra := c.cap(uint32(re.Cap << 1)) - sub := c.compile(re.Sub[0]) - ket := c.cap(uint32(re.Cap<<1 | 1)) - return c.cat(c.cat(bra, sub), ket) - case OpStar: - return c.star(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) - case OpPlus: - return c.plus(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) - case OpQuest: - return c.quest(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) - case OpConcat: - if len(re.Sub) == 0 { - return c.nop() - } - var f frag - for i, sub := range re.Sub { - if i == 0 { - f = c.compile(sub) - } else { - f = c.cat(f, c.compile(sub)) - } - } - return f - case OpAlternate: - var f frag - for _, sub := range re.Sub { - f = c.alt(f, c.compile(sub)) - } - return f - } - panic("regexp: unhandled case in compile") -} - -func (c *compiler) inst(op InstOp) frag { - // TODO: impose length limit - f := frag{i: uint32(len(c.p.Inst))} - c.p.Inst = append(c.p.Inst, Inst{Op: op}) - return f -} - -func (c *compiler) nop() frag { - f := c.inst(InstNop) - f.out = patchList(f.i << 1) - return f -} - -func (c *compiler) fail() frag { - return frag{} -} - -func (c *compiler) cap(arg uint32) frag { - f := c.inst(InstCapture) - f.out = patchList(f.i << 1) - c.p.Inst[f.i].Arg = arg - return f -} - -func (c *compiler) cat(f1, f2 frag) frag { - // concat of failure is failure - if f1.i == 0 || f2.i == 0 { - return frag{} - } - - // TODO: elide nop - - f1.out.patch(c.p, f2.i) - return frag{f1.i, f2.out} -} - -func (c *compiler) alt(f1, f2 frag) frag { - // alt of failure is other - if f1.i == 0 { - return f2 - } - if f2.i == 0 { - return f1 - } - - f := c.inst(InstAlt) - i := &c.p.Inst[f.i] - i.Out = f1.i - i.Arg = f2.i - f.out = f1.out.append(c.p, f2.out) - return f -} - -func (c *compiler) quest(f1 frag, nongreedy bool) frag { - f := c.inst(InstAlt) - i := &c.p.Inst[f.i] - if nongreedy { - i.Arg = f1.i - f.out = patchList(f.i << 1) - } else { - i.Out = f1.i - f.out = patchList(f.i<<1 | 1) - } - f.out = f.out.append(c.p, f1.out) - return f -} - -func (c *compiler) star(f1 frag, nongreedy bool) frag { - f := c.inst(InstAlt) - i := &c.p.Inst[f.i] - if nongreedy { - i.Arg = f1.i - f.out = patchList(f.i << 1) - } else { - i.Out = f1.i - f.out = patchList(f.i<<1 | 1) - } - f1.out.patch(c.p, f.i) - return f -} - -func (c *compiler) plus(f1 frag, nongreedy bool) frag { - return frag{f1.i, c.star(f1, nongreedy).out} -} - -func (c *compiler) empty(op EmptyOp) frag { - f := c.inst(InstEmptyWidth) - c.p.Inst[f.i].Arg = uint32(op) - f.out = patchList(f.i << 1) - return f -} - -func (c *compiler) rune(rune []int) frag { - f := c.inst(InstRune) - c.p.Inst[f.i].Rune = rune - f.out = patchList(f.i << 1) - return f -} diff --git a/src/pkg/exp/regexp/syntax/make_perl_groups.pl b/src/pkg/exp/regexp/syntax/make_perl_groups.pl deleted file mode 100755 index 6d1b84b10..000000000 --- a/src/pkg/exp/regexp/syntax/make_perl_groups.pl +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/perl -# Copyright 2008 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. - -# Modified version of RE2's make_perl_groups.pl. - -# Generate table entries giving character ranges -# for POSIX/Perl character classes. Rather than -# figure out what the definition is, it is easier to ask -# Perl about each letter from 0-128 and write down -# its answer. - -@posixclasses = ( - "[:alnum:]", - "[:alpha:]", - "[:ascii:]", - "[:blank:]", - "[:cntrl:]", - "[:digit:]", - "[:graph:]", - "[:lower:]", - "[:print:]", - "[:punct:]", - "[:space:]", - "[:upper:]", - "[:word:]", - "[:xdigit:]", -); - -@perlclasses = ( - "\\d", - "\\s", - "\\w", -); - -sub ComputeClass($) { - my @ranges; - my ($class) = @_; - my $regexp = "[$class]"; - my $start = -1; - for (my $i=0; $i<=129; $i++) { - if ($i == 129) { $i = 256; } - if ($i <= 128 && chr($i) =~ $regexp) { - if ($start < 0) { - $start = $i; - } - } else { - if ($start >= 0) { - push @ranges, [$start, $i-1]; - } - $start = -1; - } - } - return @ranges; -} - -sub PrintClass($$@) { - my ($cname, $name, @ranges) = @_; - print "var code$cname = []int{ /* $name */\n"; - for (my $i=0; $i<@ranges; $i++) { - my @a = @{$ranges[$i]}; - printf "\t0x%x, 0x%x,\n", $a[0], $a[1]; - } - print "}\n\n"; - my $n = @ranges; - $negname = $name; - if ($negname =~ /:/) { - $negname =~ s/:/:^/; - } else { - $negname =~ y/a-z/A-Z/; - } - return "\t`$name`: {+1, code$cname},\n" . - "\t`$negname`: {-1, code$cname},\n"; -} - -my $gen = 0; - -sub PrintClasses($@) { - my ($cname, @classes) = @_; - my @entries; - foreach my $cl (@classes) { - my @ranges = ComputeClass($cl); - push @entries, PrintClass(++$gen, $cl, @ranges); - } - print "var ${cname}Group = map[string]charGroup{\n"; - foreach my $e (@entries) { - print $e; - } - print "}\n"; - my $count = @entries; -} - -print <<EOF; -// GENERATED BY make_perl_groups.pl; DO NOT EDIT. -// make_perl_groups.pl >perl_groups.go - -package syntax - -EOF - -PrintClasses("perl", @perlclasses); -PrintClasses("posix", @posixclasses); diff --git a/src/pkg/exp/regexp/syntax/parse.go b/src/pkg/exp/regexp/syntax/parse.go deleted file mode 100644 index b6c91f7e1..000000000 --- a/src/pkg/exp/regexp/syntax/parse.go +++ /dev/null @@ -1,1798 +0,0 @@ -// Copyright 2011 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. - -package syntax - -import ( - "os" - "sort" - "strings" - "unicode" - "utf8" -) - -// An Error describes a failure to parse a regular expression -// and gives the offending expression. -type Error struct { - Code ErrorCode - Expr string -} - -func (e *Error) String() string { - return "error parsing regexp: " + e.Code.String() + ": `" + e.Expr + "`" -} - -// An ErrorCode describes a failure to parse a regular expression. -type ErrorCode string - -const ( - // Unexpected error - ErrInternalError ErrorCode = "regexp/syntax: internal error" - - // Parse errors - ErrInvalidCharClass ErrorCode = "invalid character class" - ErrInvalidCharRange ErrorCode = "invalid character class range" - ErrInvalidEscape ErrorCode = "invalid escape sequence" - ErrInvalidNamedCapture ErrorCode = "invalid named capture" - ErrInvalidPerlOp ErrorCode = "invalid or unsupported Perl syntax" - ErrInvalidRepeatOp ErrorCode = "invalid nested repetition operator" - ErrInvalidRepeatSize ErrorCode = "invalid repeat count" - ErrInvalidUTF8 ErrorCode = "invalid UTF-8" - ErrMissingBracket ErrorCode = "missing closing ]" - ErrMissingParen ErrorCode = "missing closing )" - ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator" - ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression" -) - -func (e ErrorCode) String() string { - return string(e) -} - -// Flags control the behavior of the parser and record information about regexp context. -type Flags uint16 - -const ( - FoldCase Flags = 1 << iota // case-insensitive match - Literal // treat pattern as literal string - ClassNL // allow character classes like [^a-z] and [[:space:]] to match newline - DotNL // allow . to match newline - OneLine // treat ^ and $ as only matching at beginning and end of text - NonGreedy // make repetition operators default to non-greedy - PerlX // allow Perl extensions - UnicodeGroups // allow \p{Han}, \P{Han} for Unicode group and negation - WasDollar // regexp OpEndText was $, not \z - Simple // regexp contains no counted repetition - - MatchNL = ClassNL | DotNL - - Perl = ClassNL | OneLine | PerlX | UnicodeGroups // as close to Perl as possible - POSIX Flags = 0 // POSIX syntax -) - -// Pseudo-ops for parsing stack. -const ( - opLeftParen = opPseudo + iota - opVerticalBar -) - -type parser struct { - flags Flags // parse mode flags - stack []*Regexp // stack of parsed expressions - free *Regexp - numCap int // number of capturing groups seen - wholeRegexp string - tmpClass []int // temporary char class work space -} - -func (p *parser) newRegexp(op Op) *Regexp { - re := p.free - if re != nil { - p.free = re.Sub0[0] - *re = Regexp{} - } else { - re = new(Regexp) - } - re.Op = op - return re -} - -func (p *parser) reuse(re *Regexp) { - re.Sub0[0] = p.free - p.free = re -} - -// Parse stack manipulation. - -// push pushes the regexp re onto the parse stack and returns the regexp. -func (p *parser) push(re *Regexp) *Regexp { - if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] { - // Single rune. - if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) { - return nil - } - re.Op = OpLiteral - re.Rune = re.Rune[:1] - re.Flags = p.flags &^ FoldCase - } else if re.Op == OpCharClass && len(re.Rune) == 4 && - re.Rune[0] == re.Rune[1] && re.Rune[2] == re.Rune[3] && - unicode.SimpleFold(re.Rune[0]) == re.Rune[2] && - unicode.SimpleFold(re.Rune[2]) == re.Rune[0] || - re.Op == OpCharClass && len(re.Rune) == 2 && - re.Rune[0]+1 == re.Rune[1] && - unicode.SimpleFold(re.Rune[0]) == re.Rune[1] && - unicode.SimpleFold(re.Rune[1]) == re.Rune[0] { - // Case-insensitive rune like [Aa] or [Δδ]. - if p.maybeConcat(re.Rune[0], p.flags|FoldCase) { - return nil - } - - // Rewrite as (case-insensitive) literal. - re.Op = OpLiteral - re.Rune = re.Rune[:1] - re.Flags = p.flags | FoldCase - } else { - // Incremental concatenation. - p.maybeConcat(-1, 0) - } - - p.stack = append(p.stack, re) - return re -} - -// maybeConcat implements incremental concatenation -// of literal runes into string nodes. The parser calls this -// before each push, so only the top fragment of the stack -// might need processing. Since this is called before a push, -// the topmost literal is no longer subject to operators like * -// (Otherwise ab* would turn into (ab)*.) -// If r >= 0 and there's a node left over, maybeConcat uses it -// to push r with the given flags. -// maybeConcat reports whether r was pushed. -func (p *parser) maybeConcat(r int, flags Flags) bool { - n := len(p.stack) - if n < 2 { - return false - } - - re1 := p.stack[n-1] - re2 := p.stack[n-2] - if re1.Op != OpLiteral || re2.Op != OpLiteral || re1.Flags&FoldCase != re2.Flags&FoldCase { - return false - } - - // Push re1 into re2. - re2.Rune = append(re2.Rune, re1.Rune...) - - // Reuse re1 if possible. - if r >= 0 { - re1.Rune = re1.Rune0[:1] - re1.Rune[0] = r - re1.Flags = flags - return true - } - - p.stack = p.stack[:n-1] - p.reuse(re1) - return false // did not push r -} - -// newLiteral returns a new OpLiteral Regexp with the given flags -func (p *parser) newLiteral(r int, flags Flags) *Regexp { - re := p.newRegexp(OpLiteral) - re.Flags = flags - re.Rune0[0] = r - re.Rune = re.Rune0[:1] - return re -} - -// literal pushes a literal regexp for the rune r on the stack -// and returns that regexp. -func (p *parser) literal(r int) { - p.push(p.newLiteral(r, p.flags)) -} - -// op pushes a regexp with the given op onto the stack -// and returns that regexp. -func (p *parser) op(op Op) *Regexp { - re := p.newRegexp(op) - re.Flags = p.flags - return p.push(re) -} - -// repeat replaces the top stack element with itself repeated -// according to op. -func (p *parser) repeat(op Op, min, max int, opstr, t, lastRepeat string) (string, os.Error) { - flags := p.flags - if p.flags&PerlX != 0 { - if len(t) > 0 && t[0] == '?' { - t = t[1:] - flags ^= NonGreedy - } - if lastRepeat != "" { - // In Perl it is not allowed to stack repetition operators: - // a** is a syntax error, not a doubled star, and a++ means - // something else entirely, which we don't support! - return "", &Error{ErrInvalidRepeatOp, lastRepeat[:len(lastRepeat)-len(t)]} - } - } - n := len(p.stack) - if n == 0 { - return "", &Error{ErrMissingRepeatArgument, opstr} - } - sub := p.stack[n-1] - re := p.newRegexp(op) - re.Min = min - re.Max = max - re.Flags = flags - re.Sub = re.Sub0[:1] - re.Sub[0] = sub - p.stack[n-1] = re - return t, nil -} - -// concat replaces the top of the stack (above the topmost '|' or '(') with its concatenation. -func (p *parser) concat() *Regexp { - p.maybeConcat(-1, 0) - - // Scan down to find pseudo-operator | or (. - i := len(p.stack) - for i > 0 && p.stack[i-1].Op < opPseudo { - i-- - } - subs := p.stack[i:] - p.stack = p.stack[:i] - - // Empty concatenation is special case. - if len(subs) == 0 { - return p.push(p.newRegexp(OpEmptyMatch)) - } - - return p.push(p.collapse(subs, OpConcat)) -} - -// alternate replaces the top of the stack (above the topmost '(') with its alternation. -func (p *parser) alternate() *Regexp { - // Scan down to find pseudo-operator (. - // There are no | above (. - i := len(p.stack) - for i > 0 && p.stack[i-1].Op < opPseudo { - i-- - } - subs := p.stack[i:] - p.stack = p.stack[:i] - - // Make sure top class is clean. - // All the others already are (see swapVerticalBar). - if len(subs) > 0 { - cleanAlt(subs[len(subs)-1]) - } - - // Empty alternate is special case - // (shouldn't happen but easy to handle). - if len(subs) == 0 { - return p.push(p.newRegexp(OpNoMatch)) - } - - return p.push(p.collapse(subs, OpAlternate)) -} - -// cleanAlt cleans re for eventual inclusion in an alternation. -func cleanAlt(re *Regexp) { - switch re.Op { - case OpCharClass: - re.Rune = cleanClass(&re.Rune) - if len(re.Rune) == 2 && re.Rune[0] == 0 && re.Rune[1] == unicode.MaxRune { - re.Rune = nil - re.Op = OpAnyChar - return - } - if len(re.Rune) == 4 && re.Rune[0] == 0 && re.Rune[1] == '\n'-1 && re.Rune[2] == '\n'+1 && re.Rune[3] == unicode.MaxRune { - re.Rune = nil - re.Op = OpAnyCharNotNL - return - } - if cap(re.Rune)-len(re.Rune) > 100 { - // re.Rune will not grow any more. - // Make a copy or inline to reclaim storage. - re.Rune = append(re.Rune0[:0], re.Rune...) - } - } -} - -// collapse returns the result of applying op to sub. -// If sub contains op nodes, they all get hoisted up -// so that there is never a concat of a concat or an -// alternate of an alternate. -func (p *parser) collapse(subs []*Regexp, op Op) *Regexp { - if len(subs) == 1 { - return subs[0] - } - re := p.newRegexp(op) - re.Sub = re.Sub0[:0] - for _, sub := range subs { - if sub.Op == op { - re.Sub = append(re.Sub, sub.Sub...) - p.reuse(sub) - } else { - re.Sub = append(re.Sub, sub) - } - } - if op == OpAlternate { - re.Sub = p.factor(re.Sub, re.Flags) - if len(re.Sub) == 1 { - old := re - re = re.Sub[0] - p.reuse(old) - } - } - return re -} - -// factor factors common prefixes from the alternation list sub. -// It returns a replacement list that reuses the same storage and -// frees (passes to p.reuse) any removed *Regexps. -// -// For example, -// ABC|ABD|AEF|BCX|BCY -// simplifies by literal prefix extraction to -// A(B(C|D)|EF)|BC(X|Y) -// which simplifies by character class introduction to -// A(B[CD]|EF)|BC[XY] -// -func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp { - if len(sub) < 2 { - return sub - } - - // Round 1: Factor out common literal prefixes. - var str []int - var strflags Flags - start := 0 - out := sub[:0] - for i := 0; i <= len(sub); i++ { - // Invariant: the Regexps that were in sub[0:start] have been - // used or marked for reuse, and the slice space has been reused - // for out (len(out) <= start). - // - // Invariant: sub[start:i] consists of regexps that all begin - // with str as modified by strflags. - var istr []int - var iflags Flags - if i < len(sub) { - istr, iflags = p.leadingString(sub[i]) - if iflags == strflags { - same := 0 - for same < len(str) && same < len(istr) && str[same] == istr[same] { - same++ - } - if same > 0 { - // Matches at least one rune in current range. - // Keep going around. - str = str[:same] - continue - } - } - } - - // Found end of a run with common leading literal string: - // sub[start:i] all begin with str[0:len(str)], but sub[i] - // does not even begin with str[0]. - // - // Factor out common string and append factored expression to out. - if i == start { - // Nothing to do - run of length 0. - } else if i == start+1 { - // Just one: don't bother factoring. - out = append(out, sub[start]) - } else { - // Construct factored form: prefix(suffix1|suffix2|...) - prefix := p.newRegexp(OpLiteral) - prefix.Flags = strflags - prefix.Rune = append(prefix.Rune[:0], str...) - - for j := start; j < i; j++ { - sub[j] = p.removeLeadingString(sub[j], len(str)) - } - suffix := p.collapse(sub[start:i], OpAlternate) // recurse - - re := p.newRegexp(OpConcat) - re.Sub = append(re.Sub[:0], prefix, suffix) - out = append(out, re) - } - - // Prepare for next iteration. - start = i - str = istr - strflags = iflags - } - sub = out - - // Round 2: Factor out common complex prefixes, - // just the first piece of each concatenation, - // whatever it is. This is good enough a lot of the time. - start = 0 - out = sub[:0] - var first *Regexp - for i := 0; i <= len(sub); i++ { - // Invariant: the Regexps that were in sub[0:start] have been - // used or marked for reuse, and the slice space has been reused - // for out (len(out) <= start). - // - // Invariant: sub[start:i] consists of regexps that all begin - // with str as modified by strflags. - var ifirst *Regexp - if i < len(sub) { - ifirst = p.leadingRegexp(sub[i]) - if first != nil && first.Equal(ifirst) { - continue - } - } - - // Found end of a run with common leading regexp: - // sub[start:i] all begin with first but sub[i] does not. - // - // Factor out common regexp and append factored expression to out. - if i == start { - // Nothing to do - run of length 0. - } else if i == start+1 { - // Just one: don't bother factoring. - out = append(out, sub[start]) - } else { - // Construct factored form: prefix(suffix1|suffix2|...) - prefix := first - - for j := start; j < i; j++ { - reuse := j != start // prefix came from sub[start] - sub[j] = p.removeLeadingRegexp(sub[j], reuse) - } - suffix := p.collapse(sub[start:i], OpAlternate) // recurse - - re := p.newRegexp(OpConcat) - re.Sub = append(re.Sub[:0], prefix, suffix) - out = append(out, re) - } - - // Prepare for next iteration. - start = i - first = ifirst - } - sub = out - - // Round 3: Collapse runs of single literals into character classes. - start = 0 - out = sub[:0] - for i := 0; i <= len(sub); i++ { - // Invariant: the Regexps that were in sub[0:start] have been - // used or marked for reuse, and the slice space has been reused - // for out (len(out) <= start). - // - // Invariant: sub[start:i] consists of regexps that are either - // literal runes or character classes. - if i < len(sub) && isCharClass(sub[i]) { - continue - } - - // sub[i] is not a char or char class; - // emit char class for sub[start:i]... - if i == start { - // Nothing to do - run of length 0. - } else if i == start+1 { - out = append(out, sub[start]) - } else { - // Make new char class. - // Start with most complex regexp in sub[start]. - max := start - for j := start + 1; j < i; j++ { - if sub[max].Op < sub[j].Op || sub[max].Op == sub[j].Op && len(sub[max].Rune) < len(sub[j].Rune) { - max = j - } - } - sub[start], sub[max] = sub[max], sub[start] - - for j := start + 1; j < i; j++ { - mergeCharClass(sub[start], sub[j]) - p.reuse(sub[j]) - } - cleanAlt(sub[start]) - out = append(out, sub[start]) - } - - // ... and then emit sub[i]. - if i < len(sub) { - out = append(out, sub[i]) - } - start = i + 1 - } - sub = out - - // Round 4: Collapse runs of empty matches into a single empty match. - start = 0 - out = sub[:0] - for i := range sub { - if i+1 < len(sub) && sub[i].Op == OpEmptyMatch && sub[i+1].Op == OpEmptyMatch { - continue - } - out = append(out, sub[i]) - } - sub = out - - return sub -} - -// leadingString returns the leading literal string that re begins with. -// The string refers to storage in re or its children. -func (p *parser) leadingString(re *Regexp) ([]int, Flags) { - if re.Op == OpConcat && len(re.Sub) > 0 { - re = re.Sub[0] - } - if re.Op != OpLiteral { - return nil, 0 - } - return re.Rune, re.Flags & FoldCase -} - -// removeLeadingString removes the first n leading runes -// from the beginning of re. It returns the replacement for re. -func (p *parser) removeLeadingString(re *Regexp, n int) *Regexp { - if re.Op == OpConcat && len(re.Sub) > 0 { - // Removing a leading string in a concatenation - // might simplify the concatenation. - sub := re.Sub[0] - sub = p.removeLeadingString(sub, n) - re.Sub[0] = sub - if sub.Op == OpEmptyMatch { - p.reuse(sub) - switch len(re.Sub) { - case 0, 1: - // Impossible but handle. - re.Op = OpEmptyMatch - re.Sub = nil - case 2: - old := re - re = re.Sub[1] - p.reuse(old) - default: - copy(re.Sub, re.Sub[1:]) - re.Sub = re.Sub[:len(re.Sub)-1] - } - } - return re - } - - if re.Op == OpLiteral { - re.Rune = re.Rune[:copy(re.Rune, re.Rune[n:])] - if len(re.Rune) == 0 { - re.Op = OpEmptyMatch - } - } - return re -} - -// leadingRegexp returns the leading regexp that re begins with. -// The regexp refers to storage in re or its children. -func (p *parser) leadingRegexp(re *Regexp) *Regexp { - if re.Op == OpEmptyMatch { - return nil - } - if re.Op == OpConcat && len(re.Sub) > 0 { - sub := re.Sub[0] - if sub.Op == OpEmptyMatch { - return nil - } - return sub - } - return re -} - -// removeLeadingRegexp removes the leading regexp in re. -// It returns the replacement for re. -// If reuse is true, it passes the removed regexp (if no longer needed) to p.reuse. -func (p *parser) removeLeadingRegexp(re *Regexp, reuse bool) *Regexp { - if re.Op == OpConcat && len(re.Sub) > 0 { - if reuse { - p.reuse(re.Sub[0]) - } - re.Sub = re.Sub[:copy(re.Sub, re.Sub[1:])] - switch len(re.Sub) { - case 0: - re.Op = OpEmptyMatch - re.Sub = nil - case 1: - old := re - re = re.Sub[0] - p.reuse(old) - } - return re - } - re.Op = OpEmptyMatch - return re -} - -func literalRegexp(s string, flags Flags) *Regexp { - re := &Regexp{Op: OpLiteral} - re.Flags = flags - re.Rune = re.Rune0[:0] // use local storage for small strings - for _, c := range s { - if len(re.Rune) >= cap(re.Rune) { - // string is too long to fit in Rune0. let Go handle it - re.Rune = []int(s) - break - } - re.Rune = append(re.Rune, c) - } - return re -} - -// Parsing. - -func Parse(s string, flags Flags) (*Regexp, os.Error) { - if flags&Literal != 0 { - // Trivial parser for literal string. - if err := checkUTF8(s); err != nil { - return nil, err - } - return literalRegexp(s, flags), nil - } - - // Otherwise, must do real work. - var ( - p parser - err os.Error - c int - op Op - lastRepeat string - min, max int - ) - p.flags = flags - p.wholeRegexp = s - t := s - for t != "" { - repeat := "" - BigSwitch: - switch t[0] { - default: - if c, t, err = nextRune(t); err != nil { - return nil, err - } - p.literal(c) - - case '(': - if p.flags&PerlX != 0 && len(t) >= 2 && t[1] == '?' { - // Flag changes and non-capturing groups. - if t, err = p.parsePerlFlags(t); err != nil { - return nil, err - } - break - } - p.numCap++ - p.op(opLeftParen).Cap = p.numCap - t = t[1:] - case '|': - if err = p.parseVerticalBar(); err != nil { - return nil, err - } - t = t[1:] - case ')': - if err = p.parseRightParen(); err != nil { - return nil, err - } - t = t[1:] - case '^': - if p.flags&OneLine != 0 { - p.op(OpBeginText) - } else { - p.op(OpBeginLine) - } - t = t[1:] - case '$': - if p.flags&OneLine != 0 { - p.op(OpEndText).Flags |= WasDollar - } else { - p.op(OpEndLine) - } - t = t[1:] - case '.': - if p.flags&DotNL != 0 { - p.op(OpAnyChar) - } else { - p.op(OpAnyCharNotNL) - } - t = t[1:] - case '[': - if t, err = p.parseClass(t); err != nil { - return nil, err - } - case '*', '+', '?': - switch t[0] { - case '*': - op = OpStar - case '+': - op = OpPlus - case '?': - op = OpQuest - } - if t, err = p.repeat(op, min, max, t[:1], t[1:], lastRepeat); err != nil { - return nil, err - } - case '{': - op = OpRepeat - min, max, tt, ok := p.parseRepeat(t) - if !ok { - // If the repeat cannot be parsed, { is a literal. - p.literal('{') - t = t[1:] - break - } - if t, err = p.repeat(op, min, max, t[:len(t)-len(tt)], tt, lastRepeat); err != nil { - return nil, err - } - case '\\': - if p.flags&PerlX != 0 && len(t) >= 2 { - switch t[1] { - case 'A': - p.op(OpBeginText) - t = t[2:] - break BigSwitch - case 'b': - p.op(OpWordBoundary) - t = t[2:] - break BigSwitch - case 'B': - p.op(OpNoWordBoundary) - t = t[2:] - break BigSwitch - case 'C': - // any byte; not supported - return nil, &Error{ErrInvalidEscape, t[:2]} - case 'Q': - // \Q ... \E: the ... is always literals - var lit string - if i := strings.Index(t, `\E`); i < 0 { - lit = t[2:] - t = "" - } else { - lit = t[2:i] - t = t[i+2:] - } - p.push(literalRegexp(lit, p.flags)) - break BigSwitch - case 'z': - p.op(OpEndText) - t = t[2:] - break BigSwitch - } - } - - re := p.newRegexp(OpCharClass) - re.Flags = p.flags - - // Look for Unicode character group like \p{Han} - if len(t) >= 2 && (t[1] == 'p' || t[1] == 'P') { - r, rest, err := p.parseUnicodeClass(t, re.Rune0[:0]) - if err != nil { - return nil, err - } - if r != nil { - re.Rune = r - t = rest - p.push(re) - break BigSwitch - } - } - - // Perl character class escape. - if r, rest := p.parsePerlClassEscape(t, re.Rune0[:0]); r != nil { - re.Rune = r - t = rest - p.push(re) - break BigSwitch - } - p.reuse(re) - - // Ordinary single-character escape. - if c, t, err = p.parseEscape(t); err != nil { - return nil, err - } - p.literal(c) - } - lastRepeat = repeat - } - - p.concat() - if p.swapVerticalBar() { - // pop vertical bar - p.stack = p.stack[:len(p.stack)-1] - } - p.alternate() - - n := len(p.stack) - if n != 1 { - return nil, &Error{ErrMissingParen, s} - } - return p.stack[0], nil -} - -// parseRepeat parses {min} (max=min) or {min,} (max=-1) or {min,max}. -// If s is not of that form, it returns ok == false. -func (p *parser) parseRepeat(s string) (min, max int, rest string, ok bool) { - if s == "" || s[0] != '{' { - return - } - s = s[1:] - if min, s, ok = p.parseInt(s); !ok { - return - } - if s == "" { - return - } - if s[0] != ',' { - max = min - } else { - s = s[1:] - if s == "" { - return - } - if s[0] == '}' { - max = -1 - } else if max, s, ok = p.parseInt(s); !ok { - return - } - } - if s == "" || s[0] != '}' { - return - } - rest = s[1:] - ok = true - return -} - -// parsePerlFlags parses a Perl flag setting or non-capturing group or both, -// like (?i) or (?: or (?i:. It removes the prefix from s and updates the parse state. -// The caller must have ensured that s begins with "(?". -func (p *parser) parsePerlFlags(s string) (rest string, err os.Error) { - t := s - - // Check for named captures, first introduced in Python's regexp library. - // As usual, there are three slightly different syntaxes: - // - // (?P<name>expr) the original, introduced by Python - // (?<name>expr) the .NET alteration, adopted by Perl 5.10 - // (?'name'expr) another .NET alteration, adopted by Perl 5.10 - // - // Perl 5.10 gave in and implemented the Python version too, - // but they claim that the last two are the preferred forms. - // PCRE and languages based on it (specifically, PHP and Ruby) - // support all three as well. EcmaScript 4 uses only the Python form. - // - // In both the open source world (via Code Search) and the - // Google source tree, (?P<expr>name) is the dominant form, - // so that's the one we implement. One is enough. - if len(t) > 4 && t[2] == 'P' && t[3] == '<' { - // Pull out name. - end := strings.IndexRune(t, '>') - if end < 0 { - if err = checkUTF8(t); err != nil { - return "", err - } - return "", &Error{ErrInvalidNamedCapture, s} - } - - capture := t[:end+1] // "(?P<name>" - name := t[4:end] // "name" - if err = checkUTF8(name); err != nil { - return "", err - } - if !isValidCaptureName(name) { - return "", &Error{ErrInvalidNamedCapture, capture} - } - - // Like ordinary capture, but named. - p.numCap++ - re := p.op(opLeftParen) - re.Cap = p.numCap - re.Name = name - return t[end+1:], nil - } - - // Non-capturing group. Might also twiddle Perl flags. - var c int - t = t[2:] // skip (? - flags := p.flags - sign := +1 - sawFlag := false -Loop: - for t != "" { - if c, t, err = nextRune(t); err != nil { - return "", err - } - switch c { - default: - break Loop - - // Flags. - case 'i': - flags |= FoldCase - sawFlag = true - case 'm': - flags &^= OneLine - sawFlag = true - case 's': - flags |= DotNL - sawFlag = true - case 'U': - flags |= NonGreedy - sawFlag = true - - // Switch to negation. - case '-': - if sign < 0 { - break Loop - } - sign = -1 - // Invert flags so that | above turn into &^ and vice versa. - // We'll invert flags again before using it below. - flags = ^flags - sawFlag = false - - // End of flags, starting group or not. - case ':', ')': - if sign < 0 { - if !sawFlag { - break Loop - } - flags = ^flags - } - if c == ':' { - // Open new group - p.op(opLeftParen) - } - p.flags = flags - return t, nil - } - } - - return "", &Error{ErrInvalidPerlOp, s[:len(s)-len(t)]} -} - -// isValidCaptureName reports whether name -// is a valid capture name: [A-Za-z0-9_]+. -// PCRE limits names to 32 bytes. -// Python rejects names starting with digits. -// We don't enforce either of those. -func isValidCaptureName(name string) bool { - if name == "" { - return false - } - for _, c := range name { - if c != '_' && !isalnum(c) { - return false - } - } - return true -} - -// parseInt parses a decimal integer. -func (p *parser) parseInt(s string) (n int, rest string, ok bool) { - if s == "" || s[0] < '0' || '9' < s[0] { - return - } - // Disallow leading zeros. - if len(s) >= 2 && s[0] == '0' && '0' <= s[1] && s[1] <= '9' { - return - } - for s != "" && '0' <= s[0] && s[0] <= '9' { - // Avoid overflow. - if n >= 1e8 { - return - } - n = n*10 + int(s[0]) - '0' - s = s[1:] - } - rest = s - ok = true - return -} - -// can this be represented as a character class? -// single-rune literal string, char class, ., and .|\n. -func isCharClass(re *Regexp) bool { - return re.Op == OpLiteral && len(re.Rune) == 1 || - re.Op == OpCharClass || - re.Op == OpAnyCharNotNL || - re.Op == OpAnyChar -} - -// does re match r? -func matchRune(re *Regexp, r int) bool { - switch re.Op { - case OpLiteral: - return len(re.Rune) == 1 && re.Rune[0] == r - case OpCharClass: - for i := 0; i < len(re.Rune); i += 2 { - if re.Rune[i] <= r && r <= re.Rune[i+1] { - return true - } - } - return false - case OpAnyCharNotNL: - return r != '\n' - case OpAnyChar: - return true - } - return false -} - -// parseVerticalBar handles a | in the input. -func (p *parser) parseVerticalBar() os.Error { - p.concat() - - // The concatenation we just parsed is on top of the stack. - // If it sits above an opVerticalBar, swap it below - // (things below an opVerticalBar become an alternation). - // Otherwise, push a new vertical bar. - if !p.swapVerticalBar() { - p.op(opVerticalBar) - } - - return nil -} - -// mergeCharClass makes dst = dst|src. -// The caller must ensure that dst.Op >= src.Op, -// to reduce the amount of copying. -func mergeCharClass(dst, src *Regexp) { - switch dst.Op { - case OpAnyChar: - // src doesn't add anything. - case OpAnyCharNotNL: - // src might add \n - if matchRune(src, '\n') { - dst.Op = OpAnyChar - } - case OpCharClass: - // src is simpler, so either literal or char class - if src.Op == OpLiteral { - dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0]) - } else { - dst.Rune = appendClass(dst.Rune, src.Rune) - } - case OpLiteral: - // both literal - if src.Rune[0] == dst.Rune[0] { - break - } - dst.Op = OpCharClass - dst.Rune = append(dst.Rune, dst.Rune[0]) - dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0]) - } -} - -// If the top of the stack is an element followed by an opVerticalBar -// swapVerticalBar swaps the two and returns true. -// Otherwise it returns false. -func (p *parser) swapVerticalBar() bool { - // If above and below vertical bar are literal or char class, - // can merge into a single char class. - n := len(p.stack) - if n >= 3 && p.stack[n-2].Op == opVerticalBar && isCharClass(p.stack[n-1]) && isCharClass(p.stack[n-3]) { - re1 := p.stack[n-1] - re3 := p.stack[n-3] - // Make re3 the more complex of the two. - if re1.Op > re3.Op { - re1, re3 = re3, re1 - p.stack[n-3] = re3 - } - mergeCharClass(re3, re1) - p.reuse(re1) - p.stack = p.stack[:n-1] - return true - } - - if n >= 2 { - re1 := p.stack[n-1] - re2 := p.stack[n-2] - if re2.Op == opVerticalBar { - if n >= 3 { - // Now out of reach. - // Clean opportunistically. - cleanAlt(p.stack[n-3]) - } - p.stack[n-2] = re1 - p.stack[n-1] = re2 - return true - } - } - return false -} - -// parseRightParen handles a ) in the input. -func (p *parser) parseRightParen() os.Error { - p.concat() - if p.swapVerticalBar() { - // pop vertical bar - p.stack = p.stack[:len(p.stack)-1] - } - p.alternate() - - n := len(p.stack) - if n < 2 { - return &Error{ErrInternalError, ""} - } - re1 := p.stack[n-1] - re2 := p.stack[n-2] - p.stack = p.stack[:n-2] - if re2.Op != opLeftParen { - return &Error{ErrMissingParen, p.wholeRegexp} - } - if re2.Cap == 0 { - // Just for grouping. - p.push(re1) - } else { - re2.Op = OpCapture - re2.Sub = re2.Sub0[:1] - re2.Sub[0] = re1 - p.push(re2) - } - return nil -} - -// parseEscape parses an escape sequence at the beginning of s -// and returns the rune. -func (p *parser) parseEscape(s string) (r int, rest string, err os.Error) { - t := s[1:] - if t == "" { - return 0, "", &Error{ErrTrailingBackslash, ""} - } - c, t, err := nextRune(t) - if err != nil { - return 0, "", err - } - -Switch: - switch c { - default: - if c < utf8.RuneSelf && !isalnum(c) { - // Escaped non-word characters are always themselves. - // PCRE is not quite so rigorous: it accepts things like - // \q, but we don't. We once rejected \_, but too many - // programs and people insist on using it, so allow \_. - return c, t, nil - } - - // Octal escapes. - case '1', '2', '3', '4', '5', '6', '7': - // Single non-zero digit is a backreference; not supported - if t == "" || t[0] < '0' || t[0] > '7' { - break - } - fallthrough - case '0': - // Consume up to three octal digits; already have one. - r = c - '0' - for i := 1; i < 3; i++ { - if t == "" || t[0] < '0' || t[0] > '7' { - break - } - r = r*8 + int(t[0]) - '0' - t = t[1:] - } - return r, t, nil - - // Hexadecimal escapes. - case 'x': - if t == "" { - break - } - if c, t, err = nextRune(t); err != nil { - return 0, "", err - } - if c == '{' { - // Any number of digits in braces. - // Perl accepts any text at all; it ignores all text - // after the first non-hex digit. We require only hex digits, - // and at least one. - nhex := 0 - r = 0 - for { - if t == "" { - break Switch - } - if c, t, err = nextRune(t); err != nil { - return 0, "", err - } - if c == '}' { - break - } - v := unhex(c) - if v < 0 { - break Switch - } - r = r*16 + v - if r > unicode.MaxRune { - break Switch - } - nhex++ - } - if nhex == 0 { - break Switch - } - return r, t, nil - } - - // Easy case: two hex digits. - x := unhex(c) - if c, t, err = nextRune(t); err != nil { - return 0, "", err - } - y := unhex(c) - if x < 0 || y < 0 { - break - } - return x*16 + y, t, nil - - // C escapes. There is no case 'b', to avoid misparsing - // the Perl word-boundary \b as the C backspace \b - // when in POSIX mode. In Perl, /\b/ means word-boundary - // but /[\b]/ means backspace. We don't support that. - // If you want a backspace, embed a literal backspace - // character or use \x08. - case 'a': - return '\a', t, err - case 'f': - return '\f', t, err - case 'n': - return '\n', t, err - case 'r': - return '\r', t, err - case 't': - return '\t', t, err - case 'v': - return '\v', t, err - } - return 0, "", &Error{ErrInvalidEscape, s[:len(s)-len(t)]} -} - -// parseClassChar parses a character class character at the beginning of s -// and returns it. -func (p *parser) parseClassChar(s, wholeClass string) (r int, rest string, err os.Error) { - if s == "" { - return 0, "", &Error{Code: ErrMissingBracket, Expr: wholeClass} - } - - // Allow regular escape sequences even though - // many need not be escaped in this context. - if s[0] == '\\' { - return p.parseEscape(s) - } - - return nextRune(s) -} - -type charGroup struct { - sign int - class []int -} - -// parsePerlClassEscape parses a leading Perl character class escape like \d -// from the beginning of s. If one is present, it appends the characters to r -// and returns the new slice r and the remainder of the string. -func (p *parser) parsePerlClassEscape(s string, r []int) (out []int, rest string) { - if p.flags&PerlX == 0 || len(s) < 2 || s[0] != '\\' { - return - } - g := perlGroup[s[0:2]] - if g.sign == 0 { - return - } - return p.appendGroup(r, g), s[2:] -} - -// parseNamedClass parses a leading POSIX named character class like [:alnum:] -// from the beginning of s. If one is present, it appends the characters to r -// and returns the new slice r and the remainder of the string. -func (p *parser) parseNamedClass(s string, r []int) (out []int, rest string, err os.Error) { - if len(s) < 2 || s[0] != '[' || s[1] != ':' { - return - } - - i := strings.Index(s[2:], ":]") - if i < 0 { - return - } - i += 2 - name, s := s[0:i+2], s[i+2:] - g := posixGroup[name] - if g.sign == 0 { - return nil, "", &Error{ErrInvalidCharRange, name} - } - return p.appendGroup(r, g), s, nil -} - -func (p *parser) appendGroup(r []int, g charGroup) []int { - if p.flags&FoldCase == 0 { - if g.sign < 0 { - r = appendNegatedClass(r, g.class) - } else { - r = appendClass(r, g.class) - } - } else { - tmp := p.tmpClass[:0] - tmp = appendFoldedClass(tmp, g.class) - p.tmpClass = tmp - tmp = cleanClass(&p.tmpClass) - if g.sign < 0 { - r = appendNegatedClass(r, tmp) - } else { - r = appendClass(r, tmp) - } - } - return r -} - -// unicodeTable returns the unicode.RangeTable identified by name -// and the table of additional fold-equivalent code points. -func unicodeTable(name string) (*unicode.RangeTable, *unicode.RangeTable) { - if t := unicode.Categories[name]; t != nil { - return t, unicode.FoldCategory[name] - } - if t := unicode.Scripts[name]; t != nil { - return t, unicode.FoldScript[name] - } - return nil, nil -} - -// parseUnicodeClass parses a leading Unicode character class like \p{Han} -// from the beginning of s. If one is present, it appends the characters to r -// and returns the new slice r and the remainder of the string. -func (p *parser) parseUnicodeClass(s string, r []int) (out []int, rest string, err os.Error) { - if p.flags&UnicodeGroups == 0 || len(s) < 2 || s[0] != '\\' || s[1] != 'p' && s[1] != 'P' { - return - } - - // Committed to parse or return error. - sign := +1 - if s[1] == 'P' { - sign = -1 - } - t := s[2:] - c, t, err := nextRune(t) - if err != nil { - return - } - var seq, name string - if c != '{' { - // Single-letter name. - seq = s[:len(s)-len(t)] - name = seq[2:] - } else { - // Name is in braces. - end := strings.IndexRune(s, '}') - if end < 0 { - if err = checkUTF8(s); err != nil { - return - } - return nil, "", &Error{ErrInvalidCharRange, s} - } - seq, t = s[:end+1], s[end+1:] - name = s[3:end] - if err = checkUTF8(name); err != nil { - return - } - } - - // Group can have leading negation too. \p{^Han} == \P{Han}, \P{^Han} == \p{Han}. - if name != "" && name[0] == '^' { - sign = -sign - name = name[1:] - } - - tab, fold := unicodeTable(name) - if tab == nil { - return nil, "", &Error{ErrInvalidCharRange, seq} - } - - if p.flags&FoldCase == 0 || fold == nil { - if sign > 0 { - r = appendTable(r, tab) - } else { - r = appendNegatedTable(r, tab) - } - } else { - // Merge and clean tab and fold in a temporary buffer. - // This is necessary for the negative case and just tidy - // for the positive case. - tmp := p.tmpClass[:0] - tmp = appendTable(tmp, tab) - tmp = appendTable(tmp, fold) - p.tmpClass = tmp - tmp = cleanClass(&p.tmpClass) - if sign > 0 { - r = appendClass(r, tmp) - } else { - r = appendNegatedClass(r, tmp) - } - } - return r, t, nil -} - -// parseClass parses a character class at the beginning of s -// and pushes it onto the parse stack. -func (p *parser) parseClass(s string) (rest string, err os.Error) { - t := s[1:] // chop [ - re := p.newRegexp(OpCharClass) - re.Flags = p.flags - re.Rune = re.Rune0[:0] - - sign := +1 - if t != "" && t[0] == '^' { - sign = -1 - t = t[1:] - - // If character class does not match \n, add it here, - // so that negation later will do the right thing. - if p.flags&ClassNL == 0 { - re.Rune = append(re.Rune, '\n', '\n') - } - } - - class := re.Rune - first := true // ] and - are okay as first char in class - for t == "" || t[0] != ']' || first { - // POSIX: - is only okay unescaped as first or last in class. - // Perl: - is okay anywhere. - if t != "" && t[0] == '-' && p.flags&PerlX == 0 && !first && (len(t) == 1 || t[1] != ']') { - _, size := utf8.DecodeRuneInString(t[1:]) - return "", &Error{Code: ErrInvalidCharRange, Expr: t[:1+size]} - } - first = false - - // Look for POSIX [:alnum:] etc. - if len(t) > 2 && t[0] == '[' && t[1] == ':' { - nclass, nt, err := p.parseNamedClass(t, class) - if err != nil { - return "", err - } - if nclass != nil { - class, t = nclass, nt - continue - } - } - - // Look for Unicode character group like \p{Han}. - nclass, nt, err := p.parseUnicodeClass(t, class) - if err != nil { - return "", err - } - if nclass != nil { - class, t = nclass, nt - continue - } - - // Look for Perl character class symbols (extension). - if nclass, nt := p.parsePerlClassEscape(t, class); nclass != nil { - class, t = nclass, nt - continue - } - - // Single character or simple range. - rng := t - var lo, hi int - if lo, t, err = p.parseClassChar(t, s); err != nil { - return "", err - } - hi = lo - // [a-] means (a|-) so check for final ]. - if len(t) >= 2 && t[0] == '-' && t[1] != ']' { - t = t[1:] - if hi, t, err = p.parseClassChar(t, s); err != nil { - return "", err - } - if hi < lo { - rng = rng[:len(rng)-len(t)] - return "", &Error{Code: ErrInvalidCharRange, Expr: rng} - } - } - if p.flags&FoldCase == 0 { - class = appendRange(class, lo, hi) - } else { - class = appendFoldedRange(class, lo, hi) - } - } - t = t[1:] // chop ] - - // Use &re.Rune instead of &class to avoid allocation. - re.Rune = class - class = cleanClass(&re.Rune) - if sign < 0 { - class = negateClass(class) - } - re.Rune = class - p.push(re) - return t, nil -} - -// cleanClass sorts the ranges (pairs of elements of r), -// merges them, and eliminates duplicates. -func cleanClass(rp *[]int) []int { - - // Sort by lo increasing, hi decreasing to break ties. - sort.Sort(ranges{rp}) - - r := *rp - if len(r) < 2 { - return r - } - - // Merge abutting, overlapping. - w := 2 // write index - for i := 2; i < len(r); i += 2 { - lo, hi := r[i], r[i+1] - if lo <= r[w-1]+1 { - // merge with previous range - if hi > r[w-1] { - r[w-1] = hi - } - continue - } - // new disjoint range - r[w] = lo - r[w+1] = hi - w += 2 - } - - return r[:w] -} - -// appendRange returns the result of appending the range lo-hi to the class r. -func appendRange(r []int, lo, hi int) []int { - // Expand last range or next to last range if it overlaps or abuts. - // Checking two ranges helps when appending case-folded - // alphabets, so that one range can be expanding A-Z and the - // other expanding a-z. - n := len(r) - for i := 2; i <= 4; i += 2 { // twice, using i=2, i=4 - if n >= i { - rlo, rhi := r[n-i], r[n-i+1] - if lo <= rhi+1 && rlo <= hi+1 { - if lo < rlo { - r[n-i] = lo - } - if hi > rhi { - r[n-i+1] = hi - } - return r - } - } - } - - return append(r, lo, hi) -} - -const ( - // minimum and maximum runes involved in folding. - // checked during test. - minFold = 0x0041 - maxFold = 0x1044f -) - -// appendFoldedRange returns the result of appending the range lo-hi -// and its case folding-equivalent runes to the class r. -func appendFoldedRange(r []int, lo, hi int) []int { - // Optimizations. - if lo <= minFold && hi >= maxFold { - // Range is full: folding can't add more. - return appendRange(r, lo, hi) - } - if hi < minFold || lo > maxFold { - // Range is outside folding possibilities. - return appendRange(r, lo, hi) - } - if lo < minFold { - // [lo, minFold-1] needs no folding. - r = appendRange(r, lo, minFold-1) - lo = minFold - } - if hi > maxFold { - // [maxFold+1, hi] needs no folding. - r = appendRange(r, maxFold+1, hi) - hi = maxFold - } - - // Brute force. Depend on appendRange to coalesce ranges on the fly. - for c := lo; c <= hi; c++ { - r = appendRange(r, c, c) - f := unicode.SimpleFold(c) - for f != c { - r = appendRange(r, f, f) - f = unicode.SimpleFold(f) - } - } - return r -} - -// appendClass returns the result of appending the class x to the class r. -// It assume x is clean. -func appendClass(r []int, x []int) []int { - for i := 0; i < len(x); i += 2 { - r = appendRange(r, x[i], x[i+1]) - } - return r -} - -// appendFolded returns the result of appending the case folding of the class x to the class r. -func appendFoldedClass(r []int, x []int) []int { - for i := 0; i < len(x); i += 2 { - r = appendFoldedRange(r, x[i], x[i+1]) - } - return r -} - -// appendNegatedClass returns the result of appending the negation of the class x to the class r. -// It assumes x is clean. -func appendNegatedClass(r []int, x []int) []int { - nextLo := 0 - for i := 0; i < len(x); i += 2 { - lo, hi := x[i], x[i+1] - if nextLo <= lo-1 { - r = appendRange(r, nextLo, lo-1) - } - nextLo = hi + 1 - } - if nextLo <= unicode.MaxRune { - r = appendRange(r, nextLo, unicode.MaxRune) - } - return r -} - -// appendTable returns the result of appending x to the class r. -func appendTable(r []int, x *unicode.RangeTable) []int { - for _, xr := range x.R16 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - r = appendRange(r, lo, hi) - continue - } - for c := lo; c <= hi; c += stride { - r = appendRange(r, c, c) - } - } - for _, xr := range x.R32 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - r = appendRange(r, lo, hi) - continue - } - for c := lo; c <= hi; c += stride { - r = appendRange(r, c, c) - } - } - return r -} - -// appendNegatedTable returns the result of appending the negation of x to the class r. -func appendNegatedTable(r []int, x *unicode.RangeTable) []int { - nextLo := 0 // lo end of next class to add - for _, xr := range x.R16 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - if nextLo <= lo-1 { - r = appendRange(r, nextLo, lo-1) - } - nextLo = hi + 1 - continue - } - for c := lo; c <= hi; c += stride { - if nextLo <= c-1 { - r = appendRange(r, nextLo, c-1) - } - nextLo = c + 1 - } - } - for _, xr := range x.R32 { - lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) - if stride == 1 { - if nextLo <= lo-1 { - r = appendRange(r, nextLo, lo-1) - } - nextLo = hi + 1 - continue - } - for c := lo; c <= hi; c += stride { - if nextLo <= c-1 { - r = appendRange(r, nextLo, c-1) - } - nextLo = c + 1 - } - } - if nextLo <= unicode.MaxRune { - r = appendRange(r, nextLo, unicode.MaxRune) - } - return r -} - -// negateClass overwrites r and returns r's negation. -// It assumes the class r is already clean. -func negateClass(r []int) []int { - nextLo := 0 // lo end of next class to add - w := 0 // write index - for i := 0; i < len(r); i += 2 { - lo, hi := r[i], r[i+1] - if nextLo <= lo-1 { - r[w] = nextLo - r[w+1] = lo - 1 - w += 2 - } - nextLo = hi + 1 - } - r = r[:w] - if nextLo <= unicode.MaxRune { - // It's possible for the negation to have one more - // range - this one - than the original class, so use append. - r = append(r, nextLo, unicode.MaxRune) - } - return r -} - -// ranges implements sort.Interface on a []rune. -// The choice of receiver type definition is strange -// but avoids an allocation since we already have -// a *[]int. -type ranges struct { - p *[]int -} - -func (ra ranges) Less(i, j int) bool { - p := *ra.p - i *= 2 - j *= 2 - return p[i] < p[j] || p[i] == p[j] && p[i+1] > p[j+1] -} - -func (ra ranges) Len() int { - return len(*ra.p) / 2 -} - -func (ra ranges) Swap(i, j int) { - p := *ra.p - i *= 2 - j *= 2 - p[i], p[i+1], p[j], p[j+1] = p[j], p[j+1], p[i], p[i+1] -} - - -func checkUTF8(s string) os.Error { - for s != "" { - rune, size := utf8.DecodeRuneInString(s) - if rune == utf8.RuneError && size == 1 { - return &Error{Code: ErrInvalidUTF8, Expr: s} - } - s = s[size:] - } - return nil -} - -func nextRune(s string) (c int, t string, err os.Error) { - c, size := utf8.DecodeRuneInString(s) - if c == utf8.RuneError && size == 1 { - return 0, "", &Error{Code: ErrInvalidUTF8, Expr: s} - } - return c, s[size:], nil -} - -func isalnum(c int) bool { - return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' -} - -func unhex(c int) int { - if '0' <= c && c <= '9' { - return c - '0' - } - if 'a' <= c && c <= 'f' { - return c - 'a' + 10 - } - if 'A' <= c && c <= 'F' { - return c - 'A' + 10 - } - return -1 -} diff --git a/src/pkg/exp/regexp/syntax/parse_test.go b/src/pkg/exp/regexp/syntax/parse_test.go deleted file mode 100644 index 779b9afde..000000000 --- a/src/pkg/exp/regexp/syntax/parse_test.go +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2011 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. - -package syntax - -import ( - "bytes" - "fmt" - "testing" - "unicode" -) - -var parseTests = []struct { - Regexp string - Dump string -}{ - // Base cases - {`a`, `lit{a}`}, - {`a.`, `cat{lit{a}dot{}}`}, - {`a.b`, `cat{lit{a}dot{}lit{b}}`}, - {`ab`, `str{ab}`}, - {`a.b.c`, `cat{lit{a}dot{}lit{b}dot{}lit{c}}`}, - {`abc`, `str{abc}`}, - {`a|^`, `alt{lit{a}bol{}}`}, - {`a|b`, `cc{0x61-0x62}`}, - {`(a)`, `cap{lit{a}}`}, - {`(a)|b`, `alt{cap{lit{a}}lit{b}}`}, - {`a*`, `star{lit{a}}`}, - {`a+`, `plus{lit{a}}`}, - {`a?`, `que{lit{a}}`}, - {`a{2}`, `rep{2,2 lit{a}}`}, - {`a{2,3}`, `rep{2,3 lit{a}}`}, - {`a{2,}`, `rep{2,-1 lit{a}}`}, - {`a*?`, `nstar{lit{a}}`}, - {`a+?`, `nplus{lit{a}}`}, - {`a??`, `nque{lit{a}}`}, - {`a{2}?`, `nrep{2,2 lit{a}}`}, - {`a{2,3}?`, `nrep{2,3 lit{a}}`}, - {`a{2,}?`, `nrep{2,-1 lit{a}}`}, - {``, `emp{}`}, - {`|`, `emp{}`}, // alt{emp{}emp{}} but got factored - {`|x|`, `alt{emp{}lit{x}emp{}}`}, - {`.`, `dot{}`}, - {`^`, `bol{}`}, - {`$`, `eol{}`}, - {`\|`, `lit{|}`}, - {`\(`, `lit{(}`}, - {`\)`, `lit{)}`}, - {`\*`, `lit{*}`}, - {`\+`, `lit{+}`}, - {`\?`, `lit{?}`}, - {`{`, `lit{{}`}, - {`}`, `lit{}}`}, - {`\.`, `lit{.}`}, - {`\^`, `lit{^}`}, - {`\$`, `lit{$}`}, - {`\\`, `lit{\}`}, - {`[ace]`, `cc{0x61 0x63 0x65}`}, - {`[abc]`, `cc{0x61-0x63}`}, - {`[a-z]`, `cc{0x61-0x7a}`}, - {`[a]`, `lit{a}`}, - {`\-`, `lit{-}`}, - {`-`, `lit{-}`}, - {`\_`, `lit{_}`}, - {`abc`, `str{abc}`}, - {`abc|def`, `alt{str{abc}str{def}}`}, - {`abc|def|ghi`, `alt{str{abc}str{def}str{ghi}}`}, - - // Posix and Perl extensions - {`[[:lower:]]`, `cc{0x61-0x7a}`}, - {`[a-z]`, `cc{0x61-0x7a}`}, - {`[^[:lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`}, - {`[[:^lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`}, - {`(?i)[[:lower:]]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, - {`(?i)[a-z]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, - {`(?i)[^[:lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, - {`(?i)[[:^lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, - {`\d`, `cc{0x30-0x39}`}, - {`\D`, `cc{0x0-0x2f 0x3a-0x10ffff}`}, - {`\s`, `cc{0x9-0xa 0xc-0xd 0x20}`}, - {`\S`, `cc{0x0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}`}, - {`\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a}`}, - {`\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x10ffff}`}, - {`(?i)\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a 0x17f 0x212a}`}, - {`(?i)\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, - {`[^\\]`, `cc{0x0-0x5b 0x5d-0x10ffff}`}, - // { `\C`, `byte{}` }, // probably never - - // Unicode, negatives, and a double negative. - {`\p{Braille}`, `cc{0x2800-0x28ff}`}, - {`\P{Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`\p{^Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`\P{^Braille}`, `cc{0x2800-0x28ff}`}, - {`\pZ`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`}, - {`[\p{Braille}]`, `cc{0x2800-0x28ff}`}, - {`[\P{Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`[\p{^Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, - {`[\P{^Braille}]`, `cc{0x2800-0x28ff}`}, - {`[\pZ]`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`}, - {`\p{Lu}`, mkCharClass(unicode.IsUpper)}, - {`[\p{Lu}]`, mkCharClass(unicode.IsUpper)}, - {`(?i)[\p{Lu}]`, mkCharClass(isUpperFold)}, - - // Hex, octal. - {`[\012-\234]\141`, `cat{cc{0xa-0x9c}lit{a}}`}, - {`[\x{41}-\x7a]\x61`, `cat{cc{0x41-0x7a}lit{a}}`}, - - // More interesting regular expressions. - {`a{,2}`, `str{a{,2}}`}, - {`\.\^\$\\`, `str{.^$\}`}, - {`[a-zABC]`, `cc{0x41-0x43 0x61-0x7a}`}, - {`[^a]`, `cc{0x0-0x60 0x62-0x10ffff}`}, - {`[α-ε☺]`, `cc{0x3b1-0x3b5 0x263a}`}, // utf-8 - {`a*{`, `cat{star{lit{a}}lit{{}}`}, - - // Test precedences - {`(?:ab)*`, `star{str{ab}}`}, - {`(ab)*`, `star{cap{str{ab}}}`}, - {`ab|cd`, `alt{str{ab}str{cd}}`}, - {`a(b|c)d`, `cat{lit{a}cap{cc{0x62-0x63}}lit{d}}`}, - - // Test flattening. - {`(?:a)`, `lit{a}`}, - {`(?:ab)(?:cd)`, `str{abcd}`}, - {`(?:a+b+)(?:c+d+)`, `cat{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`}, - {`(?:a+|b+)|(?:c+|d+)`, `alt{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`}, - {`(?:a|b)|(?:c|d)`, `cc{0x61-0x64}`}, - {`a|.`, `dot{}`}, - {`.|a`, `dot{}`}, - {`(?:[abc]|A|Z|hello|world)`, `alt{cc{0x41 0x5a 0x61-0x63}str{hello}str{world}}`}, - {`(?:[abc]|A|Z)`, `cc{0x41 0x5a 0x61-0x63}`}, - - // Test Perl quoted literals - {`\Q+|*?{[\E`, `str{+|*?{[}`}, - {`\Q+\E+`, `plus{lit{+}}`}, - {`\Q\\E`, `lit{\}`}, - {`\Q\\\E`, `str{\\}`}, - - // Test Perl \A and \z - {`(?m)^`, `bol{}`}, - {`(?m)$`, `eol{}`}, - {`(?-m)^`, `bot{}`}, - {`(?-m)$`, `eot{}`}, - {`(?m)\A`, `bot{}`}, - {`(?m)\z`, `eot{\z}`}, - {`(?-m)\A`, `bot{}`}, - {`(?-m)\z`, `eot{\z}`}, - - // Test named captures - {`(?P<name>a)`, `cap{name:lit{a}}`}, - - // Case-folded literals - {`[Aa]`, `litfold{A}`}, - {`[\x{100}\x{101}]`, `litfold{Ā}`}, - {`[Δδ]`, `litfold{Δ}`}, - - // Strings - {`abcde`, `str{abcde}`}, - {`[Aa][Bb]cd`, `cat{strfold{AB}str{cd}}`}, - - // Factoring. - {`abc|abd|aef|bcx|bcy`, `alt{cat{lit{a}alt{cat{lit{b}cc{0x63-0x64}}str{ef}}}cat{str{bc}cc{0x78-0x79}}}`}, - {`ax+y|ax+z|ay+w`, `cat{lit{a}alt{cat{plus{lit{x}}cc{0x79-0x7a}}cat{plus{lit{y}}lit{w}}}}`}, -} - -const testFlags = MatchNL | PerlX | UnicodeGroups - -// Test Parse -> Dump. -func TestParseDump(t *testing.T) { - for _, tt := range parseTests { - re, err := Parse(tt.Regexp, testFlags) - if err != nil { - t.Errorf("Parse(%#q): %v", tt.Regexp, err) - continue - } - d := dump(re) - if d != tt.Dump { - t.Errorf("Parse(%#q).Dump() = %#q want %#q", tt.Regexp, d, tt.Dump) - } - } -} - -// dump prints a string representation of the regexp showing -// the structure explicitly. -func dump(re *Regexp) string { - var b bytes.Buffer - dumpRegexp(&b, re) - return b.String() -} - -var opNames = []string{ - OpNoMatch: "no", - OpEmptyMatch: "emp", - OpLiteral: "lit", - OpCharClass: "cc", - OpAnyCharNotNL: "dnl", - OpAnyChar: "dot", - OpBeginLine: "bol", - OpEndLine: "eol", - OpBeginText: "bot", - OpEndText: "eot", - OpWordBoundary: "wb", - OpNoWordBoundary: "nwb", - OpCapture: "cap", - OpStar: "star", - OpPlus: "plus", - OpQuest: "que", - OpRepeat: "rep", - OpConcat: "cat", - OpAlternate: "alt", -} - -// dumpRegexp writes an encoding of the syntax tree for the regexp re to b. -// It is used during testing to distinguish between parses that might print -// the same using re's String method. -func dumpRegexp(b *bytes.Buffer, re *Regexp) { - if int(re.Op) >= len(opNames) || opNames[re.Op] == "" { - fmt.Fprintf(b, "op%d", re.Op) - } else { - switch re.Op { - default: - b.WriteString(opNames[re.Op]) - case OpStar, OpPlus, OpQuest, OpRepeat: - if re.Flags&NonGreedy != 0 { - b.WriteByte('n') - } - b.WriteString(opNames[re.Op]) - case OpLiteral: - if len(re.Rune) > 1 { - b.WriteString("str") - } else { - b.WriteString("lit") - } - if re.Flags&FoldCase != 0 { - for _, r := range re.Rune { - if unicode.SimpleFold(r) != r { - b.WriteString("fold") - break - } - } - } - } - } - b.WriteByte('{') - switch re.Op { - case OpEndText: - if re.Flags&WasDollar == 0 { - b.WriteString(`\z`) - } - case OpLiteral: - for _, r := range re.Rune { - b.WriteRune(r) - } - case OpConcat, OpAlternate: - for _, sub := range re.Sub { - dumpRegexp(b, sub) - } - case OpStar, OpPlus, OpQuest: - dumpRegexp(b, re.Sub[0]) - case OpRepeat: - fmt.Fprintf(b, "%d,%d ", re.Min, re.Max) - dumpRegexp(b, re.Sub[0]) - case OpCapture: - if re.Name != "" { - b.WriteString(re.Name) - b.WriteByte(':') - } - dumpRegexp(b, re.Sub[0]) - case OpCharClass: - sep := "" - for i := 0; i < len(re.Rune); i += 2 { - b.WriteString(sep) - sep = " " - lo, hi := re.Rune[i], re.Rune[i+1] - if lo == hi { - fmt.Fprintf(b, "%#x", lo) - } else { - fmt.Fprintf(b, "%#x-%#x", lo, hi) - } - } - } - b.WriteByte('}') -} - -func mkCharClass(f func(int) bool) string { - re := &Regexp{Op: OpCharClass} - lo := -1 - for i := 0; i <= unicode.MaxRune; i++ { - if f(i) { - if lo < 0 { - lo = i - } - } else { - if lo >= 0 { - re.Rune = append(re.Rune, lo, i-1) - lo = -1 - } - } - } - if lo >= 0 { - re.Rune = append(re.Rune, lo, unicode.MaxRune) - } - return dump(re) -} - -func isUpperFold(rune int) bool { - if unicode.IsUpper(rune) { - return true - } - c := unicode.SimpleFold(rune) - for c != rune { - if unicode.IsUpper(c) { - return true - } - c = unicode.SimpleFold(c) - } - return false -} - -func TestFoldConstants(t *testing.T) { - last := -1 - for i := 0; i <= unicode.MaxRune; i++ { - if unicode.SimpleFold(i) == i { - continue - } - if last == -1 && minFold != i { - t.Errorf("minFold=%#U should be %#U", minFold, i) - } - last = i - } - if maxFold != last { - t.Errorf("maxFold=%#U should be %#U", maxFold, last) - } -} - -func TestAppendRangeCollapse(t *testing.T) { - // AppendRange should collapse each of the new ranges - // into the earlier ones (it looks back two ranges), so that - // the slice never grows very large. - // Note that we are not calling cleanClass. - var r []int - for i := 'A'; i <= 'Z'; i++ { - r = appendRange(r, i, i) - r = appendRange(r, i+'a'-'A', i+'a'-'A') - } - if string(r) != "AZaz" { - t.Errorf("appendRange interlaced A-Z a-z = %s, want AZaz", string(r)) - } -} diff --git a/src/pkg/exp/regexp/syntax/perl_groups.go b/src/pkg/exp/regexp/syntax/perl_groups.go deleted file mode 100644 index 05b392c40..000000000 --- a/src/pkg/exp/regexp/syntax/perl_groups.go +++ /dev/null @@ -1,130 +0,0 @@ -// GENERATED BY make_perl_groups.pl; DO NOT EDIT. -// make_perl_groups.pl >perl_groups.go - -package syntax - -var code1 = []int{ /* \d */ - 0x30, 0x39, -} - -var code2 = []int{ /* \s */ - 0x9, 0xa, - 0xc, 0xd, - 0x20, 0x20, -} - -var code3 = []int{ /* \w */ - 0x30, 0x39, - 0x41, 0x5a, - 0x5f, 0x5f, - 0x61, 0x7a, -} - -var perlGroup = map[string]charGroup{ - `\d`: {+1, code1}, - `\D`: {-1, code1}, - `\s`: {+1, code2}, - `\S`: {-1, code2}, - `\w`: {+1, code3}, - `\W`: {-1, code3}, -} -var code4 = []int{ /* [:alnum:] */ - 0x30, 0x39, - 0x41, 0x5a, - 0x61, 0x7a, -} - -var code5 = []int{ /* [:alpha:] */ - 0x41, 0x5a, - 0x61, 0x7a, -} - -var code6 = []int{ /* [:ascii:] */ - 0x0, 0x7f, -} - -var code7 = []int{ /* [:blank:] */ - 0x9, 0x9, - 0x20, 0x20, -} - -var code8 = []int{ /* [:cntrl:] */ - 0x0, 0x1f, - 0x7f, 0x7f, -} - -var code9 = []int{ /* [:digit:] */ - 0x30, 0x39, -} - -var code10 = []int{ /* [:graph:] */ - 0x21, 0x7e, -} - -var code11 = []int{ /* [:lower:] */ - 0x61, 0x7a, -} - -var code12 = []int{ /* [:print:] */ - 0x20, 0x7e, -} - -var code13 = []int{ /* [:punct:] */ - 0x21, 0x2f, - 0x3a, 0x40, - 0x5b, 0x60, - 0x7b, 0x7e, -} - -var code14 = []int{ /* [:space:] */ - 0x9, 0xd, - 0x20, 0x20, -} - -var code15 = []int{ /* [:upper:] */ - 0x41, 0x5a, -} - -var code16 = []int{ /* [:word:] */ - 0x30, 0x39, - 0x41, 0x5a, - 0x5f, 0x5f, - 0x61, 0x7a, -} - -var code17 = []int{ /* [:xdigit:] */ - 0x30, 0x39, - 0x41, 0x46, - 0x61, 0x66, -} - -var posixGroup = map[string]charGroup{ - `[:alnum:]`: {+1, code4}, - `[:^alnum:]`: {-1, code4}, - `[:alpha:]`: {+1, code5}, - `[:^alpha:]`: {-1, code5}, - `[:ascii:]`: {+1, code6}, - `[:^ascii:]`: {-1, code6}, - `[:blank:]`: {+1, code7}, - `[:^blank:]`: {-1, code7}, - `[:cntrl:]`: {+1, code8}, - `[:^cntrl:]`: {-1, code8}, - `[:digit:]`: {+1, code9}, - `[:^digit:]`: {-1, code9}, - `[:graph:]`: {+1, code10}, - `[:^graph:]`: {-1, code10}, - `[:lower:]`: {+1, code11}, - `[:^lower:]`: {-1, code11}, - `[:print:]`: {+1, code12}, - `[:^print:]`: {-1, code12}, - `[:punct:]`: {+1, code13}, - `[:^punct:]`: {-1, code13}, - `[:space:]`: {+1, code14}, - `[:^space:]`: {-1, code14}, - `[:upper:]`: {+1, code15}, - `[:^upper:]`: {-1, code15}, - `[:word:]`: {+1, code16}, - `[:^word:]`: {-1, code16}, - `[:xdigit:]`: {+1, code17}, - `[:^xdigit:]`: {-1, code17}, -} diff --git a/src/pkg/exp/regexp/syntax/prog.go b/src/pkg/exp/regexp/syntax/prog.go deleted file mode 100644 index 6eeb3da0c..000000000 --- a/src/pkg/exp/regexp/syntax/prog.go +++ /dev/null @@ -1,182 +0,0 @@ -package syntax - -import ( - "bytes" - "strconv" -) - -// Compiled program. -// May not belong in this package, but convenient for now. - -// A Prog is a compiled regular expression program. -type Prog struct { - Inst []Inst - Start int // index of start instruction -} - -// An InstOp is an instruction opcode. -type InstOp uint8 - -const ( - InstAlt InstOp = iota - InstAltMatch - InstCapture - InstEmptyWidth - InstMatch - InstFail - InstNop - InstRune -) - -// An EmptyOp specifies a kind or mixture of zero-width assertions. -type EmptyOp uint8 - -const ( - EmptyBeginLine EmptyOp = 1 << iota - EmptyEndLine - EmptyBeginText - EmptyEndText - EmptyWordBoundary - EmptyNoWordBoundary -) - -// An Inst is a single instruction in a regular expression program. -type Inst struct { - Op InstOp - Out uint32 // all but InstMatch, InstFail - Arg uint32 // InstAlt, InstAltMatch, InstCapture, InstEmptyWidth - Rune []int -} - -func (p *Prog) String() string { - var b bytes.Buffer - dumpProg(&b, p) - return b.String() -} - -// MatchRune returns true if the instruction matches (and consumes) r. -// It should only be called when i.Op == InstRune. -func (i *Inst) MatchRune(r int) bool { - rune := i.Rune - - // Special case: single-rune slice is from literal string, not char class. - // TODO: Case folding. - if len(rune) == 1 { - return r == rune[0] - } - - // Peek at the first few pairs. - // Should handle ASCII well. - for j := 0; j < len(rune) && j <= 8; j += 2 { - if r < rune[j] { - return false - } - if r <= rune[j+1] { - return true - } - } - - // Otherwise binary search. - lo := 0 - hi := len(rune) / 2 - for lo < hi { - m := lo + (hi-lo)/2 - if c := rune[2*m]; c <= r { - if r <= rune[2*m+1] { - return true - } - lo = m + 1 - } else { - hi = m - } - } - return false -} - -// As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char. -// Since we act on runes, it would be easy to support Unicode here. -func wordRune(rune int) bool { - return rune == '_' || - ('A' <= rune && rune <= 'Z') || - ('a' <= rune && rune <= 'z') || - ('0' <= rune && rune <= '9') -} - -// MatchEmptyWidth returns true if the instruction matches -// an empty string between the runes before and after. -// It should only be called when i.Op == InstEmptyWidth. -func (i *Inst) MatchEmptyWidth(before int, after int) bool { - switch EmptyOp(i.Arg) { - case EmptyBeginLine: - return before == '\n' || before == -1 - case EmptyEndLine: - return after == '\n' || after == -1 - case EmptyBeginText: - return before == -1 - case EmptyEndText: - return after == -1 - case EmptyWordBoundary: - return wordRune(before) != wordRune(after) - case EmptyNoWordBoundary: - return wordRune(before) == wordRune(after) - } - panic("unknown empty width arg") -} - - -func (i *Inst) String() string { - var b bytes.Buffer - dumpInst(&b, i) - return b.String() -} - -func bw(b *bytes.Buffer, args ...string) { - for _, s := range args { - b.WriteString(s) - } -} - -func dumpProg(b *bytes.Buffer, p *Prog) { - for j := range p.Inst { - i := &p.Inst[j] - pc := strconv.Itoa(j) - if len(pc) < 3 { - b.WriteString(" "[len(pc):]) - } - if j == p.Start { - pc += "*" - } - bw(b, pc, "\t") - dumpInst(b, i) - bw(b, "\n") - } -} - -func u32(i uint32) string { - return strconv.Uitoa64(uint64(i)) -} - -func dumpInst(b *bytes.Buffer, i *Inst) { - switch i.Op { - case InstAlt: - bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg)) - case InstAltMatch: - bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg)) - case InstCapture: - bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out)) - case InstEmptyWidth: - bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out)) - case InstMatch: - bw(b, "match") - case InstFail: - bw(b, "fail") - case InstNop: - bw(b, "nop -> ", u32(i.Out)) - case InstRune: - if i.Rune == nil { - // shouldn't happen - bw(b, "rune <nil>") - } - bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out)) - } -} diff --git a/src/pkg/exp/regexp/syntax/prog_test.go b/src/pkg/exp/regexp/syntax/prog_test.go deleted file mode 100644 index 7be4281c2..000000000 --- a/src/pkg/exp/regexp/syntax/prog_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package syntax - -import ( - "testing" -) - -var compileTests = []struct { - Regexp string - Prog string -}{ - {"a", ` 0 fail - 1* rune "a" -> 2 - 2 match -`}, - {"[A-M][n-z]", ` 0 fail - 1* rune "AM" -> 2 - 2 rune "nz" -> 3 - 3 match -`}, - {"", ` 0 fail - 1* nop -> 2 - 2 match -`}, - {"a?", ` 0 fail - 1 rune "a" -> 3 - 2* alt -> 1, 3 - 3 match -`}, - {"a??", ` 0 fail - 1 rune "a" -> 3 - 2* alt -> 3, 1 - 3 match -`}, - {"a+", ` 0 fail - 1* rune "a" -> 2 - 2 alt -> 1, 3 - 3 match -`}, - {"a+?", ` 0 fail - 1* rune "a" -> 2 - 2 alt -> 3, 1 - 3 match -`}, - {"a*", ` 0 fail - 1 rune "a" -> 2 - 2* alt -> 1, 3 - 3 match -`}, - {"a*?", ` 0 fail - 1 rune "a" -> 2 - 2* alt -> 3, 1 - 3 match -`}, - {"a+b+", ` 0 fail - 1* rune "a" -> 2 - 2 alt -> 1, 3 - 3 rune "b" -> 4 - 4 alt -> 3, 5 - 5 match -`}, - {"(a+)(b+)", ` 0 fail - 1* cap 2 -> 2 - 2 rune "a" -> 3 - 3 alt -> 2, 4 - 4 cap 3 -> 5 - 5 cap 4 -> 6 - 6 rune "b" -> 7 - 7 alt -> 6, 8 - 8 cap 5 -> 9 - 9 match -`}, - {"a+|b+", ` 0 fail - 1 rune "a" -> 2 - 2 alt -> 1, 6 - 3 rune "b" -> 4 - 4 alt -> 3, 6 - 5* alt -> 1, 3 - 6 match -`}, -} - -func TestCompile(t *testing.T) { - for _, tt := range compileTests { - re, _ := Parse(tt.Regexp, Perl) - p, _ := Compile(re) - s := p.String() - if s != tt.Prog { - t.Errorf("compiled %#q:\n--- have\n%s---\n--- want\n%s---", tt.Regexp, s, tt.Prog) - } - } -} diff --git a/src/pkg/exp/regexp/syntax/regexp.go b/src/pkg/exp/regexp/syntax/regexp.go deleted file mode 100644 index 00a4addef..000000000 --- a/src/pkg/exp/regexp/syntax/regexp.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2011 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. - -// Package syntax parses regular expressions into syntax trees. -// WORK IN PROGRESS. -package syntax - -// Note to implementers: -// In this package, re is always a *Regexp and r is always a rune. - -import ( - "bytes" - "strconv" - "strings" - "unicode" -) - -// A Regexp is a node in a regular expression syntax tree. -type Regexp struct { - Op Op // operator - Flags Flags - Sub []*Regexp // subexpressions, if any - Sub0 [1]*Regexp // storage for short Sub - Rune []int // matched runes, for OpLiteral, OpCharClass - Rune0 [2]int // storage for short Rune - Min, Max int // min, max for OpRepeat - Cap int // capturing index, for OpCapture - Name string // capturing name, for OpCapture -} - -// An Op is a single regular expression operator. -type Op uint8 - -// Operators are listed in precedence order, tightest binding to weakest. -// Character class operators are listed simplest to most complex -// (OpLiteral, OpCharClass, OpAnyCharNotNL, OpAnyChar). - -const ( - OpNoMatch Op = 1 + iota // matches no strings - OpEmptyMatch // matches empty string - OpLiteral // matches Runes sequence - OpCharClass // matches Runes interpreted as range pair list - OpAnyCharNotNL // matches any character - OpAnyChar // matches any character - OpBeginLine // matches empty string at beginning of line - OpEndLine // matches empty string at end of line - OpBeginText // matches empty string at beginning of text - OpEndText // matches empty string at end of text - OpWordBoundary // matches word boundary `\b` - OpNoWordBoundary // matches word non-boundary `\B` - OpCapture // capturing subexpression with index Cap, optional name Name - OpStar // matches Sub[0] zero or more times - OpPlus // matches Sub[0] one or more times - OpQuest // matches Sub[0] zero or one times - OpRepeat // matches Sub[0] at least Min times, at most Max (Max == -1 is no limit) - OpConcat // matches concatenation of Subs - OpAlternate // matches alternation of Subs -) - -const opPseudo Op = 128 // where pseudo-ops start - -// Equal returns true if x and y have identical structure. -func (x *Regexp) Equal(y *Regexp) bool { - if x == nil || y == nil { - return x == y - } - if x.Op != y.Op { - return false - } - switch x.Op { - case OpEndText: - // The parse flags remember whether this is \z or \Z. - if x.Flags&WasDollar != y.Flags&WasDollar { - return false - } - - case OpLiteral, OpCharClass: - if len(x.Rune) != len(y.Rune) { - return false - } - for i, r := range x.Rune { - if r != y.Rune[i] { - return false - } - } - - case OpAlternate, OpConcat: - if len(x.Sub) != len(y.Sub) { - return false - } - for i, sub := range x.Sub { - if !sub.Equal(y.Sub[i]) { - return false - } - } - - case OpStar, OpPlus, OpQuest: - if x.Flags&NonGreedy != y.Flags&NonGreedy || !x.Sub[0].Equal(y.Sub[0]) { - return false - } - - case OpRepeat: - if x.Flags&NonGreedy != y.Flags&NonGreedy || x.Min != y.Min || x.Max != y.Max || !x.Sub[0].Equal(y.Sub[0]) { - return false - } - - case OpCapture: - if x.Cap != y.Cap || x.Name != y.Name || !x.Sub[0].Equal(y.Sub[0]) { - return false - } - } - return true -} - -// writeRegexp writes the Perl syntax for the regular expression re to b. -func writeRegexp(b *bytes.Buffer, re *Regexp) { - switch re.Op { - default: - b.WriteString("<invalid op" + strconv.Itoa(int(re.Op)) + ">") - case OpNoMatch: - b.WriteString(`[^\x00-\x{10FFFF}]`) - case OpEmptyMatch: - b.WriteString(`(?:)`) - case OpLiteral: - if re.Flags&FoldCase != 0 { - b.WriteString(`(?i:`) - } - for _, r := range re.Rune { - escape(b, r, false) - } - if re.Flags&FoldCase != 0 { - b.WriteString(`)`) - } - case OpCharClass: - if len(re.Rune)%2 != 0 { - b.WriteString(`[invalid char class]`) - break - } - b.WriteRune('[') - if len(re.Rune) == 0 { - b.WriteString(`^\x00-\x{10FFFF}`) - } else if re.Rune[0] == 0 && re.Rune[len(re.Rune)-1] == unicode.MaxRune { - // Contains 0 and MaxRune. Probably a negated class. - // Print the gaps. - b.WriteRune('^') - for i := 1; i < len(re.Rune)-1; i += 2 { - lo, hi := re.Rune[i]+1, re.Rune[i+1]-1 - escape(b, lo, lo == '-') - if lo != hi { - b.WriteRune('-') - escape(b, hi, hi == '-') - } - } - } else { - for i := 0; i < len(re.Rune); i += 2 { - lo, hi := re.Rune[i], re.Rune[i+1] - escape(b, lo, lo == '-') - if lo != hi { - b.WriteRune('-') - escape(b, hi, hi == '-') - } - } - } - b.WriteRune(']') - case OpAnyCharNotNL: - b.WriteString(`[^\n]`) - case OpAnyChar: - b.WriteRune('.') - case OpBeginLine: - b.WriteRune('^') - case OpEndLine: - b.WriteRune('$') - case OpBeginText: - b.WriteString(`\A`) - case OpEndText: - b.WriteString(`\z`) - case OpWordBoundary: - b.WriteString(`\b`) - case OpNoWordBoundary: - b.WriteString(`\B`) - case OpCapture: - if re.Name != "" { - b.WriteString(`(?P<`) - b.WriteString(re.Name) - b.WriteRune('>') - } else { - b.WriteRune('(') - } - if re.Sub[0].Op != OpEmptyMatch { - writeRegexp(b, re.Sub[0]) - } - b.WriteRune(')') - case OpStar, OpPlus, OpQuest, OpRepeat: - if sub := re.Sub[0]; sub.Op > OpCapture { - b.WriteString(`(?:`) - writeRegexp(b, sub) - b.WriteString(`)`) - } else { - writeRegexp(b, sub) - } - switch re.Op { - case OpStar: - b.WriteRune('*') - case OpPlus: - b.WriteRune('+') - case OpQuest: - b.WriteRune('?') - case OpRepeat: - b.WriteRune('{') - b.WriteString(strconv.Itoa(re.Min)) - if re.Max != re.Min { - b.WriteRune(',') - if re.Max >= 0 { - b.WriteString(strconv.Itoa(re.Max)) - } - } - b.WriteRune('}') - } - case OpConcat: - for _, sub := range re.Sub { - if sub.Op == OpAlternate { - b.WriteString(`(?:`) - writeRegexp(b, sub) - b.WriteString(`)`) - } else { - writeRegexp(b, sub) - } - } - case OpAlternate: - for i, sub := range re.Sub { - if i > 0 { - b.WriteRune('|') - } - writeRegexp(b, sub) - } - } -} - -func (re *Regexp) String() string { - var b bytes.Buffer - writeRegexp(&b, re) - return b.String() -} - -const meta = `\.+*?()|[]{}^$` - -func escape(b *bytes.Buffer, r int, force bool) { - if unicode.IsPrint(r) { - if strings.IndexRune(meta, r) >= 0 || force { - b.WriteRune('\\') - } - b.WriteRune(r) - return - } - - switch r { - case '\a': - b.WriteString(`\a`) - case '\f': - b.WriteString(`\f`) - case '\n': - b.WriteString(`\n`) - case '\r': - b.WriteString(`\r`) - case '\t': - b.WriteString(`\t`) - case '\v': - b.WriteString(`\v`) - default: - if r < 0x100 { - b.WriteString(`\x`) - s := strconv.Itob(r, 16) - if len(s) == 1 { - b.WriteRune('0') - } - b.WriteString(s) - break - } - b.WriteString(`\x{`) - b.WriteString(strconv.Itob(r, 16)) - b.WriteString(`}`) - } -} diff --git a/src/pkg/exp/regexp/syntax/simplify.go b/src/pkg/exp/regexp/syntax/simplify.go deleted file mode 100644 index 72390417b..000000000 --- a/src/pkg/exp/regexp/syntax/simplify.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2011 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. - -package syntax - -// Simplify returns a regexp equivalent to re but without counted repetitions -// and with various other simplifications, such as rewriting /(?:a+)+/ to /a+/. -// The resulting regexp will execute correctly but its string representation -// will not produce the same parse tree, because capturing parentheses -// may have been duplicated or removed. For example, the simplified form -// for /(x){1,2}/ is /(x)(x)?/ but both parentheses capture as $1. -// The returned regexp may share structure with or be the original. -func (re *Regexp) Simplify() *Regexp { - if re == nil { - return nil - } - switch re.Op { - case OpCapture, OpConcat, OpAlternate: - // Simplify children, building new Regexp if children change. - nre := re - for i, sub := range re.Sub { - nsub := sub.Simplify() - if nre == re && nsub != sub { - // Start a copy. - nre = new(Regexp) - *nre = *re - nre.Rune = nil - nre.Sub = append(nre.Sub0[:0], re.Sub[:i]...) - } - if nre != re { - nre.Sub = append(nre.Sub, nsub) - } - } - return nre - - case OpStar, OpPlus, OpQuest: - sub := re.Sub[0].Simplify() - return simplify1(re.Op, re.Flags, sub, re) - - case OpRepeat: - // Special special case: x{0} matches the empty string - // and doesn't even need to consider x. - if re.Min == 0 && re.Max == 0 { - return &Regexp{Op: OpEmptyMatch} - } - - // The fun begins. - sub := re.Sub[0].Simplify() - - // x{n,} means at least n matches of x. - if re.Max == -1 { - // Special case: x{0,} is x*. - if re.Min == 0 { - return simplify1(OpStar, re.Flags, sub, nil) - } - - // Special case: x{1,} is x+. - if re.Min == 1 { - return simplify1(OpPlus, re.Flags, sub, nil) - } - - // General case: x{4,} is xxxx+. - nre := &Regexp{Op: OpConcat} - nre.Sub = nre.Sub0[:0] - for i := 0; i < re.Min-1; i++ { - nre.Sub = append(nre.Sub, sub) - } - nre.Sub = append(nre.Sub, simplify1(OpPlus, re.Flags, sub, nil)) - return nre - } - - // Special case x{0} handled above. - - // Special case: x{1} is just x. - if re.Min == 1 && re.Max == 1 { - return sub - } - - // General case: x{n,m} means n copies of x and m copies of x? - // The machine will do less work if we nest the final m copies, - // so that x{2,5} = xx(x(x(x)?)?)? - - // Build leading prefix: xx. - var prefix *Regexp - if re.Min > 0 { - prefix = &Regexp{Op: OpConcat} - prefix.Sub = prefix.Sub0[:0] - for i := 0; i < re.Min; i++ { - prefix.Sub = append(prefix.Sub, sub) - } - } - - // Build and attach suffix: (x(x(x)?)?)? - if re.Max > re.Min { - suffix := simplify1(OpQuest, re.Flags, sub, nil) - for i := re.Min + 1; i < re.Max; i++ { - nre2 := &Regexp{Op: OpConcat} - nre2.Sub = append(nre2.Sub0[:0], sub, suffix) - suffix = simplify1(OpQuest, re.Flags, nre2, nil) - } - if prefix == nil { - return suffix - } - prefix.Sub = append(prefix.Sub, suffix) - } - if prefix != nil { - return prefix - } - - // Some degenerate case like min > max or min < max < 0. - // Handle as impossible match. - return &Regexp{Op: OpNoMatch} - } - - return re -} - -// simplify1 implements Simplify for the unary OpStar, -// OpPlus, and OpQuest operators. It returns the simple regexp -// equivalent to -// -// Regexp{Op: op, Flags: flags, Sub: {sub}} -// -// under the assumption that sub is already simple, and -// without first allocating that structure. If the regexp -// to be returned turns out to be equivalent to re, simplify1 -// returns re instead. -// -// simplify1 is factored out of Simplify because the implementation -// for other operators generates these unary expressions. -// Letting them call simplify1 makes sure the expressions they -// generate are simple. -func simplify1(op Op, flags Flags, sub, re *Regexp) *Regexp { - // Special case: repeat the empty string as much as - // you want, but it's still the empty string. - if sub.Op == OpEmptyMatch { - return sub - } - // The operators are idempotent if the flags match. - if op == sub.Op && flags&NonGreedy == sub.Flags&NonGreedy { - return sub - } - if re != nil && re.Op == op && re.Flags&NonGreedy == flags&NonGreedy && sub == re.Sub[0] { - return re - } - - re = &Regexp{Op: op, Flags: flags} - re.Sub = append(re.Sub0[:0], sub) - return re -} diff --git a/src/pkg/exp/regexp/syntax/simplify_test.go b/src/pkg/exp/regexp/syntax/simplify_test.go deleted file mode 100644 index c8cec2183..000000000 --- a/src/pkg/exp/regexp/syntax/simplify_test.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2011 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. - -package syntax - -import "testing" - -var simplifyTests = []struct { - Regexp string - Simple string -}{ - // Already-simple constructs - {`a`, `a`}, - {`ab`, `ab`}, - {`a|b`, `[a-b]`}, - {`ab|cd`, `ab|cd`}, - {`(ab)*`, `(ab)*`}, - {`(ab)+`, `(ab)+`}, - {`(ab)?`, `(ab)?`}, - {`.`, `.`}, - {`^`, `^`}, - {`$`, `$`}, - {`[ac]`, `[ac]`}, - {`[^ac]`, `[^ac]`}, - - // Posix character classes - {`[[:alnum:]]`, `[0-9A-Za-z]`}, - {`[[:alpha:]]`, `[A-Za-z]`}, - {`[[:blank:]]`, `[\t ]`}, - {`[[:cntrl:]]`, `[\x00-\x1f\x7f]`}, - {`[[:digit:]]`, `[0-9]`}, - {`[[:graph:]]`, `[!-~]`}, - {`[[:lower:]]`, `[a-z]`}, - {`[[:print:]]`, `[ -~]`}, - {`[[:punct:]]`, "[!-/:-@\\[-`\\{-~]"}, - {`[[:space:]]`, `[\t-\r ]`}, - {`[[:upper:]]`, `[A-Z]`}, - {`[[:xdigit:]]`, `[0-9A-Fa-f]`}, - - // Perl character classes - {`\d`, `[0-9]`}, - {`\s`, `[\t-\n\f-\r ]`}, - {`\w`, `[0-9A-Z_a-z]`}, - {`\D`, `[^0-9]`}, - {`\S`, `[^\t-\n\f-\r ]`}, - {`\W`, `[^0-9A-Z_a-z]`}, - {`[\d]`, `[0-9]`}, - {`[\s]`, `[\t-\n\f-\r ]`}, - {`[\w]`, `[0-9A-Z_a-z]`}, - {`[\D]`, `[^0-9]`}, - {`[\S]`, `[^\t-\n\f-\r ]`}, - {`[\W]`, `[^0-9A-Z_a-z]`}, - - // Posix repetitions - {`a{1}`, `a`}, - {`a{2}`, `aa`}, - {`a{5}`, `aaaaa`}, - {`a{0,1}`, `a?`}, - // The next three are illegible because Simplify inserts (?:) - // parens instead of () parens to avoid creating extra - // captured subexpressions. The comments show a version with fewer parens. - {`(a){0,2}`, `(?:(a)(a)?)?`}, // (aa?)? - {`(a){0,4}`, `(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // (a(a(aa?)?)?)? - {`(a){2,6}`, `(a)(a)(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // aa(a(a(aa?)?)?)? - {`a{0,2}`, `(?:aa?)?`}, // (aa?)? - {`a{0,4}`, `(?:a(?:a(?:aa?)?)?)?`}, // (a(a(aa?)?)?)? - {`a{2,6}`, `aa(?:a(?:a(?:aa?)?)?)?`}, // aa(a(a(aa?)?)?)? - {`a{0,}`, `a*`}, - {`a{1,}`, `a+`}, - {`a{2,}`, `aa+`}, - {`a{5,}`, `aaaaa+`}, - - // Test that operators simplify their arguments. - {`(?:a{1,}){1,}`, `a+`}, - {`(a{1,}b{1,})`, `(a+b+)`}, - {`a{1,}|b{1,}`, `a+|b+`}, - {`(?:a{1,})*`, `(?:a+)*`}, - {`(?:a{1,})+`, `a+`}, - {`(?:a{1,})?`, `(?:a+)?`}, - {``, `(?:)`}, - {`a{0}`, `(?:)`}, - - // Character class simplification - {`[ab]`, `[a-b]`}, - {`[a-za-za-z]`, `[a-z]`}, - {`[A-Za-zA-Za-z]`, `[A-Za-z]`}, - {`[ABCDEFGH]`, `[A-H]`}, - {`[AB-CD-EF-GH]`, `[A-H]`}, - {`[W-ZP-XE-R]`, `[E-Z]`}, - {`[a-ee-gg-m]`, `[a-m]`}, - {`[a-ea-ha-m]`, `[a-m]`}, - {`[a-ma-ha-e]`, `[a-m]`}, - {`[a-zA-Z0-9 -~]`, `[ -~]`}, - - // Empty character classes - {`[^[:cntrl:][:^cntrl:]]`, `[^\x00-\x{10FFFF}]`}, - - // Full character classes - {`[[:cntrl:][:^cntrl:]]`, `.`}, - - // Unicode case folding. - {`(?i)A`, `(?i:A)`}, - {`(?i)a`, `(?i:a)`}, - {`(?i)[A]`, `(?i:A)`}, - {`(?i)[a]`, `(?i:A)`}, - {`(?i)K`, `(?i:K)`}, - {`(?i)k`, `(?i:k)`}, - {`(?i)\x{212a}`, "(?i:\u212A)"}, - {`(?i)[K]`, "[Kk\u212A]"}, - {`(?i)[k]`, "[Kk\u212A]"}, - {`(?i)[\x{212a}]`, "[Kk\u212A]"}, - {`(?i)[a-z]`, "[A-Za-z\u017F\u212A]"}, - {`(?i)[\x00-\x{FFFD}]`, "[\\x00-\uFFFD]"}, - {`(?i)[\x00-\x{10FFFF}]`, `.`}, - - // Empty string as a regular expression. - // The empty string must be preserved inside parens in order - // to make submatches work right, so these tests are less - // interesting than they might otherwise be. String inserts - // explicit (?:) in place of non-parenthesized empty strings, - // to make them easier to spot for other parsers. - {`(a|b|)`, `([a-b]|(?:))`}, - {`(|)`, `()`}, - {`a()`, `a()`}, - {`(()|())`, `(()|())`}, - {`(a|)`, `(a|(?:))`}, - {`ab()cd()`, `ab()cd()`}, - {`()`, `()`}, - {`()*`, `()*`}, - {`()+`, `()+`}, - {`()?`, `()?`}, - {`(){0}`, `(?:)`}, - {`(){1}`, `()`}, - {`(){1,}`, `()+`}, - {`(){0,2}`, `(?:()()?)?`}, -} - -func TestSimplify(t *testing.T) { - for _, tt := range simplifyTests { - re, err := Parse(tt.Regexp, MatchNL|Perl&^OneLine) - if err != nil { - t.Errorf("Parse(%#q) = error %v", tt.Regexp, err) - continue - } - s := re.Simplify().String() - if s != tt.Simple { - t.Errorf("Simplify(%#q) = %#q, want %#q", tt.Regexp, s, tt.Simple) - } - } -} diff --git a/src/pkg/exp/template/Makefile b/src/pkg/exp/template/Makefile deleted file mode 100644 index 8550b0d52..000000000 --- a/src/pkg/exp/template/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2011 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. - -include ../../../Make.inc - -TARG=exp/template -GOFILES=\ - exec.go\ - funcs.go\ - lex.go\ - parse.go\ - set.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go deleted file mode 100644 index fb0a9e621..000000000 --- a/src/pkg/exp/template/exec.go +++ /dev/null @@ -1,508 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "fmt" - "io" - "os" - "reflect" - "strings" - "unicode" - "utf8" -) - -// state represents the state of an execution. It's not part of the -// template so that multiple executions of the same template -// can execute in parallel. -type state struct { - tmpl *Template - wr io.Writer - set *Set - line int // line number for errors -} - -// errorf formats the error and terminates processing. -func (s *state) errorf(format string, args ...interface{}) { - format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.name, s.line, format) - panic(fmt.Errorf(format, args...)) -} - -// error terminates processing. -func (s *state) error(err os.Error) { - s.errorf("%s", err) -} - -// Execute applies a parsed template to the specified data object, -// writing the output to wr. -func (t *Template) Execute(wr io.Writer, data interface{}) os.Error { - return t.ExecuteInSet(wr, data, nil) -} - -// ExecuteInSet applies a parsed template to the specified data object, -// writing the output to wr. Nested template invocations will be resolved -// from the specified set. -func (t *Template) ExecuteInSet(wr io.Writer, data interface{}, set *Set) (err os.Error) { - defer t.recover(&err) - state := &state{ - tmpl: t, - wr: wr, - set: set, - line: 1, - } - if t.root == nil { - state.errorf("must be parsed before execution") - } - state.walk(reflect.ValueOf(data), t.root) - return -} - -// Walk functions step through the major pieces of the template structure, -// generating output as they go. -func (s *state) walk(data reflect.Value, n node) { - switch n := n.(type) { - case *actionNode: - s.line = n.line - s.printValue(n, s.evalPipeline(data, n.pipeline)) - case *listNode: - for _, node := range n.nodes { - s.walk(data, node) - } - case *ifNode: - s.line = n.line - s.walkIfOrWith(nodeIf, data, n.pipeline, n.list, n.elseList) - case *rangeNode: - s.line = n.line - s.walkRange(data, n) - case *textNode: - if _, err := s.wr.Write(n.text); err != nil { - s.error(err) - } - case *templateNode: - s.line = n.line - s.walkTemplate(data, n) - case *withNode: - s.line = n.line - s.walkIfOrWith(nodeWith, data, n.pipeline, n.list, n.elseList) - default: - s.errorf("unknown node: %s", n) - } -} - -// walkIfOrWith walks an 'if' or 'with' node. The two control structures -// are identical in behavior except that 'with' sets dot. -func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe []*commandNode, list, elseList *listNode) { - val := s.evalPipeline(data, pipe) - truth, ok := isTrue(val) - if !ok { - s.errorf("if/with can't use value of type %T", val.Interface()) - } - if truth { - if typ == nodeWith { - data = val - } - s.walk(data, list) - } else if elseList != nil { - s.walk(data, elseList) - } -} - -// isTrue returns whether the value is 'true', in the sense of not the zero of its type, -// and whether the value has a meaningful truth value. -func isTrue(val reflect.Value) (truth, ok bool) { - switch val.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - truth = val.Len() > 0 - case reflect.Bool: - truth = val.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - truth = val.Int() != 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - truth = val.Uint() != 0 - case reflect.Float32, reflect.Float64: - truth = val.Float() != 0 - case reflect.Complex64, reflect.Complex128: - truth = val.Complex() != 0 - case reflect.Chan, reflect.Func, reflect.Ptr: - truth = !val.IsNil() - default: - return - } - return truth, true -} - -func (s *state) walkRange(data reflect.Value, r *rangeNode) { - val := s.evalPipeline(data, r.pipeline) - switch val.Kind() { - case reflect.Array, reflect.Slice: - if val.Len() == 0 { - break - } - for i := 0; i < val.Len(); i++ { - s.walk(val.Index(i), r.list) - } - return - case reflect.Map: - if val.Len() == 0 { - break - } - for _, key := range val.MapKeys() { - s.walk(val.MapIndex(key), r.list) - } - return - default: - s.errorf("range can't iterate over value of type %T", val.Interface()) - } - if r.elseList != nil { - s.walk(data, r.elseList) - } -} - -func (s *state) walkTemplate(data reflect.Value, t *templateNode) { - name := s.evalArg(data, reflect.TypeOf("string"), t.name).String() - if s.set == nil { - s.errorf("no set defined in which to invoke template named %q", name) - } - tmpl := s.set.tmpl[name] - if tmpl == nil { - s.errorf("template %q not in set", name) - } - data = s.evalPipeline(data, t.pipeline) - newState := *s - newState.tmpl = tmpl - newState.walk(data, tmpl.root) -} - -// Eval functions evaluate pipelines, commands, and their elements and extract -// values from the data structure by examining fields, calling methods, and so on. -// The printing of those values happens only through walk functions. - -func (s *state) evalPipeline(data reflect.Value, pipe []*commandNode) reflect.Value { - value := reflect.Value{} - for _, cmd := range pipe { - value = s.evalCommand(data, cmd, value) // previous value is this one's final arg. - // If the object has type interface{}, dig down one level to the thing inside. - if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 { - value = reflect.ValueOf(value.Interface()) // lovely! - } - } - return value -} - -func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.Value) reflect.Value { - firstWord := cmd.args[0] - switch n := firstWord.(type) { - case *fieldNode: - return s.evalFieldNode(data, n, cmd.args, final) - case *identifierNode: - return s.evalFieldOrCall(data, n.ident, cmd.args, final) - } - if len(cmd.args) > 1 || final.IsValid() { - s.errorf("can't give argument to non-function %s", cmd.args[0]) - } - switch word := cmd.args[0].(type) { - case *dotNode: - return data - case *boolNode: - return reflect.ValueOf(word.true) - case *numberNode: - // These are ideal constants but we don't know the type - // and we have no context. (If it was a method argument, - // we'd know what we need.) The syntax guides us to some extent. - switch { - case word.isComplex: - return reflect.ValueOf(word.complex128) // incontrovertible. - case word.isFloat && strings.IndexAny(word.text, ".eE") >= 0: - return reflect.ValueOf(word.float64) - case word.isInt: - return reflect.ValueOf(word.int64) - case word.isUint: - return reflect.ValueOf(word.uint64) - } - case *stringNode: - return reflect.ValueOf(word.text) - } - s.errorf("can't handle command %q", firstWord) - panic("not reached") -} - -func (s *state) evalFieldNode(data reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value { - // Up to the last entry, it must be a field. - n := len(field.ident) - for i := 0; i < n-1; i++ { - data = s.evalField(data, field.ident[i]) - } - // Now it can be a field or method and if a method, gets arguments. - return s.evalFieldOrCall(data, field.ident[n-1], args, final) -} - -// Is this an exported - upper case - name? -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -func (s *state) evalField(data reflect.Value, fieldName string) reflect.Value { - var isNil bool - if data, isNil = indirect(data); isNil { - s.errorf("%s is nil pointer", fieldName) - } - switch data.Kind() { - case reflect.Struct: - // Is it a field? - field := data.FieldByName(fieldName) - // TODO: look higher up the tree if we can't find it here. Also unexported fields - // might succeed higher up, as map keys. - if field.IsValid() && isExported(fieldName) { // valid and exported - return field - } - s.errorf("%s has no exported field %q", data.Type(), fieldName) - default: - s.errorf("can't evaluate field %s of type %s", fieldName, data.Type()) - } - panic("not reached") -} - -func (s *state) evalFieldOrCall(data reflect.Value, fieldName string, args []node, final reflect.Value) reflect.Value { - // Is it a function? - if function, ok := findFunction(fieldName, s.tmpl, s.set); ok { - return s.evalCall(data, function, fieldName, false, args, final) - } - ptr := data - for data.Kind() == reflect.Ptr && !data.IsNil() { - ptr, data = data, reflect.Indirect(data) - } - // Is it a method? We use the pointer because it has value methods too. - if method, ok := methodByName(ptr.Type(), fieldName); ok { - return s.evalCall(ptr, method.Func, fieldName, true, args, final) - } - if len(args) > 1 || final.IsValid() { - s.errorf("%s is not a method but has arguments", fieldName) - } - switch data.Kind() { - case reflect.Struct: - return s.evalField(data, fieldName) - default: - s.errorf("can't handle evaluation of field %s of type %s", fieldName, data.Type()) - } - panic("not reached") -} - -// TODO: delete when reflect's own MethodByName is released. -func methodByName(typ reflect.Type, name string) (reflect.Method, bool) { - for i := 0; i < typ.NumMethod(); i++ { - if typ.Method(i).Name == name { - return typ.Method(i), true - } - } - return reflect.Method{}, false -} - -var ( - osErrorType = reflect.TypeOf(new(os.Error)).Elem() -) - -func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args []node, final reflect.Value) reflect.Value { - typ := fun.Type() - if !isMethod && len(args) > 0 { // Args will be nil if it's a niladic call in an argument list - args = args[1:] // first arg is name of function; not used in call. - } - numIn := len(args) - if final.IsValid() { - numIn++ - } - numFixed := len(args) - if typ.IsVariadic() { - numFixed = typ.NumIn() - 1 // last arg is the variadic one. - if numIn < numFixed { - s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) - } - } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() { - s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) - } - if !goodFunc(typ) { - s.errorf("can't handle multiple results from method/function %q", name) - } - // Build the arg list. - argv := make([]reflect.Value, numIn) - // First arg is the receiver. - i := 0 - if isMethod { - argv[0] = v - i++ - } - // Others must be evaluated. Fixed args first. - for ; i < numFixed; i++ { - argv[i] = s.evalArg(v, typ.In(i), args[i]) - } - // And now the ... args. - if typ.IsVariadic() { - argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. - for ; i < len(args); i++ { - argv[i] = s.evalArg(v, argType, args[i]) - } - } - // Add final value if necessary. - if final.IsValid() { - argv[len(args)] = final - } - result := fun.Call(argv) - // If we have an os.Error that is not nil, stop execution and return that error to the caller. - if len(result) == 2 && !result[1].IsNil() { - s.error(result[1].Interface().(os.Error)) - } - return result[0] -} - -func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value { - if field, ok := n.(*fieldNode); ok { - value := s.evalFieldNode(data, field, []node{n}, reflect.Value{}) - if !value.Type().AssignableTo(typ) { - s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) - } - return value - } - switch typ.Kind() { - case reflect.Bool: - return s.evalBool(typ, n) - case reflect.String: - return s.evalString(typ, n) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return s.evalInteger(typ, n) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return s.evalUnsignedInteger(typ, n) - case reflect.Float32, reflect.Float64: - return s.evalFloat(typ, n) - case reflect.Complex64, reflect.Complex128: - return s.evalComplex(typ, n) - case reflect.Interface: - if typ.NumMethod() == 0 { - return s.evalEmptyInterface(data, typ, n) - } - } - s.errorf("can't handle %s for arg of type %s", n, typ) - panic("not reached") -} - -func (s *state) evalBool(typ reflect.Type, n node) reflect.Value { - if n, ok := n.(*boolNode); ok { - value := reflect.New(typ).Elem() - value.SetBool(n.true) - return value - } - s.errorf("expected bool; found %s", n) - panic("not reached") -} - -func (s *state) evalString(typ reflect.Type, n node) reflect.Value { - if n, ok := n.(*stringNode); ok { - value := reflect.New(typ).Elem() - value.SetString(n.text) - return value - } - s.errorf("expected string; found %s", n) - panic("not reached") -} - -func (s *state) evalInteger(typ reflect.Type, n node) reflect.Value { - if n, ok := n.(*numberNode); ok && n.isInt { - value := reflect.New(typ).Elem() - value.SetInt(n.int64) - return value - } - s.errorf("expected integer; found %s", n) - panic("not reached") -} - -func (s *state) evalUnsignedInteger(typ reflect.Type, n node) reflect.Value { - if n, ok := n.(*numberNode); ok && n.isUint { - value := reflect.New(typ).Elem() - value.SetUint(n.uint64) - return value - } - s.errorf("expected unsigned integer; found %s", n) - panic("not reached") -} - -func (s *state) evalFloat(typ reflect.Type, n node) reflect.Value { - if n, ok := n.(*numberNode); ok && n.isFloat { - value := reflect.New(typ).Elem() - value.SetFloat(n.float64) - return value - } - s.errorf("expected float; found %s", n) - panic("not reached") -} - -func (s *state) evalComplex(typ reflect.Type, n node) reflect.Value { - if n, ok := n.(*numberNode); ok && n.isComplex { - value := reflect.New(typ).Elem() - value.SetComplex(n.complex128) - return value - } - s.errorf("expected complex; found %s", n) - panic("not reached") -} - -func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node) reflect.Value { - switch n := n.(type) { - case *boolNode: - return reflect.ValueOf(n.true) - case *dotNode: - return data - case *fieldNode: - return s.evalFieldNode(data, n, nil, reflect.Value{}) - case *identifierNode: - return s.evalFieldOrCall(data, n.ident, nil, reflect.Value{}) - case *numberNode: - if n.isComplex { - return reflect.ValueOf(n.complex128) - } - if n.isInt { - return reflect.ValueOf(n.int64) - } - if n.isUint { - return reflect.ValueOf(n.uint64) - } - if n.isFloat { - return reflect.ValueOf(n.float64) - } - case *stringNode: - return reflect.ValueOf(n.text) - } - s.errorf("can't handle assignment of %s to empty interface argument", n) - panic("not reached") -} - -// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. -func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { - for v.Kind() == reflect.Ptr { - if v.IsNil() { - return v, true - } - v = v.Elem() - } - return v, false -} - -// printValue writes the textual representation of the value to the output of -// the template. -func (s *state) printValue(n node, v reflect.Value) { - if !v.IsValid() { - fmt.Fprint(s.wr, "<no value>") - return - } - switch v.Kind() { - case reflect.Ptr: - var isNil bool - if v, isNil = indirect(v); isNil { - fmt.Fprint(s.wr, "<nil>") - return - } - case reflect.Chan, reflect.Func, reflect.Interface: - s.errorf("can't print %s of type %s", n, v.Type()) - } - fmt.Fprint(s.wr, v.Interface()) -} diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go deleted file mode 100644 index 86b958e84..000000000 --- a/src/pkg/exp/template/exec_test.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "bytes" - "fmt" - "os" - "sort" - "strings" - "testing" -) - -// T has lots of interesting pieces to use to test execution. -type T struct { - // Basics - I int - U16 uint16 - X string - FloatZero float64 - ComplexZero float64 - // Nested structs. - U *U - // Slices - SI []int - SIEmpty []int - SB []bool - // Maps - MSI map[string]int - MSIone map[string]int // one element, for deterministic output - MSIEmpty map[string]int - SMSI []map[string]int - // Empty interfaces; used to see if we can dig inside one. - Empty0 interface{} // nil - Empty1 interface{} - Empty2 interface{} - Empty3 interface{} - Empty4 interface{} - // Pointers - PI *int - PSI *[]int - NIL *int -} - -var tVal = &T{ - I: 17, - U16: 16, - X: "x", - U: &U{"v"}, - SI: []int{3, 4, 5}, - SB: []bool{true, false}, - MSI: map[string]int{"one": 1, "two": 2, "three": 3}, - MSIone: map[string]int{"one": 1}, - SMSI: []map[string]int{ - {"one": 1, "two": 2}, - {"eleven": 11, "twelve": 12}, - }, - Empty1: 3, - Empty2: "empty2", - Empty3: []int{7, 8}, - Empty4: &U{"v"}, - PI: newInt(23), - PSI: newIntSlice(21, 22, 23), -} - -// Helpers for creation. -func newInt(n int) *int { - p := new(int) - *p = n - return p -} - -func newIntSlice(n ...int) *[]int { - p := new([]int) - *p = make([]int, len(n)) - copy(*p, n) - return p -} - -// Simple methods with and without arguments. -func (t *T) Method0() string { - return "resultOfMethod0" -} - -func (t *T) Method1(a int) int { - return a -} - -func (t *T) Method2(a uint16, b string) string { - return fmt.Sprintf("Method2: %d %s", a, b) -} - -func (t *T) MAdd(a int, b []int) []int { - v := make([]int, len(b)) - for i, x := range b { - v[i] = x + a - } - return v -} - -// MSort is used to sort map keys for stable output. (Nice trick!) -func (t *T) MSort(m map[string]int) []string { - keys := make([]string, len(m)) - i := 0 - for k := range m { - keys[i] = k - i++ - } - sort.Strings(keys) - return keys -} - -// EPERM returns a value and an os.Error according to its argument. -func (t *T) EPERM(error bool) (bool, os.Error) { - if error { - return true, os.EPERM - } - return false, nil -} - -type U struct { - V string -} - -type execTest struct { - name string - input string - output string - data interface{} - ok bool -} - -var execTests = []execTest{ - // Trivial cases. - {"empty", "", "", nil, true}, - {"text", "some text", "some text", nil, true}, - - // Fields of structs. - {".X", "-{{.X}}-", "-x-", tVal, true}, - {".U.V", "-{{.U.V}}-", "-v-", tVal, true}, - - // Dots of all kinds to test basic evaluation. - {"dot int", "<{{.}}>", "<13>", 13, true}, - {"dot uint", "<{{.}}>", "<14>", uint(14), true}, - {"dot float", "<{{.}}>", "<15.1>", 15.1, true}, - {"dot bool", "<{{.}}>", "<true>", true, true}, - {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true}, - {"dot string", "<{{.}}>", "<hello>", "hello", true}, - {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true}, - {"dot map", "<{{.}}>", "<map[two:22 one:11]>", map[string]int{"one": 11, "two": 22}, true}, - {"dot struct", "<{{.}}>", "<{7 seven}>", struct { - a int - b string - }{7, "seven"}, true}, - - // Pointers. - {"*int", "{{.PI}}", "23", tVal, true}, - {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, - {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, - {"NIL", "{{.NIL}}", "<nil>", tVal, true}, - - // Emtpy interfaces holding values. - {"empty nil", "{{.Empty0}}", "<no value>", tVal, true}, - {"empty with int", "{{.Empty1}}", "3", tVal, true}, - {"empty with string", "{{.Empty2}}", "empty2", tVal, true}, - {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true}, - {"empty with struct", "{{.Empty4}}", "{v}", tVal, true}, - - // Method calls. - {".Method0", "-{{.Method0}}-", "-resultOfMethod0-", tVal, true}, - {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, - {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, - {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, - {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, - - // Pipelines. - {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 resultOfMethod0-", tVal, true}, - - // If. - {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, - {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, - {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - - // Printf. - {"printf", `{{printf "hello, printf"}}`, "hello, printf", tVal, true}, - {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, - {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, - {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, - {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, - {"printf function", `{{printf "%#q" gopher}}`, "`gopher`", tVal, true}, - {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, - {"printf method", `{{printf "%s" .Method0}}`, "resultOfMethod0", tVal, true}, - {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) resultOfMethod0", tVal, true}, - - // HTML. - {"html", `{{html "<script>alert(\"XSS\");</script>"}}`, - "<script>alert("XSS");</script>", nil, true}, - {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`, - "<script>alert("XSS");</script>", nil, true}, - - // JavaScript. - {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, - - // Booleans - {"not", "{{not true}} {{not false}}", "false true", nil, true}, - {"and", "{{and 0 0}} {{and 1 0}} {{and 0 1}} {{and 1 1}}", "false false false true", nil, true}, - {"or", "{{or 0 0}} {{or 1 0}} {{or 0 1}} {{or 1 1}}", "false true true true", nil, true}, - {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, - {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, - - // Indexing. - {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, - {"slice[1]", "{{index .SI 1}}", "4", tVal, true}, - {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false}, - {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, - {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, - {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, - {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, false}, - {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, - {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, - - // With. - {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, - {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, - {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true}, - {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true}, - {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true}, - {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true}, - {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true}, - {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true}, - {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true}, - - // Range. - {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true}, - {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, - {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, - {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, - {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, - {"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true}, - {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, - {"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true}, - {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, - - // Error handling. - {"error method, error", "{{.EPERM true}}", "", tVal, false}, - {"error method, no error", "{{.EPERM false}}", "false", tVal, true}, -} - -func gopher() string { - return "gopher" -} - -func testExecute(execTests []execTest, set *Set, t *testing.T) { - b := new(bytes.Buffer) - funcs := FuncMap{"gopher": gopher} - for _, test := range execTests { - tmpl := New(test.name).Funcs(funcs) - err := tmpl.Parse(test.input) - if err != nil { - t.Errorf("%s: parse error: %s", test.name, err) - continue - } - b.Reset() - err = tmpl.ExecuteInSet(b, test.data, set) - switch { - case !test.ok && err == nil: - t.Errorf("%s: expected error; got none", test.name) - continue - case test.ok && err != nil: - t.Errorf("%s: unexpected execute error: %s", test.name, err) - continue - case !test.ok && err != nil: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - } - result := b.String() - if result != test.output { - t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result) - } - } -} - -func TestExecute(t *testing.T) { - testExecute(execTests, nil, t) -} - -// Check that an error from a method flows back to the top. -func TestExecuteError(t *testing.T) { - b := new(bytes.Buffer) - tmpl := New("error") - err := tmpl.Parse("{{.EPERM true}}") - if err != nil { - t.Fatalf("parse error: %s", err) - } - err = tmpl.Execute(b, tVal) - if err == nil { - t.Errorf("expected error; got none") - } else if !strings.Contains(err.String(), os.EPERM.String()) { - t.Errorf("expected os.EPERM; got %s", err) - } -} - -func TestJSEscaping(t *testing.T) { - testCases := []struct { - in, exp string - }{ - {`a`, `a`}, - {`'foo`, `\'foo`}, - {`Go "jump" \`, `Go \"jump\" \\`}, - {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, - {"unprintable \uFDFF", `unprintable \uFDFF`}, - } - for _, tc := range testCases { - s := JSEscapeString(tc.in) - if s != tc.exp { - t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp) - } - } -} diff --git a/src/pkg/exp/template/funcs.go b/src/pkg/exp/template/funcs.go deleted file mode 100644 index 66be40fd4..000000000 --- a/src/pkg/exp/template/funcs.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "bytes" - "fmt" - "io" - "os" - "reflect" - "strings" - "unicode" - "utf8" -) - -// FuncMap is the type of the map defining the mapping from names to functions. -// Each function must have either a single return value, or two return values of -// which the second has type os.Error. -type FuncMap map[string]interface{} - -var funcs = map[string]reflect.Value{ - "and": reflect.ValueOf(and), - "html": reflect.ValueOf(HTMLEscaper), - "index": reflect.ValueOf(index), - "js": reflect.ValueOf(JSEscaper), - "not": reflect.ValueOf(not), - "or": reflect.ValueOf(or), - "printf": reflect.ValueOf(fmt.Sprintf), -} - -// addFuncs adds to values the functions in funcs, converting them to reflect.Values. -func addFuncs(values map[string]reflect.Value, funcMap FuncMap) { - for name, fn := range funcMap { - v := reflect.ValueOf(fn) - if v.Kind() != reflect.Func { - panic("value for " + name + " not a function") - } - if !goodFunc(v.Type()) { - panic(fmt.Errorf("can't handle multiple results from method/function %q", name)) - } - values[name] = v - } -} - -// goodFunc checks that the function or method has the right result signature. -func goodFunc(typ reflect.Type) bool { - // We allow functions with 1 result or 2 results where the second is an os.Error. - switch { - case typ.NumOut() == 1: - return true - case typ.NumOut() == 2 && typ.Out(1) == osErrorType: - return true - } - return false -} - -// findFunction looks for a function in the template, set, and global map. -func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) { - if tmpl != nil { - if fn := tmpl.funcs[name]; fn.IsValid() { - return fn, true - } - } - if set != nil { - if fn := set.funcs[name]; fn.IsValid() { - return fn, true - } - } - if fn := funcs[name]; fn.IsValid() { - return fn, true - } - return reflect.Value{}, false -} - -// Indexing. - -// index returns the result of indexing its first argument by the following -// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each -// indexed item must be a map, slice, or array. -func index(item interface{}, indices ...interface{}) (interface{}, os.Error) { - v := reflect.ValueOf(item) - for _, i := range indices { - index := reflect.ValueOf(i) - var isNil bool - if v, isNil = indirect(v); isNil { - return nil, fmt.Errorf("index of nil pointer") - } - switch v.Kind() { - case reflect.Array, reflect.Slice: - var x int64 - switch index.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - x = index.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - x = int64(index.Uint()) - default: - return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) - } - if x < 0 || x >= int64(v.Len()) { - return nil, fmt.Errorf("index out of range: %d", x) - } - v = v.Index(int(x)) - case reflect.Map: - if !index.Type().AssignableTo(v.Type().Key()) { - return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) - } - v = v.MapIndex(index) - if !v.IsValid() { - return nil, fmt.Errorf("index %v not present in map", index.Interface()) - } - default: - return nil, fmt.Errorf("can't index item of type %s", index.Type()) - } - } - return v.Interface(), nil -} - -// Boolean logic. - -// and returns the Boolean AND of its arguments. -func and(arg0 interface{}, args ...interface{}) (truth bool) { - truth, _ = isTrue(reflect.ValueOf(arg0)) - for i := 0; truth && i < len(args); i++ { - truth, _ = isTrue(reflect.ValueOf(args[i])) - } - return -} - -// or returns the Boolean OR of its arguments. -func or(arg0 interface{}, args ...interface{}) (truth bool) { - truth, _ = isTrue(reflect.ValueOf(arg0)) - for i := 0; !truth && i < len(args); i++ { - truth, _ = isTrue(reflect.ValueOf(args[i])) - } - return -} - -// not returns the Boolean negation of its argument. -func not(arg interface{}) (truth bool) { - truth, _ = isTrue(reflect.ValueOf(arg)) - return !truth -} - -// HTML escaping. - -var ( - htmlQuot = []byte(""") // shorter than """ - htmlApos = []byte("'") // shorter than "'" - htmlAmp = []byte("&") - htmlLt = []byte("<") - htmlGt = []byte(">") -) - -// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. -func HTMLEscape(w io.Writer, b []byte) { - last := 0 - for i, c := range b { - var html []byte - switch c { - case '"': - html = htmlQuot - case '\'': - html = htmlApos - case '&': - html = htmlAmp - case '<': - html = htmlLt - case '>': - html = htmlGt - default: - continue - } - w.Write(b[last:i]) - w.Write(html) - last = i + 1 - } - w.Write(b[last:]) -} - -// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. -func HTMLEscapeString(s string) string { - // Avoid allocation if we can. - if strings.IndexAny(s, `'"&<>`) < 0 { - return s - } - var b bytes.Buffer - HTMLEscape(&b, []byte(s)) - return b.String() -} - -// HTMLEscaper returns the escaped HTML equivalent of the textual -// representation of its arguments. -func HTMLEscaper(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - return HTMLEscapeString(s) -} - -// JavaScript escaping. - -var ( - jsLowUni = []byte(`\u00`) - hex = []byte("0123456789ABCDEF") - - jsBackslash = []byte(`\\`) - jsApos = []byte(`\'`) - jsQuot = []byte(`\"`) -) - - -// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. -func JSEscape(w io.Writer, b []byte) { - last := 0 - for i := 0; i < len(b); i++ { - c := b[i] - - if ' ' <= c && c < utf8.RuneSelf && c != '\\' && c != '"' && c != '\'' { - // fast path: nothing to do - continue - } - w.Write(b[last:i]) - - if c < utf8.RuneSelf { - // Quotes and slashes get quoted. - // Control characters get written as \u00XX. - switch c { - case '\\': - w.Write(jsBackslash) - case '\'': - w.Write(jsApos) - case '"': - w.Write(jsQuot) - default: - w.Write(jsLowUni) - t, b := c>>4, c&0x0f - w.Write(hex[t : t+1]) - w.Write(hex[b : b+1]) - } - } else { - // Unicode rune. - rune, size := utf8.DecodeRune(b[i:]) - if unicode.IsPrint(rune) { - w.Write(b[i : i+size]) - } else { - // TODO(dsymonds): Do this without fmt? - fmt.Fprintf(w, "\\u%04X", rune) - } - i += size - 1 - } - last = i + 1 - } - w.Write(b[last:]) -} - -// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. -func JSEscapeString(s string) string { - // Avoid allocation if we can. - if strings.IndexFunc(s, jsIsSpecial) < 0 { - return s - } - var b bytes.Buffer - JSEscape(&b, []byte(s)) - return b.String() -} - -func jsIsSpecial(rune int) bool { - switch rune { - case '\\', '\'', '"': - return true - } - return rune < ' ' || utf8.RuneSelf <= rune -} - -// JSEscaper returns the escaped JavaScript equivalent of the textual -// representation of its arguments. -func JSEscaper(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - return JSEscapeString(s) -} diff --git a/src/pkg/exp/template/lex.go b/src/pkg/exp/template/lex.go deleted file mode 100644 index d78152979..000000000 --- a/src/pkg/exp/template/lex.go +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "fmt" - "strings" - "unicode" - "utf8" -) - -// item represents a token or text string returned from the scanner. -type item struct { - typ itemType - val string -} - -func (i item) String() string { - switch { - case i.typ == itemEOF: - return "EOF" - case i.typ == itemError: - return i.val - case i.typ > itemKeyword: - return fmt.Sprintf("<%s>", i.val) - case len(i.val) > 10: - return fmt.Sprintf("%.10q...", i.val) - } - return fmt.Sprintf("%q", i.val) -} - -// itemType identifies the type of lex items. -type itemType int - -const ( - itemError itemType = iota // error occurred; value is text of error - itemBool // boolean constant - itemComplex // complex constant (1+2i); imaginary is just a number - itemEOF - itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y') - itemIdentifier // alphanumeric identifier - itemLeftDelim // left action delimiter - itemNumber // simple number, including imaginary - itemPipe // pipe symbol - itemRawString // raw quoted string (includes quotes) - itemRightDelim // right action delimiter - itemString // quoted string (includes quotes) - itemText // plain text - // Keywords appear after all the rest. - itemKeyword // used only to delimit the keywords - itemDot // the cursor, spelled '.'. - itemDefine // define keyword - itemElse // else keyword - itemEnd // end keyword - itemIf // if keyword - itemRange // range keyword - itemTemplate // template keyword - itemWith // with keyword -) - -// Make the types prettyprint. -var itemName = map[itemType]string{ - itemError: "error", - itemBool: "bool", - itemComplex: "complex", - itemEOF: "EOF", - itemField: "field", - itemIdentifier: "identifier", - itemLeftDelim: "left delim", - itemNumber: "number", - itemPipe: "pipe", - itemRawString: "raw string", - itemRightDelim: "right delim", - itemString: "string", - // keywords - itemDot: ".", - itemDefine: "define", - itemElse: "else", - itemIf: "if", - itemEnd: "end", - itemRange: "range", - itemTemplate: "template", - itemWith: "with", -} - -func (i itemType) String() string { - s := itemName[i] - if s == "" { - return fmt.Sprintf("item%d", int(i)) - } - return s -} - -var key = map[string]itemType{ - ".": itemDot, - "define": itemDefine, - "else": itemElse, - "end": itemEnd, - "if": itemIf, - "range": itemRange, - "template": itemTemplate, - "with": itemWith, -} - -const eof = -1 - -// stateFn represents the state of the scanner as a function that returns the next state. -type stateFn func(*lexer) stateFn - -// lexer holds the state of the scanner. -type lexer struct { - name string // the name of the input; used only for error reports. - input string // the string being scanned. - state stateFn // the next lexing function to enter - pos int // current position in the input. - start int // start position of this item. - width int // width of last rune read from input. - items chan item // channel of scanned items. -} - -// next returns the next rune in the input. -func (l *lexer) next() (rune int) { - if l.pos >= len(l.input) { - l.width = 0 - return eof - } - rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) - l.pos += l.width - return rune -} - -// peek returns but does not consume the next rune in the input. -func (l *lexer) peek() int { - rune := l.next() - l.backup() - return rune -} - -// backup steps back one rune. Can only be called once per call of next. -func (l *lexer) backup() { - l.pos -= l.width -} - -// emit passes an item back to the client. -func (l *lexer) emit(t itemType) { - l.items <- item{t, l.input[l.start:l.pos]} - l.start = l.pos -} - -// ignore skips over the pending input before this point. -func (l *lexer) ignore() { - l.start = l.pos -} - -// accept consumes the next rune if it's from the valid set. -func (l *lexer) accept(valid string) bool { - if strings.IndexRune(valid, l.next()) >= 0 { - return true - } - l.backup() - return false -} - -// acceptRun consumes a run of runes from the valid set. -func (l *lexer) acceptRun(valid string) { - for strings.IndexRune(valid, l.next()) >= 0 { - } - l.backup() -} - -// lineNumber reports which line we're on. Doing it this way -// means we don't have to worry about peek double counting. -func (l *lexer) lineNumber() int { - return 1 + strings.Count(l.input[:l.pos], "\n") -} - -// error returns an error token and terminates the scan by passing -// back a nil pointer that will be the next state, terminating l.run. -func (l *lexer) errorf(format string, args ...interface{}) stateFn { - l.items <- item{itemError, fmt.Sprintf(format, args...)} - return nil -} - -// nextItem returns the next item from the input. -func (l *lexer) nextItem() item { - for { - select { - case item := <-l.items: - return item - default: - l.state = l.state(l) - } - } - panic("not reached") -} - -// lex creates a new scanner for the input string. -func lex(name, input string) *lexer { - l := &lexer{ - name: name, - input: input, - state: lexText, - items: make(chan item, 2), // Two items of buffering is sufficient for all state functions - } - return l -} - -// state functions - -const ( - leftDelim = "{{" - rightDelim = "}}" - leftComment = "{{/*" - rightComment = "*/}}" -) - -// lexText scans until an opening action delimiter, "{{". -func lexText(l *lexer) stateFn { - for { - if strings.HasPrefix(l.input[l.pos:], leftDelim) { - if l.pos > l.start { - l.emit(itemText) - } - return lexLeftDelim - } - if l.next() == eof { - break - } - } - // Correctly reached EOF. - if l.pos > l.start { - l.emit(itemText) - } - l.emit(itemEOF) - return nil -} - -// lexLeftDelim scans the left delimiter, which is known to be present. -func lexLeftDelim(l *lexer) stateFn { - if strings.HasPrefix(l.input[l.pos:], leftComment) { - return lexComment - } - l.pos += len(leftDelim) - l.emit(itemLeftDelim) - return lexInsideAction -} - -// lexComment scans a comment. The left comment marker is known to be present. -func lexComment(l *lexer) stateFn { - i := strings.Index(l.input[l.pos:], rightComment) - if i < 0 { - return l.errorf("unclosed comment") - } - l.pos += i + len(rightComment) - l.ignore() - return lexText -} - -// lexRightDelim scans the right delimiter, which is known to be present. -func lexRightDelim(l *lexer) stateFn { - l.pos += len(rightDelim) - l.emit(itemRightDelim) - return lexText -} - -// lexInsideAction scans the elements inside action delimiters. -func lexInsideAction(l *lexer) stateFn { - // Either number, quoted string, or identifier. - // Spaces separate and are ignored. - // Pipe symbols separate and are emitted. - for { - if strings.HasPrefix(l.input[l.pos:], rightDelim) { - return lexRightDelim - } - switch r := l.next(); { - case r == eof || r == '\n': - return l.errorf("unclosed action") - case isSpace(r): - l.ignore() - case r == '|': - l.emit(itemPipe) - case r == '"': - return lexQuote - case r == '`': - return lexRawQuote - case r == '.': - // special look-ahead for ".field" so we don't break l.backup(). - if l.pos < len(l.input) { - r := l.input[l.pos] - if r < '0' || '9' < r { - return lexIdentifier // itemDot comes from the keyword table. - } - } - fallthrough // '.' can start a number. - case r == '+' || r == '-' || ('0' <= r && r <= '9'): - l.backup() - return lexNumber - case isAlphaNumeric(r): - l.backup() - return lexIdentifier - default: - return l.errorf("unrecognized character in action: %#U", r) - } - } - return nil -} - -// lexIdentifier scans an alphanumeric or field. -func lexIdentifier(l *lexer) stateFn { -Loop: - for { - switch r := l.next(); { - case isAlphaNumeric(r): - // absorb. - case r == '.' && l.input[l.start] == '.': - // field chaining; absorb into one token. - default: - l.backup() - word := l.input[l.start:l.pos] - switch { - case key[word] > itemKeyword: - l.emit(key[word]) - case word[0] == '.': - l.emit(itemField) - case word == "true", word == "false": - l.emit(itemBool) - default: - l.emit(itemIdentifier) - } - break Loop - } - } - return lexInsideAction -} - -// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This -// isn't a perfect number scanner - for instance it accepts "." and "0x0.2" -// and "089" - but when it's wrong the input is invalid and the parser (via -// strconv) will notice. -func lexNumber(l *lexer) stateFn { - if !l.scanNumber() { - return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) - } - if sign := l.peek(); sign == '+' || sign == '-' { - // Complex: 1+2i. No spaces, must end in 'i'. - if !l.scanNumber() || l.input[l.pos-1] != 'i' { - return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) - } - l.emit(itemComplex) - } else { - l.emit(itemNumber) - } - return lexInsideAction -} - -func (l *lexer) scanNumber() bool { - // Optional leading sign. - l.accept("+-") - // Is it hex? - digits := "0123456789" - if l.accept("0") && l.accept("xX") { - digits = "0123456789abcdefABCDEF" - } - l.acceptRun(digits) - if l.accept(".") { - l.acceptRun(digits) - } - if l.accept("eE") { - l.accept("+-") - l.acceptRun("0123456789") - } - // Is it imaginary? - l.accept("i") - // Next thing mustn't be alphanumeric. - if isAlphaNumeric(l.peek()) { - l.next() - return false - } - return true -} - -// lexQuote scans a quoted string. -func lexQuote(l *lexer) stateFn { -Loop: - for { - switch l.next() { - case '\\': - if r := l.next(); r != eof && r != '\n' { - break - } - fallthrough - case eof, '\n': - return l.errorf("unterminated quoted string") - case '"': - break Loop - } - } - l.emit(itemString) - return lexInsideAction -} - -// lexRawQuote scans a raw quoted string. -func lexRawQuote(l *lexer) stateFn { -Loop: - for { - switch l.next() { - case eof, '\n': - return l.errorf("unterminated raw quoted string") - case '`': - break Loop - } - } - l.emit(itemRawString) - return lexInsideAction -} - -// isSpace reports whether r is a space character. -func isSpace(r int) bool { - switch r { - case ' ', '\t', '\n', '\r': - return true - } - return false -} - -// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. -func isAlphaNumeric(r int) bool { - return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) -} diff --git a/src/pkg/exp/template/lex_test.go b/src/pkg/exp/template/lex_test.go deleted file mode 100644 index 256ec04d8..000000000 --- a/src/pkg/exp/template/lex_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "reflect" - "testing" -) - -type lexTest struct { - name string - input string - items []item -} - -var ( - tEOF = item{itemEOF, ""} - tLeft = item{itemLeftDelim, "{{"} - tRight = item{itemRightDelim, "}}"} - tRange = item{itemRange, "range"} - tPipe = item{itemPipe, "|"} - tFor = item{itemIdentifier, "for"} - tQuote = item{itemString, `"abc \n\t\" "`} - raw = "`" + `abc\n\t\" ` + "`" - tRawQuote = item{itemRawString, raw} -) - -var lexTests = []lexTest{ - {"empty", "", []item{tEOF}}, - {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}}, - {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}}, - {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ - {itemText, "hello-"}, - {itemText, "-world"}, - tEOF, - }}, - {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}}, - {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}}, - {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, - {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}}, - {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{ - tLeft, - {itemNumber, "1"}, - {itemNumber, "02"}, - {itemNumber, "0x14"}, - {itemNumber, "-7.2i"}, - {itemNumber, "1e3"}, - {itemNumber, "+1.2e-4"}, - {itemNumber, "4.2i"}, - {itemComplex, "1+2i"}, - tRight, - tEOF, - }}, - {"bools", "{{true false}}", []item{ - tLeft, - {itemBool, "true"}, - {itemBool, "false"}, - tRight, - tEOF, - }}, - {"dot", "{{.}}", []item{ - tLeft, - {itemDot, "."}, - tRight, - tEOF, - }}, - {"dots", "{{.x . .2 .x.y}}", []item{ - tLeft, - {itemField, ".x"}, - {itemDot, "."}, - {itemNumber, ".2"}, - {itemField, ".x.y"}, - tRight, - tEOF, - }}, - {"keywords", "{{range if else end with}}", []item{ - tLeft, - {itemRange, "range"}, - {itemIf, "if"}, - {itemElse, "else"}, - {itemEnd, "end"}, - {itemWith, "with"}, - tRight, - tEOF, - }}, - {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{ - {itemText, "intro "}, - tLeft, - {itemIdentifier, "echo"}, - {itemIdentifier, "hi"}, - {itemNumber, "1.2"}, - tPipe, - {itemIdentifier, "noargs"}, - tPipe, - {itemIdentifier, "args"}, - {itemNumber, "1"}, - {itemString, `"hi"`}, - tRight, - {itemText, " outro"}, - tEOF, - }}, - // errors - {"badchar", "#{{#}}", []item{ - {itemText, "#"}, - tLeft, - {itemError, "unrecognized character in action: U+0023 '#'"}, - }}, - {"unclosed action", "{{\n}}", []item{ - tLeft, - {itemError, "unclosed action"}, - }}, - {"EOF in action", "{{range", []item{ - tLeft, - tRange, - {itemError, "unclosed action"}, - }}, - {"unclosed quote", "{{\"\n\"}}", []item{ - tLeft, - {itemError, "unterminated quoted string"}, - }}, - {"unclosed raw quote", "{{`xx\n`}}", []item{ - tLeft, - {itemError, "unterminated raw quoted string"}, - }}, - {"bad number", "{{3k}}", []item{ - tLeft, - {itemError, `bad number syntax: "3k"`}, - }}, -} - -// collect gathers the emitted items into a slice. -func collect(t *lexTest) (items []item) { - l := lex(t.name, t.input) - for { - item := l.nextItem() - items = append(items, item) - if item.typ == itemEOF || item.typ == itemError { - break - } - } - return -} - -func TestLex(t *testing.T) { - for _, test := range lexTests { - items := collect(&test) - if !reflect.DeepEqual(items, test.items) { - t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) - } - } -} diff --git a/src/pkg/exp/template/parse.go b/src/pkg/exp/template/parse.go deleted file mode 100644 index 8b2d60207..000000000 --- a/src/pkg/exp/template/parse.go +++ /dev/null @@ -1,783 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "bytes" - "fmt" - "os" - "reflect" - "runtime" - "strconv" - "strings" - "unicode" -) - -// Template is the representation of a parsed template. -type Template struct { - name string - root *listNode - funcs map[string]reflect.Value - // Parsing only; cleared after parse. - set *Set - lex *lexer - token item // token lookahead for parser - havePeek bool -} - -// next returns the next token. -func (t *Template) next() item { - if t.havePeek { - t.havePeek = false - } else { - t.token = t.lex.nextItem() - } - return t.token -} - -// backup backs the input stream up one token. -func (t *Template) backup() { - t.havePeek = true -} - -// peek returns but does not consume the next token. -func (t *Template) peek() item { - if t.havePeek { - return t.token - } - t.token = t.lex.nextItem() - t.havePeek = true - return t.token -} - -// A node is an element in the parse tree. The interface is trivial. -type node interface { - typ() nodeType - String() string -} - -type nodeType int - -func (t nodeType) typ() nodeType { - return t -} - -const ( - nodeText nodeType = iota - nodeAction - nodeCommand - nodeDot - nodeElse - nodeEnd - nodeField - nodeIdentifier - nodeIf - nodeList - nodeNumber - nodeRange - nodeString - nodeTemplate - nodeWith -) - -// Nodes. - -// listNode holds a sequence of nodes. -type listNode struct { - nodeType - nodes []node -} - -func newList() *listNode { - return &listNode{nodeType: nodeList} -} - -func (l *listNode) append(n node) { - l.nodes = append(l.nodes, n) -} - -func (l *listNode) String() string { - b := new(bytes.Buffer) - fmt.Fprint(b, "[") - for _, n := range l.nodes { - fmt.Fprint(b, n) - } - fmt.Fprint(b, "]") - return b.String() -} - -// textNode holds plain text. -type textNode struct { - nodeType - text []byte -} - -func newText(text string) *textNode { - return &textNode{nodeType: nodeText, text: []byte(text)} -} - -func (t *textNode) String() string { - return fmt.Sprintf("(text: %q)", t.text) -} - -// actionNode holds an action (something bounded by delimiters). -type actionNode struct { - nodeType - line int - pipeline []*commandNode -} - -func newAction(line int, pipeline []*commandNode) *actionNode { - return &actionNode{nodeType: nodeAction, line: line, pipeline: pipeline} -} - -func (a *actionNode) append(command *commandNode) { - a.pipeline = append(a.pipeline, command) -} - -func (a *actionNode) String() string { - return fmt.Sprintf("(action: %v)", a.pipeline) -} - -// commandNode holds a command (a pipeline inside an evaluating action). -type commandNode struct { - nodeType - args []node // identifier, string, or number -} - -func newCommand() *commandNode { - return &commandNode{nodeType: nodeCommand} -} - -func (c *commandNode) append(arg node) { - c.args = append(c.args, arg) -} - -func (c *commandNode) String() string { - return fmt.Sprintf("(command: %v)", c.args) -} - -// identifierNode holds an identifier. -type identifierNode struct { - nodeType - ident string -} - -func newIdentifier(ident string) *identifierNode { - return &identifierNode{nodeType: nodeIdentifier, ident: ident} -} - -func (i *identifierNode) String() string { - return fmt.Sprintf("I=%s", i.ident) -} - -// dotNode holds the special identifier '.'. It is represented by a nil pointer. -type dotNode bool - -func newDot() *dotNode { - return nil -} - -func (d *dotNode) typ() nodeType { - return nodeDot -} - -func (d *dotNode) String() string { - return "{{<.>}}" -} - -// fieldNode holds a field (identifier starting with '.'). -// The names may be chained ('.x.y'). -// The period is dropped from each ident. -type fieldNode struct { - nodeType - ident []string -} - -func newField(ident string) *fieldNode { - return &fieldNode{nodeType: nodeField, ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period -} - -func (f *fieldNode) String() string { - return fmt.Sprintf("F=%s", f.ident) -} - -// boolNode holds a boolean constant. -type boolNode struct { - nodeType - true bool -} - -func newBool(true bool) *boolNode { - return &boolNode{nodeType: nodeString, true: true} -} - -func (b *boolNode) String() string { - if b.true { - return fmt.Sprintf("B=true") - } - return fmt.Sprintf("B=false") -} - -// numberNode holds a number, signed or unsigned integer, floating, or complex. -// The value is parsed and stored under all the types that can represent the value. -// This simulates in a small amount of code the behavior of Go's ideal constants. -type numberNode struct { - nodeType - isInt bool // number has an integral value - isUint bool // number has an unsigned integral value - isFloat bool // number has a floating-point value - isComplex bool // number is complex - int64 // the signed integer value - uint64 // the unsigned integer value - float64 // the floating-point value - complex128 // the complex value - text string -} - -func newNumber(text string, isComplex bool) (*numberNode, os.Error) { - n := &numberNode{nodeType: nodeNumber, text: text} - if isComplex { - // fmt.Sscan can parse the pair, so let it do the work. - if _, err := fmt.Sscan(text, &n.complex128); err != nil { - return nil, err - } - n.isComplex = true - n.simplifyComplex() - return n, nil - } - // Imaginary constants can only be complex unless they are zero. - if len(text) > 0 && text[len(text)-1] == 'i' { - f, err := strconv.Atof64(text[:len(text)-1]) - if err == nil { - n.isComplex = true - n.complex128 = complex(0, f) - n.simplifyComplex() - return n, nil - } - } - // Do integer test first so we get 0x123 etc. - u, err := strconv.Btoui64(text, 0) // will fail for -0; fixed below. - if err == nil { - n.isUint = true - n.uint64 = u - } - i, err := strconv.Btoi64(text, 0) - if err == nil { - n.isInt = true - n.int64 = i - if i == 0 { - n.isUint = true // in case of -0. - n.uint64 = u - } - } - // If an integer extraction succeeded, promote the float. - if n.isInt { - n.isFloat = true - n.float64 = float64(n.int64) - } else if n.isUint { - n.isFloat = true - n.float64 = float64(n.uint64) - } else { - f, err := strconv.Atof64(text) - if err == nil { - n.isFloat = true - n.float64 = f - // If a floating-point extraction succeeded, extract the int if needed. - if !n.isInt && float64(int64(f)) == f { - n.isInt = true - n.int64 = int64(f) - } - if !n.isUint && float64(uint64(f)) == f { - n.isUint = true - n.uint64 = uint64(f) - } - } - } - if !n.isInt && !n.isUint && !n.isFloat { - return nil, fmt.Errorf("illegal number syntax: %q", text) - } - return n, nil -} - -// simplifyComplex pulls out any other types that are represented by the complex number. -// These all require that the imaginary part be zero. -func (n *numberNode) simplifyComplex() { - n.isFloat = imag(n.complex128) == 0 - if n.isFloat { - n.float64 = real(n.complex128) - n.isInt = float64(int64(n.float64)) == n.float64 - if n.isInt { - n.int64 = int64(n.float64) - } - n.isUint = float64(uint64(n.float64)) == n.float64 - if n.isUint { - n.uint64 = uint64(n.float64) - } - } -} - -func (n *numberNode) String() string { - return fmt.Sprintf("N=%s", n.text) -} - -// stringNode holds a quoted string. -type stringNode struct { - nodeType - text string -} - -func newString(text string) *stringNode { - return &stringNode{nodeType: nodeString, text: text} -} - -func (s *stringNode) String() string { - return fmt.Sprintf("S=%#q", s.text) -} - -// endNode represents an {{end}} action. It is represented by a nil pointer. -type endNode bool - -func newEnd() *endNode { - return nil -} - -func (e *endNode) typ() nodeType { - return nodeEnd -} - -func (e *endNode) String() string { - return "{{end}}" -} - -// elseNode represents an {{else}} action. -type elseNode struct { - nodeType - line int -} - -func newElse(line int) *elseNode { - return &elseNode{nodeType: nodeElse, line: line} -} - -func (e *elseNode) typ() nodeType { - return nodeElse -} - -func (e *elseNode) String() string { - return "{{else}}" -} -// ifNode represents an {{if}} action and its commands. -// TODO: what should evaluation look like? is a pipeline enough? -type ifNode struct { - nodeType - line int - pipeline []*commandNode - list *listNode - elseList *listNode -} - -func newIf(line int, pipeline []*commandNode, list, elseList *listNode) *ifNode { - return &ifNode{nodeType: nodeIf, line: line, pipeline: pipeline, list: list, elseList: elseList} -} - -func (i *ifNode) String() string { - if i.elseList != nil { - return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.pipeline, i.list, i.elseList) - } - return fmt.Sprintf("({{if %s}} %s)", i.pipeline, i.list) -} - -// rangeNode represents a {{range}} action and its commands. -type rangeNode struct { - nodeType - line int - pipeline []*commandNode - list *listNode - elseList *listNode -} - -func newRange(line int, pipeline []*commandNode, list, elseList *listNode) *rangeNode { - return &rangeNode{nodeType: nodeRange, line: line, pipeline: pipeline, list: list, elseList: elseList} -} - -func (r *rangeNode) String() string { - if r.elseList != nil { - return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.pipeline, r.list, r.elseList) - } - return fmt.Sprintf("({{range %s}} %s)", r.pipeline, r.list) -} - -// templateNode represents a {{template}} action. -type templateNode struct { - nodeType - line int - name node - pipeline []*commandNode -} - -func newTemplate(line int, name node, pipeline []*commandNode) *templateNode { - return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipeline: pipeline} -} - -func (t *templateNode) String() string { - return fmt.Sprintf("{{template %s %s}}", t.name, t.pipeline) -} - -// withNode represents a {{with}} action and its commands. -type withNode struct { - nodeType - line int - pipeline []*commandNode - list *listNode - elseList *listNode -} - -func newWith(line int, pipeline []*commandNode, list, elseList *listNode) *withNode { - return &withNode{nodeType: nodeWith, line: line, pipeline: pipeline, list: list, elseList: elseList} -} - -func (w *withNode) String() string { - if w.elseList != nil { - return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.pipeline, w.list, w.elseList) - } - return fmt.Sprintf("({{with %s}} %s)", w.pipeline, w.list) -} - - -// Parsing. - -// New allocates a new template with the given name. -func New(name string) *Template { - return &Template{ - name: name, - funcs: make(map[string]reflect.Value), - } -} - -// Funcs adds to the template's function map the elements of the -// argument map. It panics if a value in the map is not a function -// with appropriate return type. -// The return value is the template, so calls can be chained. -func (t *Template) Funcs(funcMap FuncMap) *Template { - addFuncs(t.funcs, funcMap) - return t -} - -// errorf formats the error and terminates processing. -func (t *Template) errorf(format string, args ...interface{}) { - t.root = nil - format = fmt.Sprintf("template: %s:%d: %s", t.name, t.lex.lineNumber(), format) - panic(fmt.Errorf(format, args...)) -} - -// error terminates processing. -func (t *Template) error(err os.Error) { - t.errorf("%s", err) -} - -// expect consumes the next token and guarantees it has the required type. -func (t *Template) expect(expected itemType, context string) item { - token := t.next() - if token.typ != expected { - t.errorf("expected %s in %s; got %s", expected, context, token) - } - return token -} - -// unexpected complains about the token and terminates processing. -func (t *Template) unexpected(token item, context string) { - t.errorf("unexpected %s in %s", token, context) -} - -// recover is the handler that turns panics into returns from the top -// level of Parse or Execute. -func (t *Template) recover(errp *os.Error) { - e := recover() - if e != nil { - if _, ok := e.(runtime.Error); ok { - panic(e) - } - t.stopParse() - *errp = e.(os.Error) - } - return -} - -// startParse starts the template parsing from the lexer. -func (t *Template) startParse(set *Set, lex *lexer) { - t.root = nil - t.set = set - t.lex = lex -} - -// stopParse terminates parsing. -func (t *Template) stopParse() { - t.set, t.lex = nil, nil -} - -// atEOF returns true if, possibly after spaces, we're at EOF. -func (t *Template) atEOF() bool { - for { - token := t.peek() - switch token.typ { - case itemEOF: - return true - case itemText: - for _, r := range token.val { - if !unicode.IsSpace(r) { - return false - } - } - t.next() // skip spaces. - continue - } - break - } - return false -} - -// Parse parses the template definition string to construct an internal representation -// of the template for execution. -func (t *Template) Parse(s string) (err os.Error) { - t.startParse(nil, lex(t.name, s)) - defer t.recover(&err) - t.parse(true) - t.stopParse() - return -} - -// ParseInSet parses the template definition string to construct an internal representation -// of the template for execution. Function bindings are checked against those in the set. -func (t *Template) ParseInSet(s string, set *Set) (err os.Error) { - t.startParse(set, lex(t.name, s)) - defer t.recover(&err) - t.parse(true) - t.stopParse() - return -} - -// parse is the helper for Parse. It triggers an error if we expect EOF but don't reach it. -func (t *Template) parse(toEOF bool) (next node) { - t.root, next = t.itemList(true) - if toEOF && next != nil { - t.errorf("unexpected %s", next) - } - return next -} - -// itemList: -// textOrAction* -// Terminates at EOF and at {{end}} or {{else}}, which is returned separately. -// The toEOF flag tells whether we expect to reach EOF. -func (t *Template) itemList(toEOF bool) (list *listNode, next node) { - list = newList() - for t.peek().typ != itemEOF { - n := t.textOrAction() - switch n.typ() { - case nodeEnd, nodeElse: - return list, n - } - list.append(n) - } - if !toEOF { - t.unexpected(t.next(), "input") - } - return list, nil -} - -// textOrAction: -// text | action -func (t *Template) textOrAction() node { - switch token := t.next(); token.typ { - case itemText: - return newText(token.val) - case itemLeftDelim: - return t.action() - default: - t.unexpected(token, "input") - } - return nil -} - -// Action: -// control -// command ("|" command)* -// Left delim is past. Now get actions. -// First word could be a keyword such as range. -func (t *Template) action() (n node) { - switch token := t.next(); token.typ { - case itemElse: - return t.elseControl() - case itemEnd: - return t.endControl() - case itemIf: - return t.ifControl() - case itemRange: - return t.rangeControl() - case itemTemplate: - return t.templateControl() - case itemWith: - return t.withControl() - } - t.backup() - return newAction(t.lex.lineNumber(), t.pipeline("command")) -} - -// Pipeline: -// field or command -// pipeline "|" pipeline -func (t *Template) pipeline(context string) (pipe []*commandNode) { - for { - switch token := t.next(); token.typ { - case itemRightDelim: - if len(pipe) == 0 { - t.errorf("missing value for %s", context) - } - return - case itemBool, itemComplex, itemDot, itemField, itemIdentifier, itemNumber, itemRawString, itemString: - t.backup() - pipe = append(pipe, t.command()) - default: - t.unexpected(token, context) - } - } - return -} - -func (t *Template) parseControl(context string) (lineNum int, pipe []*commandNode, list, elseList *listNode) { - lineNum = t.lex.lineNumber() - pipe = t.pipeline(context) - var next node - list, next = t.itemList(false) - switch next.typ() { - case nodeEnd: //done - case nodeElse: - elseList, next = t.itemList(false) - if next.typ() != nodeEnd { - t.errorf("expected end; found %s", next) - } - elseList = elseList - } - return lineNum, pipe, list, elseList -} - -// If: -// {{if pipeline}} itemList {{end}} -// {{if pipeline}} itemList {{else}} itemList {{end}} -// If keyword is past. -func (t *Template) ifControl() node { - return newIf(t.parseControl("if")) -} - -// Range: -// {{range pipeline}} itemList {{end}} -// {{range pipeline}} itemList {{else}} itemList {{end}} -// Range keyword is past. -func (t *Template) rangeControl() node { - return newRange(t.parseControl("range")) -} - -// With: -// {{with pipeline}} itemList {{end}} -// {{with pipeline}} itemList {{else}} itemList {{end}} -// If keyword is past. -func (t *Template) withControl() node { - return newWith(t.parseControl("with")) -} - - -// End: -// {{end}} -// End keyword is past. -func (t *Template) endControl() node { - t.expect(itemRightDelim, "end") - return newEnd() -} - -// Else: -// {{else}} -// Else keyword is past. -func (t *Template) elseControl() node { - t.expect(itemRightDelim, "else") - return newElse(t.lex.lineNumber()) -} - -// Template: -// {{template stringValue pipeline}} -// Template keyword is past. The name must be something that can evaluate -// to a string. -func (t *Template) templateControl() node { - var name node - switch token := t.next(); token.typ { - case itemIdentifier: - if _, ok := findFunction(token.val, t, t.set); !ok { - t.errorf("function %q not defined", token.val) - } - name = newIdentifier(token.val) - case itemDot: - name = newDot() - case itemField: - name = newField(token.val) - case itemString, itemRawString: - s, err := strconv.Unquote(token.val) - if err != nil { - t.error(err) - } - name = newString(s) - default: - t.unexpected(token, "template invocation") - } - pipeline := t.pipeline("template") - return newTemplate(t.lex.lineNumber(), name, pipeline) -} - -// command: -// space-separated arguments up to a pipeline character or right delimiter. -// we consume the pipe character but leave the right delim to terminate the action. -func (t *Template) command() *commandNode { - cmd := newCommand() -Loop: - for { - switch token := t.next(); token.typ { - case itemRightDelim: - t.backup() - break Loop - case itemPipe: - break Loop - case itemError: - t.errorf("%s", token.val) - case itemIdentifier: - if _, ok := findFunction(token.val, t, t.set); !ok { - t.errorf("function %q not defined", token.val) - } - cmd.append(newIdentifier(token.val)) - case itemDot: - cmd.append(newDot()) - case itemField: - cmd.append(newField(token.val)) - case itemBool: - cmd.append(newBool(token.val == "true")) - case itemComplex, itemNumber: - number, err := newNumber(token.val, token.typ == itemComplex) - if err != nil { - t.error(err) - } - cmd.append(number) - case itemString, itemRawString: - s, err := strconv.Unquote(token.val) - if err != nil { - t.error(err) - } - cmd.append(newString(s)) - default: - t.unexpected(token, "command") - } - } - if len(cmd.args) == 0 { - t.errorf("empty command") - } - return cmd -} diff --git a/src/pkg/exp/template/parse_test.go b/src/pkg/exp/template/parse_test.go deleted file mode 100644 index 71580f8b6..000000000 --- a/src/pkg/exp/template/parse_test.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "flag" - "fmt" - "testing" -) - -var debug = flag.Bool("debug", false, "show the errors produced by the tests") - -type numberTest struct { - text string - isInt bool - isUint bool - isFloat bool - isComplex bool - int64 - uint64 - float64 - complex128 -} - -var numberTests = []numberTest{ - // basics - {"0", true, true, true, false, 0, 0, 0, 0}, - {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint. - {"73", true, true, true, false, 73, 73, 73, 0}, - {"-73", true, false, true, false, -73, 0, -73, 0}, - {"+73", true, false, true, false, 73, 0, 73, 0}, - {"100", true, true, true, false, 100, 100, 100, 0}, - {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0}, - {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0}, - {"-1.2", false, false, true, false, 0, 0, -1.2, 0}, - {"1e19", false, true, true, false, 0, 1e19, 1e19, 0}, - {"-1e19", false, false, true, false, 0, 0, -1e19, 0}, - {"4i", false, false, false, true, 0, 0, 0, 4i}, - {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i}, - // complex with 0 imaginary are float (and maybe integer) - {"0i", true, true, true, true, 0, 0, 0, 0}, - {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2}, - {"-12+0i", true, false, true, true, -12, 0, -12, -12}, - {"13+0i", true, true, true, true, 13, 13, 13, 13}, - // funny bases - {"0123", true, true, true, false, 0123, 0123, 0123, 0}, - {"-0x0", true, true, true, false, 0, 0, 0, 0}, - {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0}, - // some broken syntax - {text: "+-2"}, - {text: "0x123."}, - {text: "1e."}, - {text: "0xi."}, - {text: "1+2."}, -} - -func TestNumberParse(t *testing.T) { - for _, test := range numberTests { - // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output - // because imaginary comes out as a number. - var c complex128 - _, err := fmt.Sscan(test.text, &c) - n, err := newNumber(test.text, err == nil) - ok := test.isInt || test.isUint || test.isFloat || test.isComplex - if ok && err != nil { - t.Errorf("unexpected error for %q", test.text) - continue - } - if !ok && err == nil { - t.Errorf("expected error for %q", test.text) - continue - } - if !ok { - continue - } - if n.isComplex != test.isComplex { - t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex) - } - if test.isInt { - if !n.isInt { - t.Errorf("expected integer for %q", test.text) - } - if n.int64 != test.int64 { - t.Errorf("int64 for %q should be %d is %d", test.text, test.int64, n.int64) - } - } else if n.isInt { - t.Errorf("did not expect integer for %q", test.text) - } - if test.isUint { - if !n.isUint { - t.Errorf("expected unsigned integer for %q", test.text) - } - if n.uint64 != test.uint64 { - t.Errorf("uint64 for %q should be %d is %d", test.text, test.uint64, n.uint64) - } - } else if n.isUint { - t.Errorf("did not expect unsigned integer for %q", test.text) - } - if test.isFloat { - if !n.isFloat { - t.Errorf("expected float for %q", test.text) - } - if n.float64 != test.float64 { - t.Errorf("float64 for %q should be %g is %g", test.text, test.float64, n.float64) - } - } else if n.isFloat { - t.Errorf("did not expect float for %q", test.text) - } - if test.isComplex { - if !n.isComplex { - t.Errorf("expected complex for %q", test.text) - } - if n.complex128 != test.complex128 { - t.Errorf("complex128 for %q should be %g is %g", test.text, test.complex128, n.complex128) - } - } else if n.isComplex { - t.Errorf("did not expect complex for %q", test.text) - } - } -} - -type parseTest struct { - name string - input string - ok bool - result string -} - -const ( - noError = true - hasError = false -) - -var parseTests = []parseTest{ - {"empty", "", noError, - `[]`}, - {"spaces", " \t\n", noError, - `[(text: " \t\n")]`}, - {"text", "some text", noError, - `[(text: "some text")]`}, - {"emptyAction", "{{}}", hasError, - `[(action: [])]`}, - {"field", "{{.X}}", noError, - `[(action: [(command: [F=[X]])])]`}, - {"simple command", "{{printf}}", noError, - `[(action: [(command: [I=printf])])]`}, - {"multi-word command", "{{printf `%d` 23}}", noError, - "[(action: [(command: [I=printf S=`%d` N=23])])]"}, - {"pipeline", "{{.X|.Y}}", noError, - `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`}, - {"simple if", "{{if .X}}hello{{end}}", noError, - `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`}, - {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, - `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, - {"simple range", "{{range .X}}hello{{end}}", noError, - `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`}, - {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, - `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`}, - {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError, - `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`}, - {"range with else", "{{range .X}}true{{else}}false{{end}}", noError, - `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, - {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError, - `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, - {"range []int", "{{range .SI}}{{.}}{{end}}", noError, - `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`}, - {"constants", "{{range .SI 1 -3.2i true false }}{{end}}", noError, - `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false])]}} [])]`}, - {"template", "{{template `x` .Y}}", noError, - "[{{template S=`x` [(command: [F=[Y]])]}}]"}, - {"with", "{{with .X}}hello{{end}}", noError, - `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`}, - {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError, - `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`}, - // Errors. - {"unclosed action", "hello{{range", hasError, ""}, - {"missing end", "hello{{range .x}}", hasError, ""}, - {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""}, - {"undefined function", "hello{{undefined}}", hasError, ""}, -} - -func TestParse(t *testing.T) { - for _, test := range parseTests { - tmpl := New(test.name) - err := tmpl.Parse(test.input) - switch { - case err == nil && !test.ok: - t.Errorf("%q: expected error; got none", test.name) - continue - case err != nil && test.ok: - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - case err != nil && !test.ok: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - continue - } - result := tmpl.root.String() - if result != test.result { - t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result) - } - } -} diff --git a/src/pkg/exp/template/set.go b/src/pkg/exp/template/set.go deleted file mode 100644 index 492e270e1..000000000 --- a/src/pkg/exp/template/set.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "fmt" - "io" - "os" - "reflect" - "runtime" - "strconv" -) - -// Set holds a set of related templates that can refer to one another by name. -// A template may be a member of multiple sets. -type Set struct { - tmpl map[string]*Template - funcs map[string]reflect.Value -} - -// NewSet allocates a new, empty template set. -func NewSet() *Set { - return &Set{ - tmpl: make(map[string]*Template), - funcs: make(map[string]reflect.Value), - } -} - -// Funcs adds to the set's function map the elements of the -// argument map. It panics if a value in the map is not a function -// with appropriate return type. -// The return value is the set, so calls can be chained. -func (s *Set) Funcs(funcMap FuncMap) *Set { - addFuncs(s.funcs, funcMap) - return s -} - -// Add adds the argument templates to the set. It panics if the call -// attempts to reuse a name defined in the template. -// The return value is the set, so calls can be chained. -func (s *Set) Add(templates ...*Template) *Set { - for _, t := range templates { - if _, ok := s.tmpl[t.name]; ok { - panic(fmt.Errorf("template: %q already defined in set", t.name)) - } - s.tmpl[t.name] = t - } - return s -} - -// Template returns the template with the given name in the set, -// or nil if there is no such template. -func (s *Set) Template(name string) *Template { - return s.tmpl[name] -} - -// Execute looks for the named template in the set and then applies that -// template to the specified data object, writing the output to wr. Nested -// template invocations will be resolved from the set. -func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error { - tmpl := s.tmpl[name] - if tmpl == nil { - return fmt.Errorf("template: no template %q in set", name) - } - return tmpl.ExecuteInSet(wr, data, s) -} - -// recover is the handler that turns panics into returns from the top -// level of Parse. -func (s *Set) recover(errp *os.Error) { - e := recover() - if e != nil { - if _, ok := e.(runtime.Error); ok { - panic(e) - } - s.tmpl = nil - *errp = e.(os.Error) - } - return -} - -// Parse parses the file into a set of named templates. -func (s *Set) Parse(text string) (err os.Error) { - defer s.recover(&err) - lex := lex("set", text) - const context = "define clause" - for { - t := New("set") // name will be updated once we know it. - t.startParse(s, lex) - // Expect EOF or "{{ define name }}". - if t.atEOF() { - return - } - t.expect(itemLeftDelim, context) - t.expect(itemDefine, context) - name := t.expect(itemString, context) - t.name, err = strconv.Unquote(name.val) - if err != nil { - t.error(err) - } - t.expect(itemRightDelim, context) - end := t.parse(false) - if end == nil { - t.errorf("unexpected EOF in %s", context) - } - if end.typ() != nodeEnd { - t.errorf("unexpected %s in %s", end, context) - } - t.stopParse() - s.tmpl[t.name] = t - } - return nil -} diff --git a/src/pkg/exp/template/set_test.go b/src/pkg/exp/template/set_test.go deleted file mode 100644 index c0115ec0a..000000000 --- a/src/pkg/exp/template/set_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2011 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. - -package template - -import ( - "fmt" - "testing" -) - -type setParseTest struct { - name string - input string - ok bool - names []string - results []string -} - -var setParseTests = []setParseTest{ - {"empty", "", noError, - nil, - nil}, - {"one", `{{define "foo"}} FOO {{end}}`, noError, - []string{"foo"}, - []string{`[(text: " FOO ")]`}}, - {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, - []string{"foo", "bar"}, - []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}}, - // errors - {"missing end", `{{define "foo"}} FOO `, hasError, - nil, - nil}, - {"malformed name", `{{define "foo}} FOO `, hasError, - nil, - nil}, -} - -func TestSetParse(t *testing.T) { - for _, test := range setParseTests { - set := NewSet() - err := set.Parse(test.input) - switch { - case err == nil && !test.ok: - t.Errorf("%q: expected error; got none", test.name) - continue - case err != nil && test.ok: - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - case err != nil && !test.ok: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - continue - } - if len(set.tmpl) != len(test.names) { - t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(set.tmpl)) - continue - } - for i, name := range test.names { - tmpl, ok := set.tmpl[name] - if !ok { - t.Errorf("%s: can't find template %q", test.name, name) - continue - } - result := tmpl.root.String() - if result != test.results[i] { - t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) - } - } - } -} - - -var setExecTests = []execTest{ - {"empty", "", "", nil, true}, - {"text", "some text", "some text", nil, true}, - {"invoke text", `{{template "text" .SI}}`, "TEXT", tVal, true}, - {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, - {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, - {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, - {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, -} - -const setText = ` - {{define "text"}}TEXT{{end}} - {{define "dotV"}}{{.V}}{{end}} - {{define "dot"}}{{.}}{{end}} - {{define "nested"}}{{template "dot" .}}{{end}} -` - -func TestSetExecute(t *testing.T) { - // Declare a set with a couple of templates first. - set := NewSet() - err := set.Parse(setText) - if err != nil { - t.Fatalf("error parsing set: %s", err) - } - testExecute(setExecTests, set, t) -} diff --git a/src/pkg/exp/wingui/Makefile b/src/pkg/exp/wingui/Makefile deleted file mode 100644 index e382c019f..000000000 --- a/src/pkg/exp/wingui/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2011 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. - -GOOS=windows - -include ../../../Make.inc - -LD:=$(LD) -Hwindowsgui - -TARG=wingui - -GOFILES=\ - gui.go\ - winapi.go\ - zwinapi.go\ - -include ../../../Make.cmd - -zwinapi.go: winapi.go - $(GOROOT)/src/pkg/syscall/mksyscall_windows.pl $< \ - | sed 's/^package.*syscall$$/package main/' \ - | sed '/^import/a \ - import "syscall"' \ - | sed 's/Syscall/syscall.Syscall/' \ - | sed 's/EINVAL/syscall.EINVAL/' \ - | gofmt \ - > $@ diff --git a/src/pkg/exp/wingui/gui.go b/src/pkg/exp/wingui/gui.go deleted file mode 100644 index cf392934c..000000000 --- a/src/pkg/exp/wingui/gui.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2011 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. - -package main - -import ( - "fmt" - "syscall" - "os" - "unsafe" -) - -// some help functions - -func abortf(format string, a ...interface{}) { - fmt.Fprintf(os.Stdout, format, a...) - os.Exit(1) -} - -func abortErrNo(funcname string, err int) { - abortf("%s failed: %d %s\n", funcname, err, syscall.Errstr(err)) -} - -// global vars - -var ( - mh uint32 - bh uint32 -) - -// WinProc called by windows to notify us of all windows events we might be interested in. -func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr { - var rc int32 - switch msg { - case WM_CREATE: - var e int - // CreateWindowEx - bh, e = CreateWindowEx( - 0, - syscall.StringToUTF16Ptr("button"), - syscall.StringToUTF16Ptr("Quit"), - WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON, - 75, 70, 140, 25, - hwnd, 1, mh, 0) - if e != 0 { - abortErrNo("CreateWindowEx", e) - } - fmt.Printf("button handle is %x\n", bh) - rc = DefWindowProc(hwnd, msg, wparam, lparam) - case WM_COMMAND: - switch uint32(lparam) { - case bh: - e := PostMessage(hwnd, WM_CLOSE, 0, 0) - if e != 0 { - abortErrNo("PostMessage", e) - } - default: - rc = DefWindowProc(hwnd, msg, wparam, lparam) - } - case WM_CLOSE: - DestroyWindow(hwnd) - case WM_DESTROY: - PostQuitMessage(0) - default: - rc = DefWindowProc(hwnd, msg, wparam, lparam) - } - //fmt.Printf("WndProc(0x%08x, %d, 0x%08x, 0x%08x) (%d)\n", hwnd, msg, wparam, lparam, rc) - return uintptr(rc) -} - -func rungui() int { - var e int - - // GetModuleHandle - mh, e = GetModuleHandle(nil) - if e != 0 { - abortErrNo("GetModuleHandle", e) - } - - // Get icon we're going to use. - myicon, e := LoadIcon(0, IDI_APPLICATION) - if e != 0 { - abortErrNo("LoadIcon", e) - } - - // Get cursor we're going to use. - mycursor, e := LoadCursor(0, IDC_ARROW) - if e != 0 { - abortErrNo("LoadCursor", e) - } - - // Create callback - wproc := syscall.NewCallback(WndProc) - - // RegisterClassEx - wcname := syscall.StringToUTF16Ptr("myWindowClass") - var wc Wndclassex - wc.Size = uint32(unsafe.Sizeof(wc)) - wc.WndProc = wproc - wc.Instance = mh - wc.Icon = myicon - wc.Cursor = mycursor - wc.Background = COLOR_BTNFACE + 1 - wc.MenuName = nil - wc.ClassName = wcname - wc.IconSm = myicon - if _, e := RegisterClassEx(&wc); e != 0 { - abortErrNo("RegisterClassEx", e) - } - - // CreateWindowEx - wh, e := CreateWindowEx( - WS_EX_CLIENTEDGE, - wcname, - syscall.StringToUTF16Ptr("My window"), - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, - 0, 0, mh, 0) - if e != 0 { - abortErrNo("CreateWindowEx", e) - } - fmt.Printf("main window handle is %x\n", wh) - - // ShowWindow - ShowWindow(wh, SW_SHOWDEFAULT) - - // UpdateWindow - if e := UpdateWindow(wh); e != 0 { - abortErrNo("UpdateWindow", e) - } - - // Process all windows messages until WM_QUIT. - var m Msg - for { - r, e := GetMessage(&m, 0, 0, 0) - if e != 0 { - abortErrNo("GetMessage", e) - } - if r == 0 { - // WM_QUIT received -> get out - break - } - TranslateMessage(&m) - DispatchMessage(&m) - } - return int(m.Wparam) -} - -func main() { - rc := rungui() - os.Exit(rc) -} diff --git a/src/pkg/exp/wingui/winapi.go b/src/pkg/exp/wingui/winapi.go deleted file mode 100644 index fb0d61009..000000000 --- a/src/pkg/exp/wingui/winapi.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2011 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. - -package main - -import ( - "syscall" - "unsafe" -) - -func loadDll(fname string) uint32 { - h, e := syscall.LoadLibrary(fname) - if e != 0 { - abortf("LoadLibrary(%s) failed with err=%d.\n", fname, e) - } - return h -} - -func getSysProcAddr(m uint32, pname string) uintptr { - p, e := syscall.GetProcAddress(m, pname) - if e != 0 { - abortf("GetProcAddress(%s) failed with err=%d.\n", pname, e) - } - return uintptr(p) -} - -type Wndclassex struct { - Size uint32 - Style uint32 - WndProc uintptr - ClsExtra int32 - WndExtra int32 - Instance uint32 - Icon uint32 - Cursor uint32 - Background uint32 - MenuName *uint16 - ClassName *uint16 - IconSm uint32 -} - -type Point struct { - X int32 - Y int32 -} - -type Msg struct { - Hwnd uint32 - Message uint32 - Wparam int32 - Lparam int32 - Time uint32 - Pt Point -} - -const ( - // Window styles - WS_OVERLAPPED = 0 - WS_POPUP = 0x80000000 - WS_CHILD = 0x40000000 - WS_MINIMIZE = 0x20000000 - WS_VISIBLE = 0x10000000 - WS_DISABLED = 0x8000000 - WS_CLIPSIBLINGS = 0x4000000 - WS_CLIPCHILDREN = 0x2000000 - WS_MAXIMIZE = 0x1000000 - WS_CAPTION = WS_BORDER | WS_DLGFRAME - WS_BORDER = 0x800000 - WS_DLGFRAME = 0x400000 - WS_VSCROLL = 0x200000 - WS_HSCROLL = 0x100000 - WS_SYSMENU = 0x80000 - WS_THICKFRAME = 0x40000 - WS_GROUP = 0x20000 - WS_TABSTOP = 0x10000 - WS_MINIMIZEBOX = 0x20000 - WS_MAXIMIZEBOX = 0x10000 - WS_TILED = WS_OVERLAPPED - WS_ICONIC = WS_MINIMIZE - WS_SIZEBOX = WS_THICKFRAME - // Common Window Styles - WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX - WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW - WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU - WS_CHILDWINDOW = WS_CHILD - - WS_EX_CLIENTEDGE = 0x200 - - // Some windows messages - WM_CREATE = 1 - WM_DESTROY = 2 - WM_CLOSE = 16 - WM_COMMAND = 273 - - // Some button control styles - BS_DEFPUSHBUTTON = 1 - - // Some color constants - COLOR_WINDOW = 5 - COLOR_BTNFACE = 15 - - // Default window position - CW_USEDEFAULT = 0x80000000 - 0x100000000 - - // Show window default style - SW_SHOWDEFAULT = 10 -) - -var ( - // Some globally known cursors - IDC_ARROW = MakeIntResource(32512) - IDC_IBEAM = MakeIntResource(32513) - IDC_WAIT = MakeIntResource(32514) - IDC_CROSS = MakeIntResource(32515) - - // Some globally known icons - IDI_APPLICATION = MakeIntResource(32512) - IDI_HAND = MakeIntResource(32513) - IDI_QUESTION = MakeIntResource(32514) - IDI_EXCLAMATION = MakeIntResource(32515) - IDI_ASTERISK = MakeIntResource(32516) - IDI_WINLOGO = MakeIntResource(32517) - IDI_WARNING = IDI_EXCLAMATION - IDI_ERROR = IDI_HAND - IDI_INFORMATION = IDI_ASTERISK -) - -//sys GetModuleHandle(modname *uint16) (handle uint32, errno int) = GetModuleHandleW -//sys RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) = user32.RegisterClassExW -//sys CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) = user32.CreateWindowExW -//sys DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.DefWindowProcW -//sys DestroyWindow(hwnd uint32) (errno int) = user32.DestroyWindow -//sys PostQuitMessage(exitcode int32) = user32.PostQuitMessage -//sys ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) = user32.ShowWindow -//sys UpdateWindow(hwnd uint32) (errno int) = user32.UpdateWindow -//sys GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) [failretval==-1] = user32.GetMessageW -//sys TranslateMessage(msg *Msg) (done bool) = user32.TranslateMessage -//sys DispatchMessage(msg *Msg) (ret int32) = user32.DispatchMessageW -//sys LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) = user32.LoadIconW -//sys LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) = user32.LoadCursorW -//sys SetCursor(cursor uint32) (precursor uint32, errno int) = user32.SetCursor -//sys SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.SendMessageW -//sys PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) = user32.PostMessageW - -func MakeIntResource(id uint16) *uint16 { - return (*uint16)(unsafe.Pointer(uintptr(id))) -} diff --git a/src/pkg/exp/wingui/zwinapi.go b/src/pkg/exp/wingui/zwinapi.go deleted file mode 100644 index 6ae6330a1..000000000 --- a/src/pkg/exp/wingui/zwinapi.go +++ /dev/null @@ -1,211 +0,0 @@ -// mksyscall_windows.pl winapi.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package main - -import "unsafe" -import "syscall" - -var ( - modkernel32 = loadDll("kernel32.dll") - moduser32 = loadDll("user32.dll") - - procGetModuleHandleW = getSysProcAddr(modkernel32, "GetModuleHandleW") - procRegisterClassExW = getSysProcAddr(moduser32, "RegisterClassExW") - procCreateWindowExW = getSysProcAddr(moduser32, "CreateWindowExW") - procDefWindowProcW = getSysProcAddr(moduser32, "DefWindowProcW") - procDestroyWindow = getSysProcAddr(moduser32, "DestroyWindow") - procPostQuitMessage = getSysProcAddr(moduser32, "PostQuitMessage") - procShowWindow = getSysProcAddr(moduser32, "ShowWindow") - procUpdateWindow = getSysProcAddr(moduser32, "UpdateWindow") - procGetMessageW = getSysProcAddr(moduser32, "GetMessageW") - procTranslateMessage = getSysProcAddr(moduser32, "TranslateMessage") - procDispatchMessageW = getSysProcAddr(moduser32, "DispatchMessageW") - procLoadIconW = getSysProcAddr(moduser32, "LoadIconW") - procLoadCursorW = getSysProcAddr(moduser32, "LoadCursorW") - procSetCursor = getSysProcAddr(moduser32, "SetCursor") - procSendMessageW = getSysProcAddr(moduser32, "SendMessageW") - procPostMessageW = getSysProcAddr(moduser32, "PostMessageW") -) - -func GetModuleHandle(modname *uint16) (handle uint32, errno int) { - r0, _, e1 := syscall.Syscall(procGetModuleHandleW, 1, uintptr(unsafe.Pointer(modname)), 0, 0) - handle = uint32(r0) - if handle == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) { - r0, _, e1 := syscall.Syscall(procRegisterClassExW, 1, uintptr(unsafe.Pointer(wndclass)), 0, 0) - atom = uint16(r0) - if atom == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) { - r0, _, e1 := syscall.Syscall12(procCreateWindowExW, 12, uintptr(exstyle), uintptr(unsafe.Pointer(classname)), uintptr(unsafe.Pointer(windowname)), uintptr(style), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(wndparent), uintptr(menu), uintptr(instance), uintptr(param)) - hwnd = uint32(r0) - if hwnd == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) { - r0, _, _ := syscall.Syscall6(procDefWindowProcW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) - lresult = int32(r0) - return -} - -func DestroyWindow(hwnd uint32) (errno int) { - r1, _, e1 := syscall.Syscall(procDestroyWindow, 1, uintptr(hwnd), 0, 0) - if int(r1) == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func PostQuitMessage(exitcode int32) { - syscall.Syscall(procPostQuitMessage, 1, uintptr(exitcode), 0, 0) - return -} - -func ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) { - r0, _, _ := syscall.Syscall(procShowWindow, 2, uintptr(hwnd), uintptr(cmdshow), 0) - wasvisible = bool(r0 != 0) - return -} - -func UpdateWindow(hwnd uint32) (errno int) { - r1, _, e1 := syscall.Syscall(procUpdateWindow, 1, uintptr(hwnd), 0, 0) - if int(r1) == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) { - r0, _, e1 := syscall.Syscall6(procGetMessageW, 4, uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax), 0, 0) - ret = int32(r0) - if ret == -1 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func TranslateMessage(msg *Msg) (done bool) { - r0, _, _ := syscall.Syscall(procTranslateMessage, 1, uintptr(unsafe.Pointer(msg)), 0, 0) - done = bool(r0 != 0) - return -} - -func DispatchMessage(msg *Msg) (ret int32) { - r0, _, _ := syscall.Syscall(procDispatchMessageW, 1, uintptr(unsafe.Pointer(msg)), 0, 0) - ret = int32(r0) - return -} - -func LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) { - r0, _, e1 := syscall.Syscall(procLoadIconW, 2, uintptr(instance), uintptr(unsafe.Pointer(iconname)), 0) - icon = uint32(r0) - if icon == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) { - r0, _, e1 := syscall.Syscall(procLoadCursorW, 2, uintptr(instance), uintptr(unsafe.Pointer(cursorname)), 0) - cursor = uint32(r0) - if cursor == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func SetCursor(cursor uint32) (precursor uint32, errno int) { - r0, _, e1 := syscall.Syscall(procSetCursor, 1, uintptr(cursor), 0, 0) - precursor = uint32(r0) - if precursor == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} - -func SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) { - r0, _, _ := syscall.Syscall6(procSendMessageW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) - lresult = int32(r0) - return -} - -func PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) { - r1, _, e1 := syscall.Syscall6(procPostMessageW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) - if int(r1) == 0 { - if e1 != 0 { - errno = int(e1) - } else { - errno = syscall.EINVAL - } - } else { - errno = 0 - } - return -} |
