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