package muesli import "runtime" import "strings" import "sort" type LogTracer struct { } func (t LogTracer) Trace(vm VM, fmt string, args ... interface{}) bool { args = append([]interface{}{vm.BackTrace()}, args...) vm.Log("TRACE", "builtin.go", 12, "%s: " + vm.BackTrace() + fmt, args...) return false } type FmtTracer struct { } func (t FmtTracer) Trace(vm VM, fm string, args ... interface{}) bool { args = append([]interface{}{vm.BackTrace()}, args...) vm.Printf("%s: " + fm + "\n", args...) return false } func printf(vm *VM, args ...Value) []Value { var format string rest, err := ParseArgs(args, &format) if err != nil { return Fail(err) } extra := ListFromList(rest) vm.Printf(format, extra...) return None() } func println(vm *VM, args ...Value) []Value { var msg string _, err := ParseArgs(args, &msg) if err != nil { return Fail(err) } else { vm.Println(msg) } return None() } func p(vm *VM, args ...Value) []Value { for _, arg := range args { vm.Printf("%v\n", arg) } return None() } func trace(vm *VM, args ...Value) []Value { var b bool = true vm.Printf("command trace: %v\n", args) _, err := ParseArgs(args, &b) if err != nil { vm.Printf("Error: %s\n", err.Error()) return Fail(err) } vm.Printf("command trace: bool: %v\n", b) if b { vm.Tracer = vm.Console // FmtTracer{} } else { vm.Tracer = nil } return Ok(BoolValue(b)) } func sumi(vm *VM, args ...Value) []Value { slice, err := ParseArgsToIntSlice(args) if err != nil { vm.Printf("Error: %s\n", err.Error()) return Fail(err) } res := int(0) for _, val := range slice { res += val } return IntOk(res) } func sumf(vm *VM, args ...Value) []Value { slice, err := ParseArgsToFloat64Slice(args) if err != nil { vm.Printf("Error: %s\n", err.Error()) return Fail(err) } res := float64(0) for _, val := range slice { res += val } return FloatOk(res) } func addi(vm *VM, args ...Value) []Value { var v1, v2 int ParseArgs(args, &v1, &v2) return IntOk(v1 + v2) } func addf(vm *VM, args ...Value) []Value { var v1, v2 float64 _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return FloatOk(v1 + v2) } func subi(vm *VM, args ...Value) []Value { var v1, v2 int _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return IntOk(v1 - v2) } func subf(vm *VM, args ...Value) []Value { var v1, v2 float64 _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return FloatOk(v1 - v2) } func muli(vm *VM, args ...Value) []Value { var v1, v2 int _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return IntOk(v1 * v2) } func mulf(vm *VM, args ...Value) []Value { var v1, v2 float64 _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return FloatOk(v1 * v2) } func divi(vm *VM, args ...Value) []Value { var v1, v2 int _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return IntOk(v1 / v2) } func divf(vm *VM, args ...Value) []Value { var v1, v2 float64 _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return FloatOk(v1 / v2) } func andb(vm * VM, args ...Value) []Value { var v1, v2 bool _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return BoolOk(v1 && v2) } func orb(vm * VM, args ...Value) []Value { var v1, v2 bool _, err := ParseArgs(args, &v1, &v2) if err != nil { return Fail(err) } return BoolOk(v1 || v2) } func val(vm *VM, args ...Value) []Value { if len(args) < 1 { return []Value{NewErrorValuef("val requres at least one argument.")} } return args } func builtin_return(vm *VM, args ...Value) []Value { vm.Frame.returned = true vm.Frame.results = args vm.Trace("Returning... %v", vm.Frame) return args } func to(vm *VM, args ...Value) []Value { var name string rest, err := ParseArgs(args, &name) if err != nil { return Fail(err) } if len(rest) < 1 { return Fail(NewErrorValuef("Need at least 2 arguments: %v", args)) } last := rest[len(rest)-1] block, isBlock := last.(*BlockValue) if ! isBlock { return Fail(NewErrorValuef("Not a block: %v", last)) } param := args[1:len(args)-1] sign, err := NewSignatureWithNames(param...) if err != nil { return Fail(NewErrorValuef("Could not parse arguments: %v: %s", name, err)) } // To must register one level up. return Ok(vm.RegisterDefined(name, sign, block, 1)) } func cover(vm *VM, args ...Value) []Value { var name, target string rest, err := ParseArgs(args, &name, &target) if err != nil { return Fail(err) } types := []TypeValue{} for i, arg := range rest { if typ, ok := arg.(TypeValue) ; ok { types = append(types, typ) } else { return Fail(NewErrorValuef("Argument %d: not a type: %v %s", i+2, arg, arg.String())) } } // Overload must be defined one scope up. err = vm.AddOverload(name, target, 1, types...) if err != nil { return Fail(err) } return Ok() } func types(vm *VM, args ...Value) []Value { result := []Value{} for _, arg := range args { typ := arg.Type() result = append(result, typ) } return Ok(NewListValue(result...)) } func set(vm *VM, val ...Value) []Value { if len(val) < 2 { return Fail(vm.Errorf("set needs at least 2 arguments: received %v", val)) } target := val[0].String() value := val[1] if target == "(" || target == "$" { if len(val) < 3 { return Fail(vm.Errorf("indirect get needs results: received %v", val)) } target = val[1].String() value = val[2] } level := 1 if len(val) > 2 { upval := val[2] if upval.Type() == IntType { upval.Convert(&level) } else if upval.String() == "top" { vm.RegisterTop(target, value) return Ok(value) } } vm.RegisterUp(target, value, level) return Ok(value) } func get(vm *VM, val ...Value) []Value { if len(val) < 1 { return Fail(vm.Errorf("get needs at least 1 argument")) } target := val[0].String() if target == "(" || target == "$" { if len(val) < 2 { return Fail(vm.Errorf("indirect get needs results: received %v", val)) } target = val[1].String() } return Ok(vm.Lookup(target)) } func addl(vm *VM, args ...Value) []Value { var value Value var list *ListValue _, err := ParseArgs(args, &list, &value) if err != nil { return Fail(err) } list.Append(value) return Ok(list) } func fetchl(vm *VM, args ...Value) []Value { var index int var list *ListValue _, err := ParseArgs(args, &list, &index) if err != nil { return Fail(err) } if (index < 0) || (index >= len(list.List)) { return Fail(vm.Errorf("index out of range: %d<->%d", index, len(list.List))) } return Ok(list.List[index]) } func storel(vm *VM, args ...Value) []Value { var index int var list *ListValue rest, err := ParseArgs(args, &list, &index) if err != nil { return Fail(err) } if len(rest) < 1 { return Fail(vm.Errorf("fetch: need 3 arguments")) } if (index < 0) || (index >= len(list.List)) { return Fail(vm.Errorf("index out of range: %d<->%d", index, len(list.List))) } list.List[index] = rest[0] return Ok(list.List[index]) } func fetchm(vm *VM, args ...Value) []Value { var index Value var hmap *MapValue _, err := ParseArgs(args, &hmap, &index) if err != nil { return Fail(err) } return Ok(hmap.Map[index]) } func storem(vm *VM, args ...Value) []Value { var index Value var hmap *MapValue rest, err := ParseArgs(args, &hmap, &index) if err != nil { return Fail(err) } if len (rest) < 1 { return Fail(vm.Errorf("fetch: need 3 arguments")) } hmap.Map[index] = rest[0] return Ok(hmap.Map[index]) } func newmap(vm *VM, args ...Value) []Value { result := make(map[Value] Value) for i := 1; i < len(args) ; i+=2 { result[args[i-1]] = args[i] } return Ok(NewMapValue(result)) } func help(vm *VM, val ...Value) [] Value { if len(val) < 1 { vm.Printf("help will display help on the callable\n\nThe following commands are available:\n") helpers := vm.DefinedHelpers() sort.SliceStable(helpers, func(i, j int) bool { return helpers[i].HelperName() < helpers[j].HelperName() }) for _, helper := range helpers { fl := strings.SplitN(helper.Help(),"\n", 2) vm.Printf("%s %s: \n", helper.HelperName(), fl[0]) } return Ok() } targetName := val[0].String() target := vm.Lookup(targetName) if target == nil { vm.Printf("help: %s not found.\n", targetName) return Ok() } if helper, isHelper := target.(Helper) ; isHelper { if len(val) > 1 { helper.SetHelp(val[1].String()) } help := helper.Help() if call, isCall := target.(Callable); isCall { vm.Printf("%s %s: %s.\n", targetName, call.Signature().String(), help) } else { vm.Printf("%s: %s.\n", targetName, help) } return Ok(StringValue(help)) } else { vm.Printf("%s %s. No help available.\n", targetName, target.Type()) } return Ok() } func explain(vm *VM, val ...Value) [] Value { var target, help string _, err := ParseArgs(val, &target, &help) if err != nil { return Fail(err) } err = vm.SetHelp(target, help) if err != nil { return Fail(err) } return Ok(StringValue(help)) } func exit(vm *VM, val ...Value) [] Value { var code int _, err := ParseArgs(val, &code) if err != nil { runtime.Goexit() } vm.ExitStatus = code runtime.Goexit() return Ok() } func comma(vm * VM, val ...Value) []Value { vm.Printf("Comma arguments: %v\n", val) if len(val) < 2 { return Fail(vm.Errorf("Need at least 2 arguments")) } target := val[0] command := val[1] val[1] = target val = val[1:] if call, isCall := command.(Callable); isCall { return vm.CallCallable(call, val...) } else if name, isName := command.(WordValue) ; isName { return vm.CallNamed(name.String(), val...) } else { return Fail(vm.Errorf("Not callable: %v", command)) } } // Get an object field func of(vm * VM, val ... Value) []Value { var object Value var name string _, err := ParseArgs(val, &object, &name) if err != nil { return Fail(err) } target, ok := object.(Object) if !ok { return Fail(vm.Errorf("Target cannot be invoked: %s %v", name, object)) } return Ok(target.Get(name)) } // Set an object field func _make(vm * VM, val ... Value) []Value { var object Value var name string rest, err := ParseArgs(val, &object, &name) if err != nil { return Fail(err) } target, ok := object.(Object) if !ok { return Fail(vm.Errorf("Target cannot be invoked: %s %v", name, object)) } return Ok(target.Set(name, rest[0])) } // Call an object method func call(vm * VM, val ... Value) []Value { var object Value var name string rest, err := ParseArgs(val, &object, &name) if err != nil { return Fail(err) } target, ok := object.(Object) if !ok { return Fail(vm.Errorf("Target cannot be invoked: %s %v", name, object)) } return target.Invoke(name, rest...) } func (vm *VM) RegisterBuiltinTypes() { vm.RegisterTop("Int", IntType) vm.RegisterTop("Float", FloatType) vm.RegisterTop("String",StringType) vm.RegisterTop("Bool", BoolType) vm.RegisterTop("Word", WordType) vm.RegisterTop("Error", ErrorType) vm.RegisterTop("Type", TypeType) vm.RegisterTop("Empty", EmptyType) vm.RegisterTop("List", ListType) vm.RegisterTop("Map", MapType) vm.RegisterTop("Any", AnyType) vm.RegisterTop("ZeroType", ZeroType) } func (vm *VM) RegisterBuiltins() { vm.RegisterBuiltinTypes() vm.RegisterBuiltinWithHelp("addl", addl, "adds an element to a list").Takes(ListType, AnyType).Returns(ListType) vm.RegisterBuiltinWithHelp("addi", addi, `adds two integers together`).Takes(IntType, IntType).Returns(BoolType) vm.RegisterBuiltinWithHelp("addf", addf, `adds two floats together`).Takes(FloatType, FloatType).Returns(FloatType) vm.RegisterBuiltinWithHelp("andb", andb, `returns true if all it's arguments are true`).Takes(BoolType, BoolType).Returns(BoolType) vm.RegisterBuiltin(",", comma) vm.RegisterBuiltin("cover", cover) vm.RegisterBuiltin("fetchl", fetchl).Takes(ListType, IntType).Returns(AnyType) vm.RegisterBuiltin("fetchm", fetchm).Takes(MapType, AnyType).Returns(AnyType) vm.RegisterBuiltin("sumi", sumi).Takes(IntType, IntType).Returns(IntType) vm.RegisterBuiltin("sumf", sumf).Takes(FloatType, FloatType).Returns(FloatType) vm.RegisterBuiltin("subi", subi).Takes(IntType, IntType).Returns(IntType) vm.RegisterBuiltin("subf", subf).Takes(FloatType, FloatType).Returns(FloatType) vm.RegisterBuiltin("divi", divi).Takes(IntType, IntType).Returns(IntType) vm.RegisterBuiltin("divf", divf).Takes(FloatType, FloatType).Returns(FloatType) vm.RegisterBuiltin("map", newmap) vm.RegisterBuiltin("muli", muli).Takes(IntType, IntType).Returns(IntType) vm.RegisterBuiltin("mulf", mulf).Takes(FloatType, FloatType).Returns(FloatType) vm.RegisterBuiltinWithHelp("orb", orb, `[Bool Bool] -> Bool: returns true if on of it's arguments is true`) // vm.RegisterCover("add") vm.RegisterBuiltin("p", p) vm.RegisterBuiltin("println", println) vm.RegisterBuiltin("printf", printf) vm.RegisterBuiltin("storel", storel).Takes(ListType, IntType).Returns(AnyType) vm.RegisterBuiltin("storem", storem).Takes(MapType, AnyType).Returns(AnyType) vm.RegisterBuiltin("trace", trace) vm.RegisterBuiltin("to", to) vm.RegisterBuiltin("types", types) vm.RegisterBuiltin("return", builtin_return) vm.RegisterBuiltin("val", val) vm.RegisterBuiltin("set", set) vm.RegisterBuiltin("get", get) vm.RegisterBuiltin("help", help) vm.RegisterBuiltin("explain", explain) vm.RegisterBuiltin("exit", exit) vm.AddOverloads("mul", Over("mulf", 0, FloatType, FloatType), Over("muli", 0, IntType, IntType), Over("mulf", 0, FloatType, IntType), Over("mulf", 0, IntType, FloatType)) vm.AddOverloads("add", Over("addf", 0, FloatType, FloatType), Over("addi", 0, IntType, IntType), Over("addf", 0, FloatType, IntType), Over("addf", 0, IntType, FloatType)) vm.AddOverloads("div", Over("divf", 0, FloatType, FloatType), Over("divi", 0, IntType, IntType), Over("divf", 0, FloatType, IntType), Over("divf", 0, IntType, FloatType)) vm.AddOverloads("sub", Over("subf", 0, FloatType, FloatType), Over("subi", 0, IntType, IntType), Over("subf", 0, FloatType, IntType), Over("subf", 0, IntType, FloatType)) vm.RegisterBuiltinWithHelp("of", of, "Gets a field in an Object value").Takes(AnyType, StringType).Returns(AnyType) vm.RegisterBuiltinWithHelp("make", _make, "Sets a field in an Object value").Takes(AnyType, StringType, AnyType).Returns(AnyType) vm.RegisterBuiltinWithHelp("call", call, "Calls a method of an Object value").Takes(AnyType, StringType, AnyType).Returns(AnyType) vm.Alias("*", "mul") vm.Alias("+", "add") vm.Alias("/", "div") vm.Alias("-", "sub") vm.Alias("||", "orb") vm.Alias("&&", "andb") vm.Alias("'s", "of") vm.SetHelp("mul", " Num: Multiplies two numbers. Cover for muli and mulf.") vm.AddOverloads("fetch", Over("fetchl", 0, ListType, IntType), Over("fetchm", 0, MapType, AnyType), ) vm.SetHelp("fetch", " storage, index. Fetch value in storage at given index.") /* vm.AddOverloads("store", Over("storel", ListType, IntType, AnyType), Over("storem", MapType, AnyType, AnyType), ) vm.SetHelp("store", " storage, index, value. Store value in storage at given index.") */ RegisterDoor(vm) }