package azalea import ( "errors" "fmt" "maps" "reflect" "unique" ) // Value are types supported by the language. type Value interface { bool | int64 | string | []string | [][2]string } type ( // FArg is an argument passed to [F]. FArg struct { K unique.Handle[Ident] V any R bool } // FArgs are arguments passed to [F]. FArgs []FArg // F is the implementation of a [Func]. F struct { F func(isPackage bool, args FArgs) (v any, set bool, err error) V map[unique.Handle[Ident]]any } ) // Apply applies named arguments and rejects unused arguments. func (args FArgs) Apply(v map[unique.Handle[Ident]]any) error { for _, arg := range args { if arg.V == nil { // unset continue } r, ok := v[arg.K] if !ok { if arg.R { continue } return UndefinedError(arg.K.Value()) } err := storeE(r, arg.V) if err != nil { return err } } return nil } // A Frame refers to local variables and debugging information. type Frame struct { // Local constants. Val map[unique.Handle[Ident]]any // Functions. Func map[unique.Handle[Ident]]F } // UnsupportedExprError is an expression with invalid concrete type. type UnsupportedExprError struct{ E any } func (e UnsupportedExprError) Error() string { return fmt.Sprintf("unsupported expression %#v", e.E) } // UndefinedError is an identifier not defined in any stack frame visible to the // expression containing it. type UndefinedError Ident func (e UndefinedError) Error() string { return "undefined: " + string(e) } // evaluate is evaluateAny with a type parameter. func evaluate[T Value](s []Frame, expr any, rp *T) bool { return evaluateAny(s, expr, rp) } // TypeError is an unexpected type during evaluation. type TypeError struct { Concrete, Asserted reflect.Type } func (e TypeError) Error() string { return "expected " + e.Asserted.String() + ", got " + e.Concrete.String() } func (e TypeError) Is(err error) bool { var v TypeError return errors.As(err, &v) && e.Asserted == v.Asserted && e.Concrete == v.Concrete } // storeE is a convenience function to set the value of a result pointer. func storeE(rp any, r any) error { pv := reflect.ValueOf(rp).Elem() v := reflect.ValueOf(r) pt, vt := pv.Type(), v.Type() if !vt.AssignableTo(pt) { return TypeError{vt, pt} } pv.Set(v) return nil } // store is like storeE, but panics if error is non-nil. func store[T Value](rp any, r T) { err := storeE(rp, r) if err != nil { panic(err) } } // EvaluationError is an error and the expression it occurred in. type EvaluationError struct { Expr any Err error } // Unwrap returns the underlying error. func (e EvaluationError) Unwrap() error { return e.Err } // Error returns a very long error description that should not be presented // to the user directly. func (e EvaluationError) Error() string { return fmt.Sprintf("expression %#v: %v", e.Expr, e.Err) } // evaluateAny implements [Evaluate]. func evaluateAny(s []Frame, expr, rp any) bool { defer func() { r := recover() if r == nil { return } err, ok := r.(error) if !ok { panic(r) } if _, ok = err.(EvaluationError); ok { panic(err) } panic(EvaluationError{expr, err}) }() switch e := expr.(type) { case Int: store(rp, int64(e)) return true case String: store(rp, string(e)) return true case Ident: var ( v any ok bool ) for i := range s { v, ok = s[len(s)-1-i].Val[unique.Make(e)] if ok { break } } if !ok { panic(UndefinedError(e)) } if err := storeE(rp, v); err != nil { panic(err) } return true case Val: if len(e) == 1 { switch v := e[0].(type) { case Ident: switch v { case "unset": return false case "true": store(rp, true) return true case "false": store(rp, false) return true default: return evaluateAny(s, v, rp) } default: return evaluateAny(s, e[0], rp) } } var v string for i := range e { var _r string if evaluate(s, e[i], &_r) { v += _r } } store(rp, v) return true case Array: r := make([]string, 0, len(e)) for i := range e { var _r string if evaluate(s, e[i], &_r) { r = append(r, _r) } } store(rp, r) return true case []KV: r := make([][2]string, 0, len(e)) for i := range e { var _r string if e[i].V == nil || evaluate(s, e[i].V, &_r) { r = append(r, [2]string{string(e[i].K), _r}) } } store(rp, r) return true case Func: var ( f F ok bool ) for i := range s { f, ok = s[len(s)-1-i].Func[unique.Make(e.Ident)] if ok { break } } if !ok { panic(UndefinedError(e.Ident)) } argc := len(e.Args) for _, arg := range e.Args { argc += len(arg.K) - 1 } fargs := make([]FArg, 0, len(e.Args)) s = append(s, Frame{Val: maps.Clone(f.V)}) fp := &s[len(s)-1] for _, arg := range e.Args { farg := FArg{R: arg.R} if !evaluateAny(s, arg.V, &farg.V) { farg.V = nil } for _, name := range arg.K { h := unique.Make(name) farg.K = h fargs = append(fargs, farg) if arg.R && farg.V != nil { if fp.Val == nil { fp.Val = make(map[unique.Handle[Ident]]any) } (*fp).Val[h] = farg.V } } } v, set, err := f.F(e.Package, fargs) if err != nil { panic(err) } else if v != nil { if err = storeE(rp, v); err != nil { panic(err) } } return set default: panic(UnsupportedExprError{expr}) } } // Evaluate evaluates a statement and returns its value. func Evaluate[T Value](s []Frame, expr any) (v T, set bool, err error) { defer func() { r := recover() if r == nil { return } _err, ok := r.(error) if !ok { panic(r) } err = _err }() set = evaluate[T](s, expr, &v) return }