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