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