SAWYER_EXPORT std::string
Grammar::evalArgument(TokenStream &tokens, ErrorLocation &eloc, bool requireRight) const {
    std::string retval, data;
    size_t depth = 0;
    while (!tokens.atEof()) {
        if (tokens.isa(TOK_LEFT)) {
            ++depth;
            retval += tokens.lexeme();
            tokens.consume();
        } else if (tokens.isa(TOK_RIGHT)) {
            if (0 == depth)
                break;
            --depth;
            retval += tokens.lexeme();
            tokens.consume();
        } else if (tokens.isa(TOK_FUNCTION)) {
            retval += evalFunction(tokens, eloc);
        } else {
            retval += tokens.lexeme();
            tokens.consume();
        }
    }
    if (requireRight) {
        if (!tokens.isa(TOK_RIGHT))
            throw SyntaxError("end-of-argument expected");
        tokens.consume();
    }
    if (depth > 0)
        throw SyntaxError("expected end-of-argument marker");
    return retval;
}
SAWYER_EXPORT std::string
Grammar::evalFunction(TokenStream &tokens, ErrorLocation &eloc) const {
    ASSERT_require(tokens.isa(TOK_FUNCTION));
    std::string funcName = tokens.lexeme();
    ASSERT_require(funcName.size() >= 2 && '@' == funcName[0]);
    funcName = funcName.substr(1);
    tokens.consume();

    // Get the function declaration
    const Function::Ptr func = functions_.getOrDefault(funcName);
    if (!func)
        throw SyntaxError("function \"" + funcName + "\" is not declared");

    // Parse the actual arguments
    std::vector<std::string> actuals;
    while (tokens.isa(TOK_LEFT)) {
        tokens.consume();
        if (func->isMacro()) {
            actuals.push_back(readArgument(tokens, eloc, CONSUME));
        } else {
            actuals.push_back(evalArgument(tokens, eloc, CONSUME));
        }
    }
    func->validateArgs(actuals, tokens);

    ErrorLocation::Trap t(eloc, tokens, "in function \"" + funcName + "\"");
    std::string retval = func->eval(*this, actuals);
    t.passed();
    return retval;
}
SAWYER_EXPORT std::string
Grammar::readArgument(TokenStream &tokens, ErrorLocation &eloc, bool requireRight) const {
    std::string retval;
    size_t depth = 0;
    for (/*void*/; !tokens.atEof(); tokens.consume()) {
        if (tokens.isa(TOK_LEFT)) {
            ++depth;
        } else if (tokens.isa(TOK_RIGHT)) {
            if (0 == depth)
                break;
            --depth;
        }
        retval += tokens.lexeme();
    }
    if (requireRight) {
        if (!tokens.isa(TOK_RIGHT))
            throw SyntaxError("end-of-argument expected");
        tokens.consume();
    }
    if (depth > 0)
        throw SyntaxError("expected end-of-argument marker");
    return retval;
}