cannam@62: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@62: // Licensed under the MIT License: cannam@62: // cannam@62: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@62: // of this software and associated documentation files (the "Software"), to deal cannam@62: // in the Software without restriction, including without limitation the rights cannam@62: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@62: // copies of the Software, and to permit persons to whom the Software is cannam@62: // furnished to do so, subject to the following conditions: cannam@62: // cannam@62: // The above copyright notice and this permission notice shall be included in cannam@62: // all copies or substantial portions of the Software. cannam@62: // cannam@62: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@62: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@62: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@62: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@62: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@62: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@62: // THE SOFTWARE. cannam@62: cannam@62: #include "calculator.capnp.h" cannam@62: #include cannam@62: #include cannam@62: #include cannam@62: #include cannam@62: cannam@62: typedef unsigned int uint; cannam@62: cannam@62: kj::Promise readValue(Calculator::Value::Client value) { cannam@62: // Helper function to asynchronously call read() on a Calculator::Value and cannam@62: // return a promise for the result. (In the future, the generated code might cannam@62: // include something like this automatically.) cannam@62: cannam@62: return value.readRequest().send() cannam@62: .then([](capnp::Response result) { cannam@62: return result.getValue(); cannam@62: }); cannam@62: } cannam@62: cannam@62: kj::Promise evaluateImpl( cannam@62: Calculator::Expression::Reader expression, cannam@62: capnp::List::Reader params = capnp::List::Reader()) { cannam@62: // Implementation of CalculatorImpl::evaluate(), also shared by cannam@62: // FunctionImpl::call(). In the latter case, `params` are the parameter cannam@62: // values passed to the function; in the former case, `params` is just an cannam@62: // empty list. cannam@62: cannam@62: switch (expression.which()) { cannam@62: case Calculator::Expression::LITERAL: cannam@62: return expression.getLiteral(); cannam@62: cannam@62: case Calculator::Expression::PREVIOUS_RESULT: cannam@62: return readValue(expression.getPreviousResult()); cannam@62: cannam@62: case Calculator::Expression::PARAMETER: { cannam@62: KJ_REQUIRE(expression.getParameter() < params.size(), cannam@62: "Parameter index out-of-range."); cannam@62: return params[expression.getParameter()]; cannam@62: } cannam@62: cannam@62: case Calculator::Expression::CALL: { cannam@62: auto call = expression.getCall(); cannam@62: auto func = call.getFunction(); cannam@62: cannam@62: // Evaluate each parameter. cannam@62: kj::Array> paramPromises = cannam@62: KJ_MAP(param, call.getParams()) { cannam@62: return evaluateImpl(param, params); cannam@62: }; cannam@62: cannam@62: // Join the array of promises into a promise for an array. cannam@62: kj::Promise> joinedParams = cannam@62: kj::joinPromises(kj::mv(paramPromises)); cannam@62: cannam@62: // When the parameters are complete, call the function. cannam@62: return joinedParams.then([KJ_CPCAP(func)](kj::Array&& paramValues) mutable { cannam@62: auto request = func.callRequest(); cannam@62: request.setParams(paramValues); cannam@62: return request.send().then( cannam@62: [](capnp::Response&& result) { cannam@62: return result.getValue(); cannam@62: }); cannam@62: }); cannam@62: } cannam@62: cannam@62: default: cannam@62: // Throw an exception. cannam@62: KJ_FAIL_REQUIRE("Unknown expression type."); cannam@62: } cannam@62: } cannam@62: cannam@62: class ValueImpl final: public Calculator::Value::Server { cannam@62: // Simple implementation of the Calculator.Value Cap'n Proto interface. cannam@62: cannam@62: public: cannam@62: ValueImpl(double value): value(value) {} cannam@62: cannam@62: kj::Promise read(ReadContext context) { cannam@62: context.getResults().setValue(value); cannam@62: return kj::READY_NOW; cannam@62: } cannam@62: cannam@62: private: cannam@62: double value; cannam@62: }; cannam@62: cannam@62: class FunctionImpl final: public Calculator::Function::Server { cannam@62: // Implementation of the Calculator.Function Cap'n Proto interface, where the cannam@62: // function is defined by a Calculator.Expression. cannam@62: cannam@62: public: cannam@62: FunctionImpl(uint paramCount, Calculator::Expression::Reader body) cannam@62: : paramCount(paramCount) { cannam@62: this->body.setRoot(body); cannam@62: } cannam@62: cannam@62: kj::Promise call(CallContext context) { cannam@62: auto params = context.getParams().getParams(); cannam@62: KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters."); cannam@62: cannam@62: return evaluateImpl(body.getRoot(), params) cannam@62: .then([KJ_CPCAP(context)](double value) mutable { cannam@62: context.getResults().setValue(value); cannam@62: }); cannam@62: } cannam@62: cannam@62: private: cannam@62: uint paramCount; cannam@62: // The function's arity. cannam@62: cannam@62: capnp::MallocMessageBuilder body; cannam@62: // Stores a permanent copy of the function body. cannam@62: }; cannam@62: cannam@62: class OperatorImpl final: public Calculator::Function::Server { cannam@62: // Implementation of the Calculator.Function Cap'n Proto interface, wrapping cannam@62: // basic binary arithmetic operators. cannam@62: cannam@62: public: cannam@62: OperatorImpl(Calculator::Operator op): op(op) {} cannam@62: cannam@62: kj::Promise call(CallContext context) { cannam@62: auto params = context.getParams().getParams(); cannam@62: KJ_REQUIRE(params.size() == 2, "Wrong number of parameters."); cannam@62: cannam@62: double result; cannam@62: switch (op) { cannam@62: case Calculator::Operator::ADD: result = params[0] + params[1]; break; cannam@62: case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break; cannam@62: case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break; cannam@62: case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break; cannam@62: default: cannam@62: KJ_FAIL_REQUIRE("Unknown operator."); cannam@62: } cannam@62: cannam@62: context.getResults().setValue(result); cannam@62: return kj::READY_NOW; cannam@62: } cannam@62: cannam@62: private: cannam@62: Calculator::Operator op; cannam@62: }; cannam@62: cannam@62: class CalculatorImpl final: public Calculator::Server { cannam@62: // Implementation of the Calculator Cap'n Proto interface. cannam@62: cannam@62: public: cannam@62: kj::Promise evaluate(EvaluateContext context) override { cannam@62: return evaluateImpl(context.getParams().getExpression()) cannam@62: .then([KJ_CPCAP(context)](double value) mutable { cannam@62: context.getResults().setValue(kj::heap(value)); cannam@62: }); cannam@62: } cannam@62: cannam@62: kj::Promise defFunction(DefFunctionContext context) override { cannam@62: auto params = context.getParams(); cannam@62: context.getResults().setFunc(kj::heap( cannam@62: params.getParamCount(), params.getBody())); cannam@62: return kj::READY_NOW; cannam@62: } cannam@62: cannam@62: kj::Promise getOperator(GetOperatorContext context) override { cannam@62: context.getResults().setFunc(kj::heap( cannam@62: context.getParams().getOp())); cannam@62: return kj::READY_NOW; cannam@62: } cannam@62: }; cannam@62: cannam@62: int main(int argc, const char* argv[]) { cannam@62: if (argc != 2) { cannam@62: std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n" cannam@62: "Runs the server bound to the given address/port.\n" cannam@62: "ADDRESS may be '*' to bind to all local addresses.\n" cannam@62: ":PORT may be omitted to choose a port automatically." << std::endl; cannam@62: return 1; cannam@62: } cannam@62: cannam@62: // Set up a server. cannam@62: capnp::EzRpcServer server(kj::heap(), argv[1]); cannam@62: cannam@62: // Write the port number to stdout, in case it was chosen automatically. cannam@62: auto& waitScope = server.getWaitScope(); cannam@62: uint port = server.getPort().wait(waitScope); cannam@62: if (port == 0) { cannam@62: // The address format "unix:/path/to/socket" opens a unix domain socket, cannam@62: // in which case the port will be zero. cannam@62: std::cout << "Listening on Unix socket..." << std::endl; cannam@62: } else { cannam@62: std::cout << "Listening on port " << port << "..." << std::endl; cannam@62: } cannam@62: cannam@62: // Run forever, accepting connections and handling requests. cannam@62: kj::NEVER_DONE.wait(waitScope); cannam@62: }