cannam@133
|
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
cannam@133
|
2 // Licensed under the MIT License:
|
cannam@133
|
3 //
|
cannam@133
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
cannam@133
|
5 // of this software and associated documentation files (the "Software"), to deal
|
cannam@133
|
6 // in the Software without restriction, including without limitation the rights
|
cannam@133
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
cannam@133
|
8 // copies of the Software, and to permit persons to whom the Software is
|
cannam@133
|
9 // furnished to do so, subject to the following conditions:
|
cannam@133
|
10 //
|
cannam@133
|
11 // The above copyright notice and this permission notice shall be included in
|
cannam@133
|
12 // all copies or substantial portions of the Software.
|
cannam@133
|
13 //
|
cannam@133
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
cannam@133
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
cannam@133
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
cannam@133
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
cannam@133
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
cannam@133
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
cannam@133
|
20 // THE SOFTWARE.
|
cannam@133
|
21
|
cannam@133
|
22 #include "calculator.capnp.h"
|
cannam@133
|
23 #include <kj/debug.h>
|
cannam@133
|
24 #include <capnp/ez-rpc.h>
|
cannam@133
|
25 #include <capnp/message.h>
|
cannam@133
|
26 #include <iostream>
|
cannam@133
|
27
|
cannam@133
|
28 typedef unsigned int uint;
|
cannam@133
|
29
|
cannam@133
|
30 kj::Promise<double> readValue(Calculator::Value::Client value) {
|
cannam@133
|
31 // Helper function to asynchronously call read() on a Calculator::Value and
|
cannam@133
|
32 // return a promise for the result. (In the future, the generated code might
|
cannam@133
|
33 // include something like this automatically.)
|
cannam@133
|
34
|
cannam@133
|
35 return value.readRequest().send()
|
cannam@133
|
36 .then([](capnp::Response<Calculator::Value::ReadResults> result) {
|
cannam@133
|
37 return result.getValue();
|
cannam@133
|
38 });
|
cannam@133
|
39 }
|
cannam@133
|
40
|
cannam@133
|
41 kj::Promise<double> evaluateImpl(
|
cannam@133
|
42 Calculator::Expression::Reader expression,
|
cannam@133
|
43 capnp::List<double>::Reader params = capnp::List<double>::Reader()) {
|
cannam@133
|
44 // Implementation of CalculatorImpl::evaluate(), also shared by
|
cannam@133
|
45 // FunctionImpl::call(). In the latter case, `params` are the parameter
|
cannam@133
|
46 // values passed to the function; in the former case, `params` is just an
|
cannam@133
|
47 // empty list.
|
cannam@133
|
48
|
cannam@133
|
49 switch (expression.which()) {
|
cannam@133
|
50 case Calculator::Expression::LITERAL:
|
cannam@133
|
51 return expression.getLiteral();
|
cannam@133
|
52
|
cannam@133
|
53 case Calculator::Expression::PREVIOUS_RESULT:
|
cannam@133
|
54 return readValue(expression.getPreviousResult());
|
cannam@133
|
55
|
cannam@133
|
56 case Calculator::Expression::PARAMETER: {
|
cannam@133
|
57 KJ_REQUIRE(expression.getParameter() < params.size(),
|
cannam@133
|
58 "Parameter index out-of-range.");
|
cannam@133
|
59 return params[expression.getParameter()];
|
cannam@133
|
60 }
|
cannam@133
|
61
|
cannam@133
|
62 case Calculator::Expression::CALL: {
|
cannam@133
|
63 auto call = expression.getCall();
|
cannam@133
|
64 auto func = call.getFunction();
|
cannam@133
|
65
|
cannam@133
|
66 // Evaluate each parameter.
|
cannam@133
|
67 kj::Array<kj::Promise<double>> paramPromises =
|
cannam@133
|
68 KJ_MAP(param, call.getParams()) {
|
cannam@133
|
69 return evaluateImpl(param, params);
|
cannam@133
|
70 };
|
cannam@133
|
71
|
cannam@133
|
72 // Join the array of promises into a promise for an array.
|
cannam@133
|
73 kj::Promise<kj::Array<double>> joinedParams =
|
cannam@133
|
74 kj::joinPromises(kj::mv(paramPromises));
|
cannam@133
|
75
|
cannam@133
|
76 // When the parameters are complete, call the function.
|
cannam@133
|
77 return joinedParams.then([func](kj::Array<double>&& paramValues) mutable {
|
cannam@133
|
78 auto request = func.callRequest();
|
cannam@133
|
79 request.setParams(paramValues);
|
cannam@133
|
80 return request.send().then(
|
cannam@133
|
81 [](capnp::Response<Calculator::Function::CallResults>&& result) {
|
cannam@133
|
82 return result.getValue();
|
cannam@133
|
83 });
|
cannam@133
|
84 });
|
cannam@133
|
85 }
|
cannam@133
|
86
|
cannam@133
|
87 default:
|
cannam@133
|
88 // Throw an exception.
|
cannam@133
|
89 KJ_FAIL_REQUIRE("Unknown expression type.");
|
cannam@133
|
90 }
|
cannam@133
|
91 }
|
cannam@133
|
92
|
cannam@133
|
93 class ValueImpl final: public Calculator::Value::Server {
|
cannam@133
|
94 // Simple implementation of the Calculator.Value Cap'n Proto interface.
|
cannam@133
|
95
|
cannam@133
|
96 public:
|
cannam@133
|
97 ValueImpl(double value): value(value) {}
|
cannam@133
|
98
|
cannam@133
|
99 kj::Promise<void> read(ReadContext context) {
|
cannam@133
|
100 context.getResults().setValue(value);
|
cannam@133
|
101 return kj::READY_NOW;
|
cannam@133
|
102 }
|
cannam@133
|
103
|
cannam@133
|
104 private:
|
cannam@133
|
105 double value;
|
cannam@133
|
106 };
|
cannam@133
|
107
|
cannam@133
|
108 class FunctionImpl final: public Calculator::Function::Server {
|
cannam@133
|
109 // Implementation of the Calculator.Function Cap'n Proto interface, where the
|
cannam@133
|
110 // function is defined by a Calculator.Expression.
|
cannam@133
|
111
|
cannam@133
|
112 public:
|
cannam@133
|
113 FunctionImpl(uint paramCount, Calculator::Expression::Reader body)
|
cannam@133
|
114 : paramCount(paramCount) {
|
cannam@133
|
115 this->body.setRoot(body);
|
cannam@133
|
116 }
|
cannam@133
|
117
|
cannam@133
|
118 kj::Promise<void> call(CallContext context) {
|
cannam@133
|
119 auto params = context.getParams().getParams();
|
cannam@133
|
120 KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters.");
|
cannam@133
|
121
|
cannam@133
|
122 return evaluateImpl(body.getRoot<Calculator::Expression>(), params)
|
cannam@133
|
123 .then([context](double value) mutable {
|
cannam@133
|
124 context.getResults().setValue(value);
|
cannam@133
|
125 });
|
cannam@133
|
126 }
|
cannam@133
|
127
|
cannam@133
|
128 private:
|
cannam@133
|
129 uint paramCount;
|
cannam@133
|
130 // The function's arity.
|
cannam@133
|
131
|
cannam@133
|
132 capnp::MallocMessageBuilder body;
|
cannam@133
|
133 // Stores a permanent copy of the function body.
|
cannam@133
|
134 };
|
cannam@133
|
135
|
cannam@133
|
136 class OperatorImpl final: public Calculator::Function::Server {
|
cannam@133
|
137 // Implementation of the Calculator.Function Cap'n Proto interface, wrapping
|
cannam@133
|
138 // basic binary arithmetic operators.
|
cannam@133
|
139
|
cannam@133
|
140 public:
|
cannam@133
|
141 OperatorImpl(Calculator::Operator op): op(op) {}
|
cannam@133
|
142
|
cannam@133
|
143 kj::Promise<void> call(CallContext context) {
|
cannam@133
|
144 auto params = context.getParams().getParams();
|
cannam@133
|
145 KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
|
cannam@133
|
146
|
cannam@133
|
147 double result;
|
cannam@133
|
148 switch (op) {
|
cannam@133
|
149 case Calculator::Operator::ADD: result = params[0] + params[1]; break;
|
cannam@133
|
150 case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break;
|
cannam@133
|
151 case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break;
|
cannam@133
|
152 case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break;
|
cannam@133
|
153 default:
|
cannam@133
|
154 KJ_FAIL_REQUIRE("Unknown operator.");
|
cannam@133
|
155 }
|
cannam@133
|
156
|
cannam@133
|
157 context.getResults().setValue(result);
|
cannam@133
|
158 return kj::READY_NOW;
|
cannam@133
|
159 }
|
cannam@133
|
160
|
cannam@133
|
161 private:
|
cannam@133
|
162 Calculator::Operator op;
|
cannam@133
|
163 };
|
cannam@133
|
164
|
cannam@133
|
165 class CalculatorImpl final: public Calculator::Server {
|
cannam@133
|
166 // Implementation of the Calculator Cap'n Proto interface.
|
cannam@133
|
167
|
cannam@133
|
168 public:
|
cannam@133
|
169 kj::Promise<void> evaluate(EvaluateContext context) override {
|
cannam@133
|
170 return evaluateImpl(context.getParams().getExpression())
|
cannam@133
|
171 .then([context](double value) mutable {
|
cannam@133
|
172 context.getResults().setValue(kj::heap<ValueImpl>(value));
|
cannam@133
|
173 });
|
cannam@133
|
174 }
|
cannam@133
|
175
|
cannam@133
|
176 kj::Promise<void> defFunction(DefFunctionContext context) override {
|
cannam@133
|
177 auto params = context.getParams();
|
cannam@133
|
178 context.getResults().setFunc(kj::heap<FunctionImpl>(
|
cannam@133
|
179 params.getParamCount(), params.getBody()));
|
cannam@133
|
180 return kj::READY_NOW;
|
cannam@133
|
181 }
|
cannam@133
|
182
|
cannam@133
|
183 kj::Promise<void> getOperator(GetOperatorContext context) override {
|
cannam@133
|
184 context.getResults().setFunc(kj::heap<OperatorImpl>(
|
cannam@133
|
185 context.getParams().getOp()));
|
cannam@133
|
186 return kj::READY_NOW;
|
cannam@133
|
187 }
|
cannam@133
|
188 };
|
cannam@133
|
189
|
cannam@133
|
190 int main(int argc, const char* argv[]) {
|
cannam@133
|
191 if (argc != 2) {
|
cannam@133
|
192 std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n"
|
cannam@133
|
193 "Runs the server bound to the given address/port.\n"
|
cannam@133
|
194 "ADDRESS may be '*' to bind to all local addresses.\n"
|
cannam@133
|
195 ":PORT may be omitted to choose a port automatically." << std::endl;
|
cannam@133
|
196 return 1;
|
cannam@133
|
197 }
|
cannam@133
|
198
|
cannam@133
|
199 // Set up a server.
|
cannam@133
|
200 capnp::EzRpcServer server(kj::heap<CalculatorImpl>(), argv[1]);
|
cannam@133
|
201
|
cannam@133
|
202 // Write the port number to stdout, in case it was chosen automatically.
|
cannam@133
|
203 auto& waitScope = server.getWaitScope();
|
cannam@133
|
204 uint port = server.getPort().wait(waitScope);
|
cannam@133
|
205 if (port == 0) {
|
cannam@133
|
206 // The address format "unix:/path/to/socket" opens a unix domain socket,
|
cannam@133
|
207 // in which case the port will be zero.
|
cannam@133
|
208 std::cout << "Listening on Unix socket..." << std::endl;
|
cannam@133
|
209 } else {
|
cannam@133
|
210 std::cout << "Listening on port " << port << "..." << std::endl;
|
cannam@133
|
211 }
|
cannam@133
|
212
|
cannam@133
|
213 // Run forever, accepting connections and handling requests.
|
cannam@133
|
214 kj::NEVER_DONE.wait(waitScope);
|
cannam@133
|
215 }
|