// noOp is the dual of noVar. It also checks for assignment to builtins. // It just errors out if there is a conflict. func (c *Context) noOp(name string) { if name == "pi" || name == "e" { // Cannot redefine these. value.Errorf("cannot reassign %q", name) } if c.UnaryFn[name] == nil && c.BinaryFn[name] == nil { return } value.Errorf("cannot define variable %s; it is an op", name) }
// noVar guarantees that there is no global variable with that name, // preventing an op from being defined with the same name as a variable, // which could cause problems. A variable with value zero is considered to // be OK, so one can clear a variable before defining a symbol. A cleared // variable is removed from the global symbol table. // noVar also prevents defining builtin variables as ops. func (c *Context) noVar(name string) { if name == "_" || name == "pi" || name == "e" { // Cannot redefine these. value.Errorf(`cannot define op with name %q`, name) } sym := c.Stack[0][name] if sym == nil { return } if i, ok := sym.(value.Int); ok && i == 0 { delete(c.Stack[0], name) return } value.Errorf("cannot define op %s; it is a variable (%[1]s=0 to clear)", name) }
func (e variableExpr) Eval(context value.Context) value.Value { v := context.Lookup(e.name) if v == nil { value.Errorf("undefined variable %q", e.name) } return v }
func (u *unaryCall) Eval(context value.Context) value.Value { arg := u.arg.Eval(context) context.Push() defer context.Pop() exec := context.(*execContext) // Sigh. fn := exec.unaryFn[u.name] if fn == nil || fn.body == nil { value.Errorf("unary %q undefined", u.name) } context.AssignLocal(fn.right.name, arg) var v value.Value for _, e := range fn.body { v = e.Eval(context) } if v == nil { value.Errorf("no value returned by %q", u.name) } return v }
// EvalUnary evaluates a unary operator. func (c *Context) EvalUnary(op string, right value.Value) value.Value { right = right.Eval(c) fn := c.UnaryFn[op] if fn == nil { return value.Unary(c, op, right) } if fn.Body == nil { value.Errorf("unary %q undefined", op) } c.push() defer c.pop() c.assignLocal(fn.Right, right) var v value.Value for _, e := range fn.Body { v = e.Eval(c) } if v == nil { value.Errorf("no value returned by %q", op) } return v }
func (s sliceExpr) Eval(context value.Context) value.Value { v := make([]value.Value, len(s)) for i, x := range s { elem := x.Eval(context) // Each element must be a singleton. if !isScalar(elem) { value.Errorf("vector element must be scalar; have %s", elem) } v[i] = elem } return value.NewVector(v) }
func (b *binaryCall) Eval(context value.Context) value.Value { left := b.left.Eval(context) right := b.right.Eval(context) context.Push() defer context.Pop() exec := context.(*execContext) // Sigh. fn := exec.binaryFn[b.name] if fn == nil || fn.body == nil { value.Errorf("binary %q undefined", b.name) } context.AssignLocal(fn.left.name, left) context.AssignLocal(fn.right.name, right) var v value.Value for _, e := range fn.body { v = e.Eval(context) } if v == nil { value.Errorf("no value returned by %q", b.name) } return v }
// Assign assigns the variable the value. The variable must // be defined either in the current function or globally. // Inside a function, new variables become locals. func (c *Context) Assign(name string, val value.Value) { n := len(c.Stack) if n == 0 { value.Errorf("empty stack; cannot happen") } globals := c.Stack[0] if n > 1 { // In this function? frame := c.Stack[n-1] _, globallyDefined := globals[name] if _, ok := frame[name]; ok || !globallyDefined { frame[name] = val return } } // Assign global variable. c.noOp(name) globals[name] = val }
// put writes to out a version of the value that will recreate it when parsed. func put(out io.Writer, val value.Value) { switch val := val.(type) { case value.Char: fmt.Fprintf(out, "%q", rune(val)) case value.Int: fmt.Fprintf(out, "%d", int(val)) case value.BigInt: fmt.Fprintf(out, "%d", val.Int) case value.BigRat: fmt.Fprintf(out, "%d/%d", val.Num(), val.Denom()) case value.BigFloat: // TODO The actual value might not have the same prec as // the configuration, so we might not get this right // Probably not important but it would be nice to fix it. if val.Sign() == 0 || val.IsInf() { // These have prec 0 and are easy. // They shouldn't appear anyway, but be safe. fmt.Fprintf(out, "%g", val) return } digits := int(float64(val.Prec()) * 0.301029995664) // 10 log 2. fmt.Fprintf(out, "%.*g", digits, val.Float) case value.Vector: if val.AllChars() { fmt.Fprintf(out, "%q", val) return } for i, v := range val { if i > 0 { fmt.Fprint(out, " ") } put(out, v) } case value.Matrix: put(out, val.Shape()) fmt.Fprint(out, " rho ") put(out, val.Data()) default: value.Errorf("internal error: can't save type %T", val) } }
// save writes the state of the workspace to the named file. // The format of the output is ivy source text. func save(c *exec.Context, file string, conf *config.Config) { // "<conf.out>" is a special case for testing. out := conf.Output() if file != "<conf.out>" { fd, err := os.Create(file) if err != nil { value.Errorf("%s", err) } defer fd.Close() buf := bufio.NewWriter(fd) defer buf.Flush() out = buf } // Configuration settings. We will set the base below, // after we have printed all numbers in base 10. fmt.Fprintf(out, ")prec %d\n", conf.FloatPrec()) ibase, obase := conf.Base() fmt.Fprintf(out, ")maxbits %d\n", conf.MaxBits()) fmt.Fprintf(out, ")maxdigits %d\n", conf.MaxDigits()) fmt.Fprintf(out, ")origin %d\n", conf.Origin()) fmt.Fprintf(out, ")prompt %q\n", conf.Prompt()) fmt.Fprintf(out, ")format %q\n", conf.Format()) // Ops. printed := make(map[exec.OpDef]bool) for _, def := range c.Defs { var fn *exec.Function if def.IsBinary { fn = c.BinaryFn[def.Name] } else { fn = c.UnaryFn[def.Name] } for _, ref := range references(c, fn.Body) { if !printed[ref] { if ref.IsBinary { fmt.Fprintf(out, "op _ %s _\n", ref.Name) } else { fmt.Fprintf(out, "op %s _\n", ref.Name) } printed[ref] = true } } printed[def] = true fmt.Fprintln(out, fn) } // Global variables. syms := c.Stack[0] if len(syms) > 0 { // Set the base strictly to 10 for output. fmt.Fprintf(out, "# Set base 10 for parsing numbers.\n)base 10\n") // Sort the names for consistent output. sorted := sortSyms(syms) for _, sym := range sorted { // pi and e are generated if sym.name == "pi" || sym.name == "e" { continue } fmt.Fprintf(out, "%s = ", sym.name) put(out, sym.val) fmt.Fprint(out, "\n") } } // Now we can set the base. fmt.Fprintf(out, ")ibase %d\n", ibase) fmt.Fprintf(out, ")obase %d\n", obase) }
func (p *Parser) errorf(format string, args ...interface{}) { p.peekTok = scan.Token{Type: scan.EOF} value.Errorf(format, args...) }