Distributed programming: Difference between revisions
Content added Content deleted
(Added Wren) |
|||
Line 1,915: | Line 1,915: | ||
Now you can enter commands in the client terminal and get the output back through the same connection. |
Now you can enter commands in the client terminal and get the output back through the same connection. |
||
=={{header|Wren}}== |
|||
{{trans|Go}} |
|||
{{libheader|WrenGo}} |
|||
{{libheader|Wren-fmt}} |
|||
As Wren has no networking support at present, we use embedded programs for both the server and client with a Go host using the net/rpc package in its standard library. |
|||
Moreover, as Wren's VM is not re-entrant, we need to run two VMs from the server side, one to call Go from Wren and the other to call Wren from Go. |
|||
'''Server:''' |
|||
<br> |
|||
We need two Wren scripts one for each VM: |
|||
<lang ecmascript>/* distributed_programming_server.wren */ |
|||
class Rpc { |
|||
foreign static register() |
|||
foreign static handleHTTP() |
|||
} |
|||
foreign class Listener { |
|||
construct listen(network, address) {} |
|||
} |
|||
class HTTP { |
|||
foreign static serve(listener) |
|||
} |
|||
Rpc.register() |
|||
Rpc.handleHTTP() |
|||
var listener = Listener.listen("tcp", ":1234") |
|||
HTTP.serve(listener)</lang> |
|||
<br> |
|||
<lang ecmascript>/* distributed_programming_server2.wren */ |
|||
class TaxComputer { |
|||
static tax(amount, rate) { |
|||
if (amount < 0) Fiber.abort("Negative values not allowed.") |
|||
return amount * rate |
|||
} |
|||
}</lang> |
|||
<br> |
|||
We now embed these scripts in the following Go program and run it on one terminal. |
|||
<lang go>/* go run distributed_programming_server.go */ |
|||
package main |
|||
import( |
|||
wren "github.com/crazyinfin8/WrenGo" |
|||
"log" |
|||
"net" |
|||
"net/http" |
|||
"net/rpc" |
|||
) |
|||
type any = interface{} |
|||
type TaxComputer float64 |
|||
var vm2 *wren.VM |
|||
var fileName = "distributed_programming_server.wren" |
|||
var fileName2 = "distributed_programming_server2.wren" |
|||
func (taxRate TaxComputer) Tax(x float64, r *float64) error { |
|||
wrenVar, _ := vm2.GetVariable(fileName2, "TaxComputer") |
|||
wrenClass, _ := wrenVar.(*wren.Handle) |
|||
defer wrenClass.Free() |
|||
wrenMethod, _ := wrenClass.Func("tax(_,_)") |
|||
defer wrenMethod.Free() |
|||
ret, _ := wrenMethod.Call(x, float64(taxRate)) |
|||
*r = ret.(float64) |
|||
return nil |
|||
} |
|||
func register(vm *wren.VM, parameters []any) (any, error) { |
|||
c := TaxComputer(0.05) // 5% tax rate |
|||
rpc.Register(c) |
|||
return nil, nil |
|||
} |
|||
func handleHTTP(vm *wren.VM, parameters []any) (any, error) { |
|||
rpc.HandleHTTP() |
|||
return nil, nil |
|||
} |
|||
func serve(vm *wren.VM, parameters []any) (any, error) { |
|||
handle := parameters[1].(*wren.ForeignHandle) |
|||
ifc, _ := handle.Get() |
|||
listener := ifc.(*net.Listener) |
|||
http.Serve(*listener, nil) |
|||
return nil, nil |
|||
} |
|||
func listen(vm *wren.VM, parameters []any) (any, error) { |
|||
network := parameters[1].(string) |
|||
address := parameters[2].(string) |
|||
listener, err := net.Listen(network, address) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
return &listener, nil |
|||
} |
|||
func main() { |
|||
vm := wren.NewVM() |
|||
vm2 = wren.NewVM() |
|||
vm2.InterpretFile(fileName2) |
|||
rpcMethodMap := wren.MethodMap { |
|||
"static register()": register, |
|||
"static handleHTTP()": handleHTTP, |
|||
} |
|||
httpMethodMap := wren.MethodMap { "static serve(_)":serve } |
|||
classMap := wren.ClassMap { |
|||
"Listener": wren.NewClass(listen, nil, nil), |
|||
"Rpc" : wren.NewClass(nil, nil, rpcMethodMap), |
|||
"HTTP" : wren.NewClass(nil, nil, httpMethodMap), |
|||
} |
|||
module := wren.NewModule(classMap) |
|||
vm.SetModule(fileName, module) |
|||
vm.InterpretFile(fileName) |
|||
vm.Free() |
|||
vm2.Free() |
|||
}</lang> |
|||
<br> |
|||
'''Client:''' |
|||
<br> |
|||
Just one Wren script needed here: |
|||
<lang ecmascript>/* distributed_programming_client.wren */ |
|||
import "./fmt" for Fmt |
|||
foreign class Client { |
|||
construct dialHTTP(network, address) {} |
|||
foreign call(serviceMethod, arg) |
|||
} |
|||
var client = Client.dialHTTP("tcp", "localhost:1234") |
|||
var amounts = [3, 5.6] |
|||
for (amount in amounts) { |
|||
var tax = client.call("TaxComputer.Tax", amount) |
|||
Fmt.print("Tax on $0.2f = $0.2f", amount, tax) |
|||
}</lang> |
|||
<br> |
|||
which we embed in the following Go program and run it on a different terminal. |
|||
<lang go>/* go run distributed_programming_client.go */ |
|||
package main |
|||
import( |
|||
wren "github.com/crazyinfin8/WrenGo" |
|||
"log" |
|||
"net/rpc" |
|||
"strings" |
|||
) |
|||
type any = interface{} |
|||
func dialHTTP(vm *wren.VM, parameters []any) (any, error) { |
|||
network := parameters[1].(string) |
|||
address := parameters[2].(string) |
|||
client, err := rpc.DialHTTP(network, address) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
return &client, nil |
|||
} |
|||
func call(vm *wren.VM, parameters []any) (any, error) { |
|||
handle := parameters[0].(*wren.ForeignHandle) |
|||
ifc, _ := handle.Get() |
|||
client := ifc.(**rpc.Client) |
|||
serviceMethod := parameters[1].(string) |
|||
amount := parameters[2].(float64) |
|||
var tax float64 |
|||
err := (*client).Call(serviceMethod, amount, &tax) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
return tax, nil |
|||
} |
|||
func moduleFn(vm *wren.VM, name string) (string, bool) { |
|||
if name != "meta" && name != "random" && !strings.HasSuffix(name, ".wren") { |
|||
name += ".wren" |
|||
} |
|||
return wren.DefaultModuleLoader(vm, name) |
|||
} |
|||
func main() { |
|||
cfg := wren.NewConfig() |
|||
cfg.LoadModuleFn = moduleFn |
|||
vm := cfg.NewVM() |
|||
fileName := "distributed_programming_client.wren" |
|||
clientMethodMap := wren.MethodMap { "call(_,_)": call } |
|||
classMap := wren.ClassMap { "Client": wren.NewClass(dialHTTP, nil, clientMethodMap) } |
|||
module := wren.NewModule(classMap) |
|||
vm.SetModule(fileName, module) |
|||
vm.InterpretFile(fileName) |
|||
vm.Free() |
|||
}</lang> |
|||
{{out}} |
|||
Output on the client terminal: |
|||
<pre> |
|||
Tax on 3.00 = 0.15 |
|||
Tax on 5.60 = 0.28 |
|||
</pre> |
|||
{{omit from|Lotus 123 Macro Scripting}} |
{{omit from|Lotus 123 Macro Scripting}} |