<commit_before>#include <vector>
#include <utility>
#include <algorithm>
#include <functional>
#include <unordered_set>
#include <set>
#include "tokenizer.h"
#include "shl_exception.h"

using std::stack;
using std::function;
using std::unordered_set;
using std::set;

namespace shl {

vector<pair<Range, Scope> > Tokenizer::tokenize(const Grammar& grammar, const string& text) {
    vector<pair<Range, Scope> > tokens;
    vector<const Pattern*> pattern_stack;

    tokens.push_back(std::make_pair(Range(0, text.size()), Scope(grammar.name)));
    tokenize(text, grammar, Match::make_dummy(0,0), pattern_stack, tokens);

    return tokens;
}

void for_all_subrules(const Pattern& rule, set<const Pattern*>& visited, function<void(const Pattern&)> callback) {
    const Pattern& real_rule = (rule.include)? *rule.include : rule;
    if (visited.count(&real_rule) > 0) return;
    visited.insert(&real_rule);

    if (real_rule.begin.empty()) {
        for (auto& subrule : real_rule.patterns) {
            for_all_subrules(subrule, visited, callback);
        }
    } else {
        callback(real_rule);
    }
}

void for_all_subrules(const vector<Pattern>& patterns, function<void(const Pattern&)> callback) {
    set<const Pattern*> visited;
    for (auto& rule : patterns) {
        for_all_subrules(rule, visited, callback);
    }
}

inline bool is_end_match_first(const Pattern& pattern, const Match& end_match, const Match& current_first_match) {
    if (pattern.applyEndPatternLast) {
        return end_match[0].position < current_first_match[0].position;
    } else {
        return end_match[0].position <= current_first_match[0].position;
    }
}

/**
 * Find the next lexeme iteratively
 *
 * This method find the next lexeme by matching all begin field of the sub-patterns
 * of current rule, and its own end field, whichever comes first will be seleteced
 *
 * When true returned, found will contain the selected next pattern, and match will hold
 * the results. When false returned, there are 3 scenarios:
 * 1. end field is matched, found & match contains results
 * 2. current pattern is toplevel rule, so no end field, found is nullptr
 * 3. When nothing can be matched(either source text or syntax definition is invalid),
 *    and user specified OPTION_TOLERATE_ERROR, found will be nullptr, otherwise exception
 *    will be thrown
 *
 * Note:
 * begin_end_pos: this is the offset of current pattern's begin match.end(), it's meant to
 * support Perl style \G, and stands for the *previous* match.end()
 */
bool Tokenizer::next_lexeme(const string& text, const int begin_end_pos, const Match& begin_lexeme, const Pattern& pattern, const Pattern** found, Match& match) {
    int pos = begin_lexeme[0].end();
    const Pattern* found_pattern = nullptr;
    Match first_match;
    bool is_close = false;

    // first find pattern or end pattern, whichever comes first
    for_all_subrules(pattern.patterns, [&found_pattern, &first_match, pos, &text, begin_end_pos](const Pattern& sub_pattern) {
        Match tmp = sub_pattern.begin.match(text, pos, begin_end_pos);
        if (tmp != Match::NOT_MATCHED) {
            if( found_pattern == nullptr || tmp[0].position < first_match[0].position) {
                first_match = std::move(tmp);
                found_pattern = &sub_pattern;
            }
        }
    });
    if (!pattern.end.empty()) {
        Match end_match = pattern.end.match(text, pos, begin_end_pos);
        if (end_match != Match::NOT_MATCHED) {
            if( found_pattern == nullptr || is_end_match_first(pattern, end_match, first_match)) {
                first_match = std::move(end_match);
                found_pattern = &pattern;
                is_close = true;
            }
        }
    }
    if ( found_pattern != nullptr) {
        *found = found_pattern;
        match = first_match;
        return !is_close;
    }

    // special handle for toplevel rule
    if (pattern.end.empty()) {
        // all rule with begin will has an end, which is enforced by grammar loader
        // so only toplevel rules can be here
        *found = nullptr;
        is_close = true;
        return false;
    }

    // When no lexeme found
    if (_option & OPTION_TOLERATE_ERROR) {
        *found = nullptr;
        return false;
    } else {
       throw InvalidSourceException("scope not properly closed");
    }
}

vector<string> get_name(vector<const Pattern*>& stack, const string& name, const string& enclosing_name) {
    vector<string> names;

    for(auto pattern : stack) {
        names.push_back(pattern->name);
        names.push_back(pattern->content_name);
    }
    if (!enclosing_name.empty()) names.push_back(enclosing_name);
    names.push_back(name); // name can't be empty

    return names;
}

void Tokenizer::add_scope(vector<pair<Range, Scope> >& tokens, const Range& range, vector<const Pattern*>& stack, const string& name, const string& enclosing_name = "") {
    if (name.empty()) return;
    if (range.length == 0) return; // only captures can potentially has 0 length

    Scope scope(get_name(stack, name, enclosing_name));
    tokens.push_back(std::make_pair(range, scope));
}

inline void append_back(vector<pair<Range, Scope> >& target, const vector<pair<Range, Scope> >& source ) {
    std::move(source.begin(), source.end(), std::back_inserter(target));
}

void Tokenizer::process_capture(vector<pair<Range, Scope> >& tokens, const Match& match, vector<const Pattern*>& stack, const map<int, string>& capture, const string& enclosing_name) {
    for (auto& pair : capture) {
        unsigned int capture_num = pair.first;
        const string& name = pair.second;
        if (match.size() > capture_num) {
            add_scope(tokens, match[capture_num], stack, name, enclosing_name);
        } else {
            if (_option & OPTION_STRICT) {
                throw InvalidSourceException("capture number out of range");
            }
        }
    }
}

Match Tokenizer::tokenize(const string& text, const Pattern& pattern, const Match& begin_lexeme, vector<const Pattern*>& stack, vector<pair<Range, Scope> >& tokens) {
    stack.push_back(&pattern);

    int begin_end_pos = begin_lexeme[0].end();
    const Pattern* found_pattern = nullptr;
    Match last_lexeme, match;
    last_lexeme = begin_lexeme;
    while(next_lexeme(text, begin_end_pos, last_lexeme, pattern, &found_pattern, match)) {
        if (found_pattern->is_match_rule) {
            add_scope(tokens, match[0], stack, found_pattern->name);
            process_capture(tokens, match, stack, found_pattern->captures, found_pattern->name);
            last_lexeme = match;

        } else {
            vector<pair<Range, Scope> > child_tokens;

            Match end_match = tokenize(text, *found_pattern, match, stack, child_tokens);

            Range name_range = Range(match[0].position, end_match[0].end() - match[0].position);
            add_scope(tokens, name_range, stack, found_pattern->name);
            process_capture(tokens, match, stack, found_pattern->begin_captures, found_pattern->name);

            Range content_range = Range(match[0].end(), end_match[0].position - match[0].end());
            add_scope(tokens, content_range, stack, found_pattern->content_name, found_pattern->name);

            append_back(tokens, child_tokens);
            process_capture(tokens, end_match, stack, found_pattern->end_captures, found_pattern->name);
            last_lexeme = end_match;
        }
    }

    stack.pop_back();

    if ( found_pattern == nullptr) { //see comments for next_lexeme
        return last_lexeme;
    } else {
        return match;
    }

}

}
<commit_msg>add comment<commit_after>#include <vector>
#include <utility>
#include <algorithm>
#include <functional>
#include <unordered_set>
#include <set>
#include "tokenizer.h"
#include "shl_exception.h"

using std::stack;
using std::function;
using std::unordered_set;
using std::set;

namespace shl {

vector<pair<Range, Scope> > Tokenizer::tokenize(const Grammar& grammar, const string& text) {
    vector<pair<Range, Scope> > tokens;
    vector<const Pattern*> pattern_stack;

    tokens.push_back(std::make_pair(Range(0, text.size()), Scope(grammar.name)));
    tokenize(text, grammar, Match::make_dummy(0,0), pattern_stack, tokens);

    return tokens;
}

void for_all_subrules(const Pattern& rule, set<const Pattern*>& visited, function<void(const Pattern&)> callback) {
    const Pattern& real_rule = (rule.include)? *rule.include : rule;
    if (visited.count(&real_rule) > 0) return;
    visited.insert(&real_rule);

    if (real_rule.begin.empty()) {
        for (auto& subrule : real_rule.patterns) {
            for_all_subrules(subrule, visited, callback);
        }
    } else {
        // we don't need to look at it's subrule at this time
        callback(real_rule);
    }
}

void for_all_subrules(const vector<Pattern>& patterns, function<void(const Pattern&)> callback) {
    set<const Pattern*> visited;
    for (auto& rule : patterns) {
        for_all_subrules(rule, visited, callback);
    }
}

inline bool is_end_match_first(const Pattern& pattern, const Match& end_match, const Match& current_first_match) {
    if (pattern.applyEndPatternLast) {
        return end_match[0].position < current_first_match[0].position;
    } else {
        return end_match[0].position <= current_first_match[0].position;
    }
}

/**
 * Find the next lexeme iteratively
 *
 * This method find the next lexeme by matching all begin field of the sub-patterns
 * of current rule, and its own end field, whichever comes first will be seleteced
 *
 * When true returned, found will contain the selected next pattern, and match will hold
 * the results. When false returned, there are 3 scenarios:
 * 1. end field is matched, found & match contains results
 * 2. current pattern is toplevel rule, so no end field, found is nullptr
 * 3. When nothing can be matched(either source text or syntax definition is invalid),
 *    and user specified OPTION_TOLERATE_ERROR, found will be nullptr, otherwise exception
 *    will be thrown
 *
 * Note:
 * begin_end_pos: this is the offset of current pattern's begin match.end(), it's meant to
 * support Perl style \G, and stands for the *previous* match.end()
 */
bool Tokenizer::next_lexeme(const string& text, const int begin_end_pos, const Match& begin_lexeme, const Pattern& pattern, const Pattern** found, Match& match) {
    int pos = begin_lexeme[0].end();
    const Pattern* found_pattern = nullptr;
    Match first_match;
    bool is_close = false;

    // first find pattern or end pattern, whichever comes first
    for_all_subrules(pattern.patterns, [&found_pattern, &first_match, pos, &text, begin_end_pos](const Pattern& sub_pattern) {
        Match tmp = sub_pattern.begin.match(text, pos, begin_end_pos);
        if (tmp != Match::NOT_MATCHED) {
            if( found_pattern == nullptr || tmp[0].position < first_match[0].position) {
                first_match = std::move(tmp);
                found_pattern = &sub_pattern;
            }
        }
    });
    if (!pattern.end.empty()) {
        Match end_match = pattern.end.match(text, pos, begin_end_pos);
        if (end_match != Match::NOT_MATCHED) {
            if( found_pattern == nullptr || is_end_match_first(pattern, end_match, first_match)) {
                first_match = std::move(end_match);
                found_pattern = &pattern;
                is_close = true;
            }
        }
    }
    if ( found_pattern != nullptr) {
        *found = found_pattern;
        match = first_match;
        return !is_close;
    }

    // special handle for toplevel rule
    if (pattern.end.empty()) {
        // all rule with begin will has an end, which is enforced by grammar loader
        // so only toplevel rules can be here
        *found = nullptr;
        is_close = true;
        return false;
    }

    // When no lexeme found
    if (_option & OPTION_TOLERATE_ERROR) {
        *found = nullptr;
        return false;
    } else {
       throw InvalidSourceException("scope not properly closed");
    }
}

vector<string> get_name(vector<const Pattern*>& stack, const string& name, const string& enclosing_name) {
    vector<string> names;

    for(auto pattern : stack) {
        names.push_back(pattern->name);
        names.push_back(pattern->content_name);
    }
    if (!enclosing_name.empty()) names.push_back(enclosing_name);
    names.push_back(name); // name can't be empty

    return names;
}

void Tokenizer::add_scope(vector<pair<Range, Scope> >& tokens, const Range& range, vector<const Pattern*>& stack, const string& name, const string& enclosing_name = "") {
    if (name.empty()) return;
    if (range.length == 0) return; // only captures can potentially has 0 length

    Scope scope(get_name(stack, name, enclosing_name));
    tokens.push_back(std::make_pair(range, scope));
}

inline void append_back(vector<pair<Range, Scope> >& target, const vector<pair<Range, Scope> >& source ) {
    std::move(source.begin(), source.end(), std::back_inserter(target));
}

void Tokenizer::process_capture(vector<pair<Range, Scope> >& tokens, const Match& match, vector<const Pattern*>& stack, const map<int, string>& capture, const string& enclosing_name) {
    for (auto& pair : capture) {
        unsigned int capture_num = pair.first;
        const string& name = pair.second;
        if (match.size() > capture_num) {
            add_scope(tokens, match[capture_num], stack, name, enclosing_name);
        } else {
            if (_option & OPTION_STRICT) {
                throw InvalidSourceException("capture number out of range");
            }
        }
    }
}

Match Tokenizer::tokenize(const string& text, const Pattern& pattern, const Match& begin_lexeme, vector<const Pattern*>& stack, vector<pair<Range, Scope> >& tokens) {
    stack.push_back(&pattern);

    int begin_end_pos = begin_lexeme[0].end();
    const Pattern* found_pattern = nullptr;
    Match last_lexeme, match;
    last_lexeme = begin_lexeme;
    while(next_lexeme(text, begin_end_pos, last_lexeme, pattern, &found_pattern, match)) {
        if (found_pattern->is_match_rule) {
            add_scope(tokens, match[0], stack, found_pattern->name);
            process_capture(tokens, match, stack, found_pattern->captures, found_pattern->name);
            last_lexeme = match;

        } else {
            vector<pair<Range, Scope> > child_tokens;

            Match end_match = tokenize(text, *found_pattern, match, stack, child_tokens);

            Range name_range = Range(match[0].position, end_match[0].end() - match[0].position);
            add_scope(tokens, name_range, stack, found_pattern->name);
            process_capture(tokens, match, stack, found_pattern->begin_captures, found_pattern->name);

            Range content_range = Range(match[0].end(), end_match[0].position - match[0].end());
            add_scope(tokens, content_range, stack, found_pattern->content_name, found_pattern->name);

            append_back(tokens, child_tokens);
            process_capture(tokens, end_match, stack, found_pattern->end_captures, found_pattern->name);
            last_lexeme = end_match;
        }
    }

    stack.pop_back();

    if ( found_pattern == nullptr) { //see comments for next_lexeme
        return last_lexeme;
    } else {
        return match;
    }

}

}
<|endoftext|>


<commit_before>#include <Windows.h>
#include <windowsx.h>
#include <string>
#include <vector>

#define VK_Z_KEY 0x5A
// These keys are used to send windows to tray
#define TRAY_KEY VK_Z_KEY
#define MOD_KEY MOD_WIN + MOD_SHIFT

#define WM_ICON 0x1C0A
#define WM_OURICON 0x1C0B
#define EXIT_ID 0x99
#define SHOW_ALL_ID 0x98
#define MAXIMUM_WINDOWS 100

// Stores hidden window record.
typedef struct HIDDEN_WINDOW {
  NOTIFYICONDATA icon;
  HWND window;
} HIDDEN_WINDOW;

// Current execution context
typedef struct TRCONTEXT {
  HWND mainWindow;
  HIDDEN_WINDOW icons[MAXIMUM_WINDOWS];
  HMENU trayMenu;
  int iconIndex; // How many windows are currently hidden
} TRCONTEXT;

HANDLE saveFile;

// Saves our hidden windows so they can be restored in case
// of crashing.
void save(const TRCONTEXT *context) {
  DWORD numbytes;
  // Truncate file
  SetFilePointer(saveFile, 0, NULL, FILE_BEGIN);
  SetEndOfFile(saveFile);
  if (!context->iconIndex) {
    return;
  }
  for (int i = 0; i < context->iconIndex; i++)
  {
    if (context->icons[i].window) {
      std::string str;
      str = std::to_string((long)context->icons[i].window);
      str += ',';
      const char *handleString = str.c_str();
      WriteFile(saveFile, handleString, strlen(handleString), &numbytes, NULL);
    }

  }

}

// Restores a window
void showWindow(TRCONTEXT *context, LPARAM lParam) {
  for (int i = 0; i < context->iconIndex; i++)
  {
    if (context->icons[i].icon.uID == HIWORD(lParam)) {
      ShowWindow(context->icons[i].window, SW_SHOW);
      Shell_NotifyIcon(NIM_DELETE, &context->icons[i].icon);
      SetForegroundWindow(context->icons[i].window);
      context->icons[i] = {};
      std::vector<HIDDEN_WINDOW> temp = std::vector<HIDDEN_WINDOW>(context->iconIndex);
      // Restructure array so there are no holes
      for (int j = 0, x = 0; j < context->iconIndex; j++)
      {
        if (context->icons[j].window) {
          temp[x] = context->icons[j];
          x++;
        }
      }
      memcpy_s(context->icons, sizeof(context->icons), &temp.front(), sizeof(HIDDEN_WINDOW)*context->iconIndex);
      context->iconIndex--;
      save(context);
      break;
    }
  }
}

// Minimizes the current window to tray.
// Uses currently focused window unless supplied a handle as the argument.
void minimizeToTray(TRCONTEXT *context, long restoreWindow) {
  // Taskbar and desktop windows are restricted from hiding.
  const char restrictWins[][14] = { {"WorkerW"}, {"Shell_TrayWnd"} };

  HWND currWin = 0;
  if (!restoreWindow) {
    currWin = GetForegroundWindow();
  }
  else {
    currWin = reinterpret_cast<HWND>(restoreWindow);
  }

  if (!currWin) {
    return;
  }

  char className[256];
  if (!GetClassName(currWin, className, 256)) {
    return;
  }
  else {
    for (int i = 0; i < sizeof(restrictWins) / sizeof(*restrictWins); i++)
    {
      if (strcmp(restrictWins[i], className) == 0) {
        return;
      }
    }
  }
  if (context->iconIndex == MAXIMUM_WINDOWS) {
    MessageBox(NULL, "Error! Too many hidden windows. Please unhide some.", "Traymond", MB_OK | MB_ICONERROR);
    return;
  }
  ULONG_PTR icon = GetClassLongPtr(currWin, GCLP_HICONSM);
  if (!icon) {
    icon = SendMessage(currWin, WM_GETICON, 2, NULL);
    if (!icon) {
      return;
    }
  }

  NOTIFYICONDATA nid;
  nid.cbSize = sizeof(NOTIFYICONDATA);
  nid.hWnd = context->mainWindow;
  nid.hIcon = (HICON)icon;
  nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_SHOWTIP;
  nid.uVersion = NOTIFYICON_VERSION_4;
  nid.uID = LOWORD(reinterpret_cast<UINT>(currWin));
  nid.uCallbackMessage = WM_ICON;
  GetWindowText(currWin, nid.szTip, 128);
  for (int i = 0; i < context->iconIndex; i++) {
    if (context->icons[i].icon.uID == nid.uID) {
      return;
    }
  }
  context->icons[context->iconIndex].icon = nid;
  context->icons[context->iconIndex].window = currWin;
  context->iconIndex++;
  Shell_NotifyIcon(NIM_ADD, &nid);
  Shell_NotifyIcon(NIM_SETVERSION, &nid);
  ShowWindow(currWin, SW_HIDE);
  if (!restoreWindow) {
    save(context);
  }

}

// Adds our own icon to tray
void createTrayIcon(HWND mainWindow, HINSTANCE hInstance, NOTIFYICONDATA* icon) {
  icon->cbSize = sizeof(NOTIFYICONDATA);
  icon->hWnd = mainWindow;
  icon->hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(101));
  icon->uFlags = NIF_ICON | NIF_TIP | NIF_SHOWTIP | NIF_MESSAGE;
  icon->uVersion = NOTIFYICON_VERSION_4;
  icon->uID = reinterpret_cast<UINT>(mainWindow);
  icon->uCallbackMessage = WM_OURICON;
  strcpy_s(icon->szTip, "Traymond");
  Shell_NotifyIcon(NIM_ADD, icon);
  Shell_NotifyIcon(NIM_SETVERSION, icon);
}

// Creates our tray icon menu
void createTrayMenu(HMENU* trayMenu) {
  *trayMenu = CreatePopupMenu();

  MENUITEMINFO showAllMenuItem;
  MENUITEMINFO exitMenuItem;

  exitMenuItem.cbSize = sizeof(MENUITEMINFO);
  exitMenuItem.fMask = MIIM_STRING | MIIM_ID;
  exitMenuItem.fType = MFT_STRING;
  exitMenuItem.dwTypeData = "Exit";
  exitMenuItem.cch = 5;
  exitMenuItem.wID = EXIT_ID;

  showAllMenuItem.cbSize = sizeof(MENUITEMINFO);
  showAllMenuItem.fMask = MIIM_STRING | MIIM_ID;
  showAllMenuItem.fType = MFT_STRING;
  showAllMenuItem.dwTypeData = "Restore all windows";
  showAllMenuItem.cch = 20;
  showAllMenuItem.wID = SHOW_ALL_ID;

  InsertMenuItem(*trayMenu, 0, FALSE, &showAllMenuItem);
  InsertMenuItem(*trayMenu, 0, FALSE, &exitMenuItem);
}
// Shows all hidden windows;
void showAllWindows(TRCONTEXT *context) {
  for (int i = 0; i < context->iconIndex; i++)
  {
    ShowWindow(context->icons[i].window, SW_SHOW);
    Shell_NotifyIcon(NIM_DELETE, &context->icons[i].icon);
    context->icons[i] = {};
  }
  save(context);
  context->iconIndex = 0;
}

void exitApp() {
  PostQuitMessage(0);
}

// Creates and reads the save file to restore hidden windows in case of unexpected termination
void startup(TRCONTEXT *context) {
  if ((saveFile = CreateFile("traymond.dat", GENERIC_READ | GENERIC_WRITE, \
    0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
    MessageBox(NULL, "Error! Traymond could not create a save file.", "Traymond", MB_OK | MB_ICONERROR);
    exitApp();
  }
  // Check if we've crashed (i. e. there is a save file) during current uptime and
  // if there are windows to restore, in which case restore them and
  // display a reassuring message.
  if (GetLastError() == ERROR_ALREADY_EXISTS) {
    DWORD numbytes;
    DWORD fileSize = GetFileSize(saveFile, NULL);

    if (!fileSize) {
      return;
    };

    FILETIME saveFileWriteTime;
    GetFileTime(saveFile, NULL, NULL, &saveFileWriteTime);
    uint64_t writeTime = ((uint64_t)saveFileWriteTime.dwHighDateTime << 32 | (uint64_t)saveFileWriteTime.dwLowDateTime) / 10000;
    GetSystemTimeAsFileTime(&saveFileWriteTime);
    writeTime = (((uint64_t)saveFileWriteTime.dwHighDateTime << 32 | (uint64_t)saveFileWriteTime.dwLowDateTime) / 10000) - writeTime;

    if (GetTickCount64() < writeTime) {
      return;
    }

    std::vector<char> contents = std::vector<char>(fileSize);
    ReadFile(saveFile, &contents.front(), fileSize, &numbytes, NULL);
    char handle[10];
    int index = 0;
    for (size_t i = 0; i < fileSize; i++)
    {
      if (contents[i] != ',') {
        handle[index] = contents[i];
        index++;
      }
      else {
        index = 0;
        minimizeToTray(context, std::stoi(std::string(handle)));
        memset(handle, 0, sizeof(handle));
      }
    }
    std::string restore_message = "Traymond had previously been terminated unexpectedly.\n\nRestored " + \
      std::to_string(context->iconIndex) + (context->iconIndex > 1 ? " icons." : " icon.");
    MessageBox(NULL, restore_message.c_str(), "Traymond", MB_OK);
    }
  }

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

  TRCONTEXT* context = reinterpret_cast<TRCONTEXT*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
  POINT pt;
  switch (uMsg)
  {
  case WM_ICON:
    if (LOWORD(lParam) == WM_LBUTTONDBLCLK) {
      showWindow(context, lParam);
    }
    break;
  case WM_OURICON:
    if (LOWORD(lParam) == WM_RBUTTONUP) {
      SetForegroundWindow(hwnd);
      GetCursorPos(&pt);
      TrackPopupMenuEx(context->trayMenu, \
      (GetSystemMetrics(SM_MENUDROPALIGNMENT) ? TPM_RIGHTALIGN : TPM_LEFTALIGN) | TPM_BOTTOMALIGN, \
        pt.x, pt.y, hwnd, NULL);
    }
    break;
  case WM_COMMAND:
    if (HIWORD(wParam) == 0) {
      switch LOWORD(wParam) {
      case SHOW_ALL_ID:
        showAllWindows(context);
        break;
      case EXIT_ID:
        exitApp();
        break;
      }
    }
    break;
  case WM_HOTKEY: // We only have one hotkey, so no need to check the message
    minimizeToTray(context, NULL);
    break;
  default:
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }
  return 0;
}

#pragma warning( push )
#pragma warning( disable : 4100 )
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
#pragma warning( pop )

  TRCONTEXT context = {};

  NOTIFYICONDATA icon = {};

  // Mutex to allow only one instance
  const char szUniqueNamedMutex[] = "traymond_mutex";
  HANDLE mutex = CreateMutex(NULL, TRUE, szUniqueNamedMutex);
  if (GetLastError() == ERROR_ALREADY_EXISTS)
  {
    MessageBox(NULL, "Error! Another instance of Traymond is already running.", "Traymond", MB_OK | MB_ICONERROR);
    return 1;
  }

  BOOL bRet;
  MSG msg;

  const char CLASS_NAME[] = "Traymond";

  WNDCLASS wc = {};
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.lpszClassName = CLASS_NAME;

  if (!RegisterClass(&wc)) {
    return 1;
  }

  context.mainWindow = CreateWindow(CLASS_NAME, NULL, NULL, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);

  if (!context.mainWindow) {
    return 1;
  }

  // Store our context in main window for retrieval by WindowProc
  SetWindowLongPtr(context.mainWindow, GWLP_USERDATA, reinterpret_cast<LONG>(&context));

  if (!RegisterHotKey(context.mainWindow, 0, MOD_KEY | MOD_NOREPEAT, TRAY_KEY)) {
    MessageBox(NULL, "Error! Could not register the hotkey.", "Traymond", MB_OK | MB_ICONERROR);
    return 1;
  }

  createTrayIcon(context.mainWindow, hInstance, &icon);
  createTrayMenu(&context.trayMenu);
  startup(&context);

  while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
  {
    if (bRet != -1) {
      DispatchMessage(&msg);
    }
  }
  // Clean up on exit;
  showAllWindows(&context);
  Shell_NotifyIcon(NIM_DELETE, &icon);
  ReleaseMutex(mutex);
  CloseHandle(mutex);
  CloseHandle(saveFile);
  DestroyMenu(context.trayMenu);
  DestroyWindow(context.mainWindow);
  DeleteFile("traymond.dat"); // No save file means we have exited gracefully
  UnregisterHotKey(context.mainWindow, 0);
  return msg.wParam;
}
<commit_msg>Fix interaction with pinned applications<commit_after>#include <Windows.h>
#include <windowsx.h>
#include <string>
#include <vector>

#define VK_Z_KEY 0x5A
// These keys are used to send windows to tray
#define TRAY_KEY VK_Z_KEY
#define MOD_KEY MOD_WIN + MOD_SHIFT

#define WM_ICON 0x1C0A
#define WM_OURICON 0x1C0B
#define EXIT_ID 0x99
#define SHOW_ALL_ID 0x98
#define MAXIMUM_WINDOWS 100

// Stores hidden window record.
typedef struct HIDDEN_WINDOW {
  NOTIFYICONDATA icon;
  HWND window;
} HIDDEN_WINDOW;

// Current execution context
typedef struct TRCONTEXT {
  HWND mainWindow;
  HIDDEN_WINDOW icons[MAXIMUM_WINDOWS];
  HMENU trayMenu;
  int iconIndex; // How many windows are currently hidden
} TRCONTEXT;

HANDLE saveFile;

// Saves our hidden windows so they can be restored in case
// of crashing.
void save(const TRCONTEXT *context) {
  DWORD numbytes;
  // Truncate file
  SetFilePointer(saveFile, 0, NULL, FILE_BEGIN);
  SetEndOfFile(saveFile);
  if (!context->iconIndex) {
    return;
  }
  for (int i = 0; i < context->iconIndex; i++)
  {
    if (context->icons[i].window) {
      std::string str;
      str = std::to_string((long)context->icons[i].window);
      str += ',';
      const char *handleString = str.c_str();
      WriteFile(saveFile, handleString, strlen(handleString), &numbytes, NULL);
    }

  }

}

// Restores a window
void showWindow(TRCONTEXT *context, LPARAM lParam) {
  for (int i = 0; i < context->iconIndex; i++)
  {
    if (context->icons[i].icon.uID == HIWORD(lParam)) {
      ShowWindow(context->icons[i].window, SW_SHOW);
      Shell_NotifyIcon(NIM_DELETE, &context->icons[i].icon);
      SetForegroundWindow(context->icons[i].window);
      context->icons[i] = {};
      std::vector<HIDDEN_WINDOW> temp = std::vector<HIDDEN_WINDOW>(context->iconIndex);
      // Restructure array so there are no holes
      for (int j = 0, x = 0; j < context->iconIndex; j++)
      {
        if (context->icons[j].window) {
          temp[x] = context->icons[j];
          x++;
        }
      }
      memcpy_s(context->icons, sizeof(context->icons), &temp.front(), sizeof(HIDDEN_WINDOW)*context->iconIndex);
      context->iconIndex--;
      save(context);
      break;
    }
  }
}

// Minimizes the current window to tray.
// Uses currently focused window unless supplied a handle as the argument.
void minimizeToTray(TRCONTEXT *context, long restoreWindow) {
  // Taskbar and desktop windows are restricted from hiding.
  const char restrictWins[][14] = { {"WorkerW"}, {"Shell_TrayWnd"} };

  HWND currWin = 0;
  if (!restoreWindow) {
    currWin = GetForegroundWindow();
  }
  else {
    currWin = reinterpret_cast<HWND>(restoreWindow);
  }

  if (!currWin) {
    return;
  }

  char className[256];
  if (!GetClassName(currWin, className, 256)) {
    return;
  }
  else {
    for (int i = 0; i < sizeof(restrictWins) / sizeof(*restrictWins); i++)
    {
      if (strcmp(restrictWins[i], className) == 0) {
        return;
      }
    }
  }
  if (context->iconIndex == MAXIMUM_WINDOWS) {
    MessageBox(NULL, "Error! Too many hidden windows. Please unhide some.", "Traymond", MB_OK | MB_ICONERROR);
    return;
  }
  ULONG_PTR icon = GetClassLongPtr(currWin, GCLP_HICONSM);
  if (!icon) {
    icon = SendMessage(currWin, WM_GETICON, 2, NULL);
    if (!icon) {
      return;
    }
  }

  NOTIFYICONDATA nid;
  nid.cbSize = sizeof(NOTIFYICONDATA);
  nid.hWnd = context->mainWindow;
  nid.hIcon = (HICON)icon;
  nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_SHOWTIP;
  nid.uVersion = NOTIFYICON_VERSION_4;
  nid.uID = LOWORD(reinterpret_cast<UINT>(currWin));
  nid.uCallbackMessage = WM_ICON;
  GetWindowText(currWin, nid.szTip, 128);
  context->icons[context->iconIndex].icon = nid;
  context->icons[context->iconIndex].window = currWin;
  context->iconIndex++;
  Shell_NotifyIcon(NIM_ADD, &nid);
  Shell_NotifyIcon(NIM_SETVERSION, &nid);
  ShowWindow(currWin, SW_HIDE);
  if (!restoreWindow) {
    save(context);
  }

}

// Adds our own icon to tray
void createTrayIcon(HWND mainWindow, HINSTANCE hInstance, NOTIFYICONDATA* icon) {
  icon->cbSize = sizeof(NOTIFYICONDATA);
  icon->hWnd = mainWindow;
  icon->hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(101));
  icon->uFlags = NIF_ICON | NIF_TIP | NIF_SHOWTIP | NIF_MESSAGE;
  icon->uVersion = NOTIFYICON_VERSION_4;
  icon->uID = reinterpret_cast<UINT>(mainWindow);
  icon->uCallbackMessage = WM_OURICON;
  strcpy_s(icon->szTip, "Traymond");
  Shell_NotifyIcon(NIM_ADD, icon);
  Shell_NotifyIcon(NIM_SETVERSION, icon);
}

// Creates our tray icon menu
void createTrayMenu(HMENU* trayMenu) {
  *trayMenu = CreatePopupMenu();

  MENUITEMINFO showAllMenuItem;
  MENUITEMINFO exitMenuItem;

  exitMenuItem.cbSize = sizeof(MENUITEMINFO);
  exitMenuItem.fMask = MIIM_STRING | MIIM_ID;
  exitMenuItem.fType = MFT_STRING;
  exitMenuItem.dwTypeData = "Exit";
  exitMenuItem.cch = 5;
  exitMenuItem.wID = EXIT_ID;

  showAllMenuItem.cbSize = sizeof(MENUITEMINFO);
  showAllMenuItem.fMask = MIIM_STRING | MIIM_ID;
  showAllMenuItem.fType = MFT_STRING;
  showAllMenuItem.dwTypeData = "Restore all windows";
  showAllMenuItem.cch = 20;
  showAllMenuItem.wID = SHOW_ALL_ID;

  InsertMenuItem(*trayMenu, 0, FALSE, &showAllMenuItem);
  InsertMenuItem(*trayMenu, 0, FALSE, &exitMenuItem);
}
// Shows all hidden windows;
void showAllWindows(TRCONTEXT *context) {
  for (int i = 0; i < context->iconIndex; i++)
  {
    ShowWindow(context->icons[i].window, SW_SHOW);
    Shell_NotifyIcon(NIM_DELETE, &context->icons[i].icon);
    context->icons[i] = {};
  }
  save(context);
  context->iconIndex = 0;
}

void exitApp() {
  PostQuitMessage(0);
}

// Creates and reads the save file to restore hidden windows in case of unexpected termination
void startup(TRCONTEXT *context) {
  if ((saveFile = CreateFile("traymond.dat", GENERIC_READ | GENERIC_WRITE, \
    0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
    MessageBox(NULL, "Error! Traymond could not create a save file.", "Traymond", MB_OK | MB_ICONERROR);
    exitApp();
  }
  // Check if we've crashed (i. e. there is a save file) during current uptime and
  // if there are windows to restore, in which case restore them and
  // display a reassuring message.
  if (GetLastError() == ERROR_ALREADY_EXISTS) {
    DWORD numbytes;
    DWORD fileSize = GetFileSize(saveFile, NULL);

    if (!fileSize) {
      return;
    };

    FILETIME saveFileWriteTime;
    GetFileTime(saveFile, NULL, NULL, &saveFileWriteTime);
    uint64_t writeTime = ((uint64_t)saveFileWriteTime.dwHighDateTime << 32 | (uint64_t)saveFileWriteTime.dwLowDateTime) / 10000;
    GetSystemTimeAsFileTime(&saveFileWriteTime);
    writeTime = (((uint64_t)saveFileWriteTime.dwHighDateTime << 32 | (uint64_t)saveFileWriteTime.dwLowDateTime) / 10000) - writeTime;

    if (GetTickCount64() < writeTime) {
      return;
    }

    std::vector<char> contents = std::vector<char>(fileSize);
    ReadFile(saveFile, &contents.front(), fileSize, &numbytes, NULL);
    char handle[10];
    int index = 0;
    for (size_t i = 0; i < fileSize; i++)
    {
      if (contents[i] != ',') {
        handle[index] = contents[i];
        index++;
      }
      else {
        index = 0;
        minimizeToTray(context, std::stoi(std::string(handle)));
        memset(handle, 0, sizeof(handle));
      }
    }
    std::string restore_message = "Traymond had previously been terminated unexpectedly.\n\nRestored " + \
      std::to_string(context->iconIndex) + (context->iconIndex > 1 ? " icons." : " icon.");
    MessageBox(NULL, restore_message.c_str(), "Traymond", MB_OK);
    }
  }

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

  TRCONTEXT* context = reinterpret_cast<TRCONTEXT*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
  POINT pt;
  switch (uMsg)
  {
  case WM_ICON:
    if (LOWORD(lParam) == WM_LBUTTONDBLCLK) {
      showWindow(context, lParam);
    }
    break;
  case WM_OURICON:
    if (LOWORD(lParam) == WM_RBUTTONUP) {
      SetForegroundWindow(hwnd);
      GetCursorPos(&pt);
      TrackPopupMenuEx(context->trayMenu, \
      (GetSystemMetrics(SM_MENUDROPALIGNMENT) ? TPM_RIGHTALIGN : TPM_LEFTALIGN) | TPM_BOTTOMALIGN, \
        pt.x, pt.y, hwnd, NULL);
    }
    break;
  case WM_COMMAND:
    if (HIWORD(wParam) == 0) {
      switch LOWORD(wParam) {
      case SHOW_ALL_ID:
        showAllWindows(context);
        break;
      case EXIT_ID:
        exitApp();
        break;
      }
    }
    break;
  case WM_HOTKEY: // We only have one hotkey, so no need to check the message
    minimizeToTray(context, NULL);
    break;
  default:
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }
  return 0;
}

#pragma warning( push )
#pragma warning( disable : 4100 )
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
#pragma warning( pop )

  TRCONTEXT context = {};

  NOTIFYICONDATA icon = {};

  // Mutex to allow only one instance
  const char szUniqueNamedMutex[] = "traymond_mutex";
  HANDLE mutex = CreateMutex(NULL, TRUE, szUniqueNamedMutex);
  if (GetLastError() == ERROR_ALREADY_EXISTS)
  {
    MessageBox(NULL, "Error! Another instance of Traymond is already running.", "Traymond", MB_OK | MB_ICONERROR);
    return 1;
  }

  BOOL bRet;
  MSG msg;

  const char CLASS_NAME[] = "Traymond";

  WNDCLASS wc = {};
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.lpszClassName = CLASS_NAME;

  if (!RegisterClass(&wc)) {
    return 1;
  }

  context.mainWindow = CreateWindow(CLASS_NAME, NULL, NULL, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);

  if (!context.mainWindow) {
    return 1;
  }

  // Store our context in main window for retrieval by WindowProc
  SetWindowLongPtr(context.mainWindow, GWLP_USERDATA, reinterpret_cast<LONG>(&context));

  if (!RegisterHotKey(context.mainWindow, 0, MOD_KEY | MOD_NOREPEAT, TRAY_KEY)) {
    MessageBox(NULL, "Error! Could not register the hotkey.", "Traymond", MB_OK | MB_ICONERROR);
    return 1;
  }

  createTrayIcon(context.mainWindow, hInstance, &icon);
  createTrayMenu(&context.trayMenu);
  startup(&context);

  while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
  {
    if (bRet != -1) {
      DispatchMessage(&msg);
    }
  }
  // Clean up on exit;
  showAllWindows(&context);
  Shell_NotifyIcon(NIM_DELETE, &icon);
  ReleaseMutex(mutex);
  CloseHandle(mutex);
  CloseHandle(saveFile);
  DestroyMenu(context.trayMenu);
  DestroyWindow(context.mainWindow);
  DeleteFile("traymond.dat"); // No save file means we have exited gracefully
  UnregisterHotKey(context.mainWindow, 0);
  return msg.wParam;
}
<|endoftext|>


<commit_before>// $Id$

/*
Copyright (c) 2007-2009, Trustees of The Leland Stanford Junior University
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the Stanford University nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*booksim_config.cpp
 *
 *Contains all the configurable parameters in a network
 *
 */

#include "booksim.hpp"
#include "booksim_config.hpp"

BookSimConfig::BookSimConfig( )
{
  //========================================================
  // Network options
  //========================================================

  // Channel length listing file
  AddStrField( "channel_file", "" ) ;

  // Use read/write request reply scheme

  _int_map["use_read_write"] = 0;

  _int_map["read_request_begin_vc"] = 0;
  _int_map["read_request_end_vc"] = 5;

  _int_map["write_request_begin_vc"] = 2;
  _int_map["write_request_end_vc"] = 7;

  _int_map["read_reply_begin_vc"] = 8;
  _int_map["read_reply_end_vc"] = 13;

  _int_map["write_reply_begin_vc"] = 10;
  _int_map["write_reply_end_vc"] = 15;

  // Physical sub-networks
  _int_map["physical_subnetworks"] = 1;

  // Control Injection of Packets into Replicated Networks
  _int_map["read_request_subnet"] = 0;

  _int_map["read_reply_subnet"] = 0;

  _int_map["write_request_subnet"] = 0;

  _int_map["write_reply_subnet"] = 0;

  //==== Topology options =======================
  //important
  AddStrField( "topology", "torus" );
  _int_map["k"] = 8; //network radix
  _int_map["n"] = 2; //network dimension
  _int_map["c"] = 1; //concentration
  AddStrField( "routing_function", "none" );
  _int_map["use_noc_latency"] = 1;

  //not critical
  _int_map["x"] = 8; //number of routers in X
  _int_map["y"] = 8; //number of routers in Y
  _int_map["xr"] = 1; //number of nodes per router in X only if c>1
  _int_map["yr"] = 1; //number of nodes per router in Y only if c>1
  _int_map["limit"] = 0; //how many of the nodes are actually used

  _int_map["link_failures"] = 0; //legacy
  _int_map["fail_seed"]     = 0; //legacy

  //==== Cmesh topology options =======================
  _int_map["express_channels"] = 0; //for Cmesh only, 0=no express channels
  //==== Single-node options ===============================

  _int_map["in_ports"]  = 5;
  _int_map["out_ports"] = 5;
  _int_map["voq"] = 0; //output queuing

  //========================================================
  // Router options
  //========================================================

  //==== General options ===================================

  AddStrField( "router", "iq" );

  _int_map["output_delay"] = 0;
  _int_map["credit_delay"] = 0;
  _float_map["internal_speedup"] = 1.0;

  //==== Input-queued ======================================

  // Control of virtual channel speculation
  _int_map["speculative"] = 0 ;

  // what to use to inhibit speculative allocator grants?
  AddStrField("filter_spec_grants", "confl_nonspec_gnts");

  _int_map["num_vcs"]         = 16;
  _int_map["vc_buf_size"]     = 8;
  _int_map["shared_buf_size"] = 0;
  _int_map["dynamic_sharing"] = 0;

  _int_map["wait_for_tail_credit"] = 0; // reallocate a VC before a tail credit?
  _int_map["vc_busy_when_full"] = 0; // mark VCs as in use when they have no credit available
  _int_map["vc_priority_donation"] = 0; // allow high-priority flits to donate their priority to low-priority that they are queued up behind
  _int_map["replies_inherit_priority"] = 0; // whenusing request-reply traffic (use_read_write=1) with age-based priority, make replies inherit their corresponding requests' age

  _int_map["hold_switch_for_packet"] = 0; // hold a switch config for the entire packet

  _int_map["input_speedup"]     = 1;  // expansion of input ports into crossbar
  _int_map["output_speedup"]    = 1;  // expansion of output ports into crossbar

  _int_map["routing_delay"]    = 1;
  _int_map["vc_alloc_delay"]   = 1;
  _int_map["sw_alloc_delay"]   = 1;
  _int_map["st_prepare_delay"] = 0;
  _int_map["st_final_delay"]   = 1;

  //==== Event-driven =====================================

  _int_map["vct"] = 0;

  //==== Allocators ========================================

  AddStrField( "vc_allocator", "islip" );
  AddStrField( "sw_allocator", "islip" );

  AddStrField( "vc_alloc_arb_type", "round_robin" );
  AddStrField( "sw_alloc_arb_type", "round_robin" );

  _int_map["alloc_iters"] = 1;

  // dub: allow setting the number of iterations for each allocator separately
  // (a value of 0 indicates it should inherit its value from alloc_iters)
  _int_map["vc_alloc_iters"] = 0;
  _int_map["sw_alloc_iters"] = 0;

  //==== Traffic ========================================

  AddStrField( "traffic", "uniform" );

  AddStrField( "hotspot_nodes", "" );
  AddStrField( "hotspot_rates", "" );

  AddStrField( "combined_patterns", "" );
  AddStrField( "combined_rates", "" );

  _int_map["perm_seed"] = 0;         // seed value for random permuation trafficpattern generator

  _float_map["injection_rate"]       = 0.1; //if 0.0 assumes it is batch mode
  _int_map["injection_rate_uses_flits"] = 0;

  _int_map["const_flits_per_packet"] = 1; //use  read_request_size etc insted

  AddStrField( "injection_process", "bernoulli" );

  _float_map["burst_alpha"] = 0.5; // burst interval
  _float_map["burst_beta"]  = 0.5; // burst length

  AddStrField( "priority", "none" );  // message priorities

  _int_map["batch_size"] = 1000;
  _int_map["batch_count"] = 1;
  _int_map["max_outstanding_requests"] = 4;

  _int_map["read_request_size"]  = 1; //flit per packet
  _int_map["write_request_size"] = 1; //flit per packet
  _int_map["read_reply_size"]    = 1; //flit per packet
  _int_map["write_reply_size"]   = 1; //flit per packet

  //==== Simulation parameters ==========================

  // types:
  //   latency    - average + latency distribution for a particular injection rate
  //   throughput - sustained throughput for a particular injection rate

  AddStrField( "sim_type", "latency" );

  _int_map["warmup_periods"] = 3; // number of samples periods to "warm-up" the simulation

  _int_map["sample_period"] = 1000; // how long between measurements
  _int_map["max_samples"]   = 10;   // maximum number of sample periods in a simulation

  _float_map["latency_thres"] = 500.0; // if avg. latency exceeds the threshold, assume unstable
  _float_map["warmup_thres"] = 0.05; // consider warmed up once relative change in latency and throughput between successive iterations is smaller than this

  // consider converged once relative change in latency / throughput between successive iterations is smaller than this
  _float_map["stopping_thres"] = 0.05;
  _float_map["acc_stopping_thres"] = 0.05;

  _int_map["sim_count"]     = 1;   // number of simulations to perform

  _int_map["include_queuing"] =1; // non-zero includes source queuing latency

  //  _int_map["reorder"]         = 0;  // know what you're doing

  //_int_map["flit_timing"]     = 0;  // know what you're doing
  //_int_map["split_packets"]   = 0;  // know what you're doing

  _int_map["seed"]            = 0; //random seed for simulation, e.g. traffic

  _int_map["print_activity"] = 0;

  _int_map["print_csv_results"] = 0;
  _int_map["print_vc_stats"] = 0;

  _int_map["deadlock_warn_timeout"] = 256;

  _int_map["drain_measured_only"] = 0;

  _int_map["viewer_trace"] = 0;

  AddStrField("watch_file", "");

  AddStrField("watch_flits", "");
  AddStrField("watch_packets", "");

  AddStrField("watch_out", "");

  AddStrField("stats_out", "");
  AddStrField("flow_out", "");

  //==================Power model params=====================
  _int_map["sim_power"] = 0;
  AddStrField("power_output_file","pwr_tmp");
  AddStrField("tech_file", "../utils/temp");
  _int_map["channel_width"] = 128;
  _int_map["channel_sweep"] = 0;

  //==================Network file===========================
  AddStrField("network_file","");
}

#ifdef USE_GUI

//A list of important simulator for the booksim gui, anything else not listed here is still included
//but just not very organized
vector< pair<string, vector< string> > > *BookSimConfig::GetImportantMap(){
  //Vector of 5 categories, each category is a vector of potions. Maps don't work because it autosorts
  vector< pair<string, vector< string> > > *important = new  vector< pair<string, vector< string> > >;
  important->push_back( make_pair( "Topology", vector<string>() ));
  (*important)[0].second.push_back("topology");
  (*important)[0].second.push_back("k");
  (*important)[0].second.push_back("n");
  (*important)[0].second.push_back("c");
  (*important)[0].second.push_back( "routing_function");
  (*important)[0].second.push_back("use_noc_latency");

  important->push_back(make_pair("Router", vector<string>()));
  (*important)[1].second.push_back("router");
  (*important)[1].second.push_back("num_vcs");
  (*important)[1].second.push_back("vc_buf_size");
  (*important)[1].second.push_back("routing_delay");
  (*important)[1].second.push_back("vc_alloc_delay");
  (*important)[1].second.push_back("sw_alloc_delay");
  (*important)[1].second.push_back("st_prepare_delay");
  (*important)[1].second.push_back("st_final_delay");

  important->push_back(make_pair("Allocator", vector<string>()));
  (*important)[2].second.push_back("vc_allocator");
  (*important)[2].second.push_back("vc_alloc_arb_type");
  (*important)[2].second.push_back("sw_allocator");
  (*important)[2].second.push_back("sw_alloc_arb_type");
  (*important)[2].second.push_back( "priority");
  (*important)[2].second.push_back("speculative");

  important->push_back(make_pair("Simulation", vector<string>()));
 (*important)[3].second.push_back("traffic");
 (*important)[3].second.push_back("injection_rate");
 (*important)[3].second.push_back("injection_rate_uses_flits");
 (*important)[3].second.push_back("sim_type");
 (*important)[3].second.push_back("latency_thres");
 (*important)[3].second.push_back("const_flits_per_packet");
 (*important)[3].second.push_back("injection_process");
 (*important)[3].second.push_back("sample_period");

  important->push_back(make_pair("Statistics", vector<string>()));
  (*important)[4].second.push_back("print_activity");
  (*important)[4].second.push_back("print_csv_results");
  (*important)[4].second.push_back("print_vc_stats");
  (*important)[4].second.push_back("stats_out");
  (*important)[4].second.push_back("sim_power");
  (*important)[4].second.push_back("power_output_file");

  return important;
}

#endif

vector<string> BookSimConfig::tokenize(string data) {
  const string separator = "{,}";
  vector<string> result;
  size_t last_pos = data.find_first_not_of(separator);
  size_t pos = data.find_first_of(separator, last_pos);
  while(pos != string::npos || last_pos != string::npos) {
    result.push_back(data.substr(last_pos, pos - last_pos));
    last_pos = data.find_first_not_of(separator, pos);
    pos = data.find_first_of(separator, last_pos);
  }
  return result;
}

PowerConfig::PowerConfig( )
{

  _int_map["H_INVD2"] = 0;
  _int_map["W_INVD2"] = 0;
  _int_map["H_DFQD1"] = 0;
  _int_map["W_DFQD1"] = 0;
  _int_map["H_ND2D1"] = 0;
  _int_map["W_ND2D1"] = 0;
  _int_map["H_SRAM"] = 0;
  _int_map["W_SRAM"] = 0;
  _float_map["Vdd"] = 0;
  _float_map["R"] = 0;
  _float_map["IoffSRAM"] = 0;
  _float_map["IoffP"] = 0;
  _float_map["IoffN"] = 0;
  _float_map["Cg_pwr"] = 0;
  _float_map["Cd_pwr"] = 0;
  _float_map["Cgdl"] = 0;
  _float_map["Cg"] = 0;
  _float_map["Cd"] = 0;
  _float_map["LAMBDA"] = 0;
  _float_map["MetalPitch"] = 0;
  _float_map["Rw"] = 0;
  _float_map["Cw_gnd"] = 0;
  _float_map["Cw_cpl"] = 0;
  _float_map["wire_length"] = 0;

}
<commit_msg>reorder parameters<commit_after>// $Id$

/*
Copyright (c) 2007-2009, Trustees of The Leland Stanford Junior University
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the Stanford University nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*booksim_config.cpp
 *
 *Contains all the configurable parameters in a network
 *
 */

#include "booksim.hpp"
#include "booksim_config.hpp"

BookSimConfig::BookSimConfig( )
{
  //========================================================
  // Network options
  //========================================================

  // Channel length listing file
  AddStrField( "channel_file", "" ) ;

  // Physical sub-networks
  _int_map["physical_subnetworks"] = 1;

  //==== Topology options =======================
  //important
  AddStrField( "topology", "torus" );
  _int_map["k"] = 8; //network radix
  _int_map["n"] = 2; //network dimension
  _int_map["c"] = 1; //concentration
  AddStrField( "routing_function", "none" );
  _int_map["use_noc_latency"] = 1;

  //not critical
  _int_map["x"] = 8; //number of routers in X
  _int_map["y"] = 8; //number of routers in Y
  _int_map["xr"] = 1; //number of nodes per router in X only if c>1
  _int_map["yr"] = 1; //number of nodes per router in Y only if c>1
  _int_map["limit"] = 0; //how many of the nodes are actually used

  _int_map["link_failures"] = 0; //legacy
  _int_map["fail_seed"]     = 0; //legacy

  //==== Cmesh topology options =======================
  _int_map["express_channels"] = 0; //for Cmesh only, 0=no express channels
  //==== Single-node options ===============================

  _int_map["in_ports"]  = 5;
  _int_map["out_ports"] = 5;
  _int_map["voq"] = 0; //output queuing

  //========================================================
  // Router options
  //========================================================

  //==== General options ===================================

  AddStrField( "router", "iq" );

  _int_map["output_delay"] = 0;
  _int_map["credit_delay"] = 0;
  _float_map["internal_speedup"] = 1.0;

  //==== Input-queued ======================================

  // Control of virtual channel speculation
  _int_map["speculative"] = 0 ;

  // what to use to inhibit speculative allocator grants?
  AddStrField("filter_spec_grants", "confl_nonspec_gnts");

  _int_map["num_vcs"]         = 16;
  _int_map["vc_buf_size"]     = 8;
  _int_map["shared_buf_size"] = 0;
  _int_map["dynamic_sharing"] = 0;

  _int_map["wait_for_tail_credit"] = 0; // reallocate a VC before a tail credit?
  _int_map["vc_busy_when_full"] = 0; // mark VCs as in use when they have no credit available
  _int_map["vc_priority_donation"] = 0; // allow high-priority flits to donate their priority to low-priority that they are queued up behind
  _int_map["replies_inherit_priority"] = 0; // whenusing request-reply traffic (use_read_write=1) with age-based priority, make replies inherit their corresponding requests' age

  _int_map["hold_switch_for_packet"] = 0; // hold a switch config for the entire packet

  _int_map["input_speedup"]     = 1;  // expansion of input ports into crossbar
  _int_map["output_speedup"]    = 1;  // expansion of output ports into crossbar

  _int_map["routing_delay"]    = 1;
  _int_map["vc_alloc_delay"]   = 1;
  _int_map["sw_alloc_delay"]   = 1;
  _int_map["st_prepare_delay"] = 0;
  _int_map["st_final_delay"]   = 1;

  //==== Event-driven =====================================

  _int_map["vct"] = 0;

  //==== Allocators ========================================

  AddStrField( "vc_allocator", "islip" );
  AddStrField( "sw_allocator", "islip" );

  AddStrField( "vc_alloc_arb_type", "round_robin" );
  AddStrField( "sw_alloc_arb_type", "round_robin" );

  _int_map["alloc_iters"] = 1;

  // dub: allow setting the number of iterations for each allocator separately
  // (a value of 0 indicates it should inherit its value from alloc_iters)
  _int_map["vc_alloc_iters"] = 0;
  _int_map["sw_alloc_iters"] = 0;

  //==== Traffic ========================================

  AddStrField( "traffic", "uniform" );

  AddStrField( "hotspot_nodes", "" );
  AddStrField( "hotspot_rates", "" );

  AddStrField( "combined_patterns", "" );
  AddStrField( "combined_rates", "" );

  _int_map["perm_seed"] = 0;         // seed value for random permuation trafficpattern generator

  _float_map["injection_rate"]       = 0.1; //if 0.0 assumes it is batch mode
  _int_map["injection_rate_uses_flits"] = 0;

  _int_map["const_flits_per_packet"] = 1; //use  read_request_size etc insted

  AddStrField( "injection_process", "bernoulli" );

  _float_map["burst_alpha"] = 0.5; // burst interval
  _float_map["burst_beta"]  = 0.5; // burst length

  AddStrField( "priority", "none" );  // message priorities

  _int_map["batch_size"] = 1000;
  _int_map["batch_count"] = 1;
  _int_map["max_outstanding_requests"] = 4;

  // Use read/write request reply scheme
  _int_map["use_read_write"] = 0;

  // Control assignment of packets to VCs
  _int_map["read_request_begin_vc"] = 0;
  _int_map["read_request_end_vc"] = 5;
  _int_map["write_request_begin_vc"] = 2;
  _int_map["write_request_end_vc"] = 7;
  _int_map["read_reply_begin_vc"] = 8;
  _int_map["read_reply_end_vc"] = 13;
  _int_map["write_reply_begin_vc"] = 10;
  _int_map["write_reply_end_vc"] = 15;

  // Control Injection of Packets into Replicated Networks
  _int_map["read_request_subnet"] = 0;
  _int_map["read_reply_subnet"] = 0;
  _int_map["write_request_subnet"] = 0;
  _int_map["write_reply_subnet"] = 0;

  // Set packet length in flits
  _int_map["read_request_size"]  = 1;
  _int_map["write_request_size"] = 1;
  _int_map["read_reply_size"]    = 1;
  _int_map["write_reply_size"]   = 1;

  //==== Simulation parameters ==========================

  // types:
  //   latency    - average + latency distribution for a particular injection rate
  //   throughput - sustained throughput for a particular injection rate

  AddStrField( "sim_type", "latency" );

  _int_map["warmup_periods"] = 3; // number of samples periods to "warm-up" the simulation

  _int_map["sample_period"] = 1000; // how long between measurements
  _int_map["max_samples"]   = 10;   // maximum number of sample periods in a simulation

  _float_map["latency_thres"] = 500.0; // if avg. latency exceeds the threshold, assume unstable
  _float_map["warmup_thres"] = 0.05; // consider warmed up once relative change in latency and throughput between successive iterations is smaller than this

  // consider converged once relative change in latency / throughput between successive iterations is smaller than this
  _float_map["stopping_thres"] = 0.05;
  _float_map["acc_stopping_thres"] = 0.05;

  _int_map["sim_count"]     = 1;   // number of simulations to perform

  _int_map["include_queuing"] =1; // non-zero includes source queuing latency

  //  _int_map["reorder"]         = 0;  // know what you're doing

  //_int_map["flit_timing"]     = 0;  // know what you're doing
  //_int_map["split_packets"]   = 0;  // know what you're doing

  _int_map["seed"]            = 0; //random seed for simulation, e.g. traffic

  _int_map["print_activity"] = 0;

  _int_map["print_csv_results"] = 0;
  _int_map["print_vc_stats"] = 0;

  _int_map["deadlock_warn_timeout"] = 256;

  _int_map["drain_measured_only"] = 0;

  _int_map["viewer_trace"] = 0;

  AddStrField("watch_file", "");

  AddStrField("watch_flits", "");
  AddStrField("watch_packets", "");

  AddStrField("watch_out", "");

  AddStrField("stats_out", "");
  AddStrField("flow_out", "");

  //==================Power model params=====================
  _int_map["sim_power"] = 0;
  AddStrField("power_output_file","pwr_tmp");
  AddStrField("tech_file", "../utils/temp");
  _int_map["channel_width"] = 128;
  _int_map["channel_sweep"] = 0;

  //==================Network file===========================
  AddStrField("network_file","");
}

#ifdef USE_GUI

//A list of important simulator for the booksim gui, anything else not listed here is still included
//but just not very organized
vector< pair<string, vector< string> > > *BookSimConfig::GetImportantMap(){
  //Vector of 5 categories, each category is a vector of potions. Maps don't work because it autosorts
  vector< pair<string, vector< string> > > *important = new  vector< pair<string, vector< string> > >;
  important->push_back( make_pair( "Topology", vector<string>() ));
  (*important)[0].second.push_back("topology");
  (*important)[0].second.push_back("k");
  (*important)[0].second.push_back("n");
  (*important)[0].second.push_back("c");
  (*important)[0].second.push_back( "routing_function");
  (*important)[0].second.push_back("use_noc_latency");

  important->push_back(make_pair("Router", vector<string>()));
  (*important)[1].second.push_back("router");
  (*important)[1].second.push_back("num_vcs");
  (*important)[1].second.push_back("vc_buf_size");
  (*important)[1].second.push_back("routing_delay");
  (*important)[1].second.push_back("vc_alloc_delay");
  (*important)[1].second.push_back("sw_alloc_delay");
  (*important)[1].second.push_back("st_prepare_delay");
  (*important)[1].second.push_back("st_final_delay");

  important->push_back(make_pair("Allocator", vector<string>()));
  (*important)[2].second.push_back("vc_allocator");
  (*important)[2].second.push_back("vc_alloc_arb_type");
  (*important)[2].second.push_back("sw_allocator");
  (*important)[2].second.push_back("sw_alloc_arb_type");
  (*important)[2].second.push_back( "priority");
  (*important)[2].second.push_back("speculative");

  important->push_back(make_pair("Simulation", vector<string>()));
 (*important)[3].second.push_back("traffic");
 (*important)[3].second.push_back("injection_rate");
 (*important)[3].second.push_back("injection_rate_uses_flits");
 (*important)[3].second.push_back("sim_type");
 (*important)[3].second.push_back("latency_thres");
 (*important)[3].second.push_back("const_flits_per_packet");
 (*important)[3].second.push_back("injection_process");
 (*important)[3].second.push_back("sample_period");

  important->push_back(make_pair("Statistics", vector<string>()));
  (*important)[4].second.push_back("print_activity");
  (*important)[4].second.push_back("print_csv_results");
  (*important)[4].second.push_back("print_vc_stats");
  (*important)[4].second.push_back("stats_out");
  (*important)[4].second.push_back("sim_power");
  (*important)[4].second.push_back("power_output_file");

  return important;
}

#endif

vector<string> BookSimConfig::tokenize(string data) {
  const string separator = "{,}";
  vector<string> result;
  size_t last_pos = data.find_first_not_of(separator);
  size_t pos = data.find_first_of(separator, last_pos);
  while(pos != string::npos || last_pos != string::npos) {
    result.push_back(data.substr(last_pos, pos - last_pos));
    last_pos = data.find_first_not_of(separator, pos);
    pos = data.find_first_of(separator, last_pos);
  }
  return result;
}

PowerConfig::PowerConfig( )
{

  _int_map["H_INVD2"] = 0;
  _int_map["W_INVD2"] = 0;
  _int_map["H_DFQD1"] = 0;
  _int_map["W_DFQD1"] = 0;
  _int_map["H_ND2D1"] = 0;
  _int_map["W_ND2D1"] = 0;
  _int_map["H_SRAM"] = 0;
  _int_map["W_SRAM"] = 0;
  _float_map["Vdd"] = 0;
  _float_map["R"] = 0;
  _float_map["IoffSRAM"] = 0;
  _float_map["IoffP"] = 0;
  _float_map["IoffN"] = 0;
  _float_map["Cg_pwr"] = 0;
  _float_map["Cd_pwr"] = 0;
  _float_map["Cgdl"] = 0;
  _float_map["Cg"] = 0;
  _float_map["Cd"] = 0;
  _float_map["LAMBDA"] = 0;
  _float_map["MetalPitch"] = 0;
  _float_map["Rw"] = 0;
  _float_map["Cw_gnd"] = 0;
  _float_map["Cw_cpl"] = 0;
  _float_map["wire_length"] = 0;

}
<|endoftext|>


<commit_before>#include <array>
#include <iterator>
#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>

#include "builtin.hh"
#include "util.hh"
#include "number.hh"
#include "procedure.hh"
#include "lisp_ptr.hh"
#include "eval.hh"
#include "builtin_util.hh"
#include "printer.hh"

using namespace std;
using namespace Procedure;

namespace {

template<typename Fun>
inline void number_pred(Fun&& fun){
  auto arg = pick_args_1();
  auto num = arg.get<Number*>();
  if(!num){
    VM.return_value = Lisp_ptr{false};
    return;
  }

  VM.return_value = Lisp_ptr{fun(num)};
}

void complexp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer;
    });
}

void realp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() <= Number::Type::real;
    });
}

void rationalp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() < Number::Type::real;
    });
}

void integerp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void exactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void inexactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::complex
        || n->type() == Number::Type::real;
    });
}

void number_type_check_failed(const char* func_name, Lisp_ptr p){
  fprintf(zs::err, "native func: %s: arg is not number! (%s)\n",
          func_name, stringify(p.tag()));
  VM.return_value = {};
}

struct complex_found{
  bool operator()(const Number::complex_type&, const Number::complex_type&) const{
    fprintf(zs::err, "native func: number compare: complex cannot be ordinated\n");
    return true;
  }
};

template<template <typename> class Fun,
         class ComplexComparator = complex_found>
struct number_equal_pred {
  Lisp_ptr non_number_found;

  number_equal_pred() : non_number_found(){}

  inline bool operator()(const Lisp_ptr& p1, const Lisp_ptr& p2){
    auto n1 = p1.get<Number*>();
    if(!n1 || n1->type() < Number::Type::integer){
      non_number_found = p1;
      return true;
    }

    auto n2 = p2.get<Number*>();
    if(!n2 || n2->type() < Number::Type::integer){
      non_number_found = p2;
      return true;
    }

    if(n1->type() == Number::Type::integer && n2->type() == Number::Type::integer){
      static const Fun<Number::integer_type> fun;
      return !fun(n2->get<Number::integer_type>(), n1->get<Number::integer_type>());
    }else if(n1->type() <= Number::Type::real && n2->type() <= Number::Type::real){
      static const Fun<Number::real_type> fun;
      return !fun(n2->coerce<Number::real_type>(), n1->coerce<Number::real_type>());
    }else{
      static const ComplexComparator fun;
      return !fun(n2->coerce<Number::complex_type>(), n1->coerce<Number::complex_type>());
    }
  }
};

template<typename Fun>
void number_compare(const char* name, Fun&& fun){
  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  auto ret = std::is_sorted(begin(args), end(args), fun);
  if(fun.non_number_found){
    number_type_check_failed(name, fun.non_number_found);
    return;
  }

  VM.return_value = Lisp_ptr{ret};
}

void number_equal(){
  number_compare("=", number_equal_pred<std::equal_to,
                                        std::equal_to<Number::complex_type> >());
}

void number_less(){
  number_compare("<", number_equal_pred<std::less>());
}

void number_greater(){
  number_compare(">", number_equal_pred<std::greater>());
}

void number_less_eq(){
  number_compare("<=", number_equal_pred<std::less_equal>());
}

void number_greater_eq(){
  number_compare(">=", number_equal_pred<std::greater_equal>());
}

bool number_comparable_check(Number* n){
  switch(n->type()){
  case Number::Type::complex:
    fprintf(zs::err, "native func: number compare: complex cannot be ordinated\n");
    VM.return_value = {};
    return false;
  case Number::Type::real:
  case Number::Type::integer:
    return true;
  case Number::Type::uninitialized:
  default:
    UNEXP_DEFAULT();
  }
}

void zerop(){
  number_pred([](Number* num) -> bool {
      switch(num->type()){
      case Number::Type::complex: {
        auto c = num->get<Number::complex_type>();
        return (c.real() == 0 && c.imag() == 0);
      }
      case Number::Type::real:
        return num->get<Number::real_type>() == 0;
      case Number::Type::integer:
        return num->get<Number::integer_type>() == 0;
      case Number::Type::uninitialized:
      default:
        UNEXP_DEFAULT();
      }
    });
}

template<template <typename> class Fun>
struct pos_neg_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
      return false;
    case Number::Type::real:
      return fun(num->get<Number::real_type>(), 0);
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>(), 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void positivep(){
  number_pred(pos_neg_pred<std::greater>());
}

void negativep(){
  number_pred(pos_neg_pred<std::less>());
}

template<template <typename> class Fun>
struct even_odd_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
    case Number::Type::real:
      return false;
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>() % 2, 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void oddp(){
  number_pred(even_odd_pred<std::not_equal_to>());
}

void evenp(){
  number_pred(even_odd_pred<std::equal_to>());
}

template<template <typename> class Fun>
void number_minmax(const char* name){
  static constexpr Fun<Number::real_type> fun;

  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  auto n_obj = args.front().get<Number*>();
  if(!n_obj){
    number_type_check_failed(name, args.front());
    return;
  }

  if(!number_comparable_check(n_obj)){
    return;
  }

  auto minmax_n_obj = n_obj;
  auto minmax_n = n_obj->coerce<Number::real_type>();

  for(auto i = next(begin(args)), e = end(args);
      i != e; ++i){
    n_obj = i->get<Number*>();
    if(!n_obj){
      number_type_check_failed(name, *i);
      return;
    }

    if(!number_comparable_check(n_obj)){
      return;
    }

    auto n2 = n_obj->coerce<Number::real_type>();

    if(fun(n2, minmax_n)){
      minmax_n_obj = n_obj;
      minmax_n = n2;
    }
  }

  VM.return_value = Lisp_ptr{minmax_n_obj};
}

void number_max(){
  number_minmax<std::greater>("max");
}

void number_min(){
  number_minmax<std::less>("min");
}

void plus_2(){
  auto args = pick_args<2>();

  VM.return_value = {};

  Number* n1 = args[0].get<Number*>();
  if(!n1){
    fprintf(zs::err, "native func '+': first arg is not number! %s\n",
            stringify(args[0].tag()));
    return;
  }

  Number* n2 = args[1].get<Number*>();
  if(!n2){
    fprintf(zs::err, "native func '+': second arg is not number! %s\n",
            stringify(args[1].tag()));
    return;
  }

  Number* newn = new Number(n1->get<long>() + n2->get<long>());
  VM.return_value = newn;
}

constexpr struct Entry {
  const char* name;
  const NProcedure func;

  constexpr Entry(const char* n, const NProcedure& f)
    : name(n), func(f){}
} builtin_numeric[] = {
  {"complex?", {
      complexp,
      Calling::function, {1, false}}},
  {"real?", {
      realp,
      Calling::function, {1, false}}},
  {"rational?", {
      rationalp,
      Calling::function, {1, false}}},
  {"integer?", {
      integerp,
      Calling::function, {1, false}}},

  {"exact?", {
      exactp,
      Calling::function, {1, false}}},
  {"inexact?", {
      inexactp,
      Calling::function, {1, false}}},

  {"=", {
      number_equal,
      Calling::function, {2, true}}},
  {"<", {
      number_less,
      Calling::function, {2, true}}},
  {">", {
      number_greater,
      Calling::function, {2, true}}},
  {"<=", {
      number_less_eq,
      Calling::function, {2, true}}},
  {">=", {
      number_greater_eq,
      Calling::function, {2, true}}},

  {"zero?", {
      zerop,
      Calling::function, {1, false}}},
  {"positive?", {
      positivep,
      Calling::function, {1, false}}},
  {"negative?", {
      negativep,
      Calling::function, {1, false}}},
  {"odd?", {
      oddp,
      Calling::function, {1, false}}},
  {"even?", {
      evenp,
      Calling::function, {1, false}}},

  {"max", {
      number_max,
      Calling::function, {2, true}}},
  {"min", {
      number_min,
      Calling::function, {2, true}}},

  {"+", {
      plus_2,
      Calling::function, {2, true}}}
};

} //namespace

void install_builtin_numeric(){
  for(auto& e : builtin_numeric){
    VM.set(intern(VM.symtable, e.name), {&e.func});
  }
}
<commit_msg>cneanup<commit_after>#include <array>
#include <iterator>
#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>

#include "builtin.hh"
#include "util.hh"
#include "number.hh"
#include "procedure.hh"
#include "lisp_ptr.hh"
#include "eval.hh"
#include "builtin_util.hh"
#include "printer.hh"

using namespace std;
using namespace Procedure;

namespace {

template<typename Fun>
inline void number_pred(Fun&& fun){
  auto arg = pick_args_1();
  auto num = arg.get<Number*>();
  if(!num){
    VM.return_value = Lisp_ptr{false};
    return;
  }

  VM.return_value = Lisp_ptr{fun(num)};
}

void complexp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer;
    });
}

void realp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() <= Number::Type::real;
    });
}

void rationalp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() < Number::Type::real;
    });
}

void integerp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void exactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void inexactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::complex
        || n->type() == Number::Type::real;
    });
}

void number_type_check_failed(const char* func_name, Lisp_ptr p){
  fprintf(zs::err, "native func: %s: arg is not number! (%s)\n",
          func_name, stringify(p.tag()));
  VM.return_value = {};
}

struct complex_found{
  bool operator()(const Number::complex_type&, const Number::complex_type&) const{
    fprintf(zs::err, "native func: number compare: complex cannot be ordinated\n");
    return true;
  }
};

template<template <typename> class Fun,
         class ComplexComparator = complex_found>
struct number_comparator {
  Lisp_ptr non_number_found;

  number_comparator() : non_number_found(){}

  inline bool operator()(const Lisp_ptr& p1, const Lisp_ptr& p2){
    auto n1 = p1.get<Number*>();
    if(!n1 || n1->type() < Number::Type::integer){
      non_number_found = p1;
      return true;
    }

    auto n2 = p2.get<Number*>();
    if(!n2 || n2->type() < Number::Type::integer){
      non_number_found = p2;
      return true;
    }

    if(n1->type() == Number::Type::integer && n2->type() == Number::Type::integer){
      static const Fun<Number::integer_type> fun;
      return fun(n1->get<Number::integer_type>(), n2->get<Number::integer_type>());
    }else if(n1->type() <= Number::Type::real && n2->type() <= Number::Type::real){
      static const Fun<Number::real_type> fun;
      return fun(n1->coerce<Number::real_type>(), n2->coerce<Number::real_type>());
    }else{
      static const ComplexComparator fun;
      return fun(n1->coerce<Number::complex_type>(), n2->coerce<Number::complex_type>());
    }
  }

  struct for_is_sorted : public number_comparator {
    inline bool operator()(const Lisp_ptr& p1, const Lisp_ptr& p2){
      return !number_comparator::operator()(p2, p1);
    }
  };
};

template<typename Fun>
inline void number_compare(const char* name, Fun&& fun){
  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  auto ret = std::is_sorted(begin(args), end(args), fun);
  if(fun.non_number_found){
    number_type_check_failed(name, fun.non_number_found);
    return;
  }

  VM.return_value = Lisp_ptr{ret};
}

void number_equal(){
  number_compare("=",
                 number_comparator<std::equal_to,
                                   std::equal_to<Number::complex_type> >::for_is_sorted());
}

void number_less(){
  number_compare("<",
                 number_comparator<std::less>::for_is_sorted());
}

void number_greater(){
  number_compare(">",
                 number_comparator<std::greater>::for_is_sorted());
}

void number_less_eq(){
  number_compare("<=",
                 number_comparator<std::less_equal>::for_is_sorted());
}

void number_greater_eq(){
  number_compare(">=",
                 number_comparator<std::greater_equal>::for_is_sorted());
}

bool number_comparable_check(Number* n){
  switch(n->type()){
  case Number::Type::complex:
    fprintf(zs::err, "native func: number compare: complex cannot be ordinated\n");
    VM.return_value = {};
    return false;
  case Number::Type::real:
  case Number::Type::integer:
    return true;
  case Number::Type::uninitialized:
  default:
    UNEXP_DEFAULT();
  }
}

void zerop(){
  number_pred([](Number* num) -> bool {
      switch(num->type()){
      case Number::Type::complex: {
        auto c = num->get<Number::complex_type>();
        return (c.real() == 0 && c.imag() == 0);
      }
      case Number::Type::real:
        return num->get<Number::real_type>() == 0;
      case Number::Type::integer:
        return num->get<Number::integer_type>() == 0;
      case Number::Type::uninitialized:
      default:
        UNEXP_DEFAULT();
      }
    });
}

template<template <typename> class Fun>
struct pos_neg_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
      return false;
    case Number::Type::real:
      return fun(num->get<Number::real_type>(), 0);
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>(), 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void positivep(){
  number_pred(pos_neg_pred<std::greater>());
}

void negativep(){
  number_pred(pos_neg_pred<std::less>());
}

template<template <typename> class Fun>
struct even_odd_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
    case Number::Type::real:
      return false;
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>() % 2, 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void oddp(){
  number_pred(even_odd_pred<std::not_equal_to>());
}

void evenp(){
  number_pred(even_odd_pred<std::equal_to>());
}

template<template <typename> class Fun>
void number_minmax(const char* name){
  static constexpr Fun<Number::real_type> fun;

  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  auto n_obj = args.front().get<Number*>();
  if(!n_obj){
    number_type_check_failed(name, args.front());
    return;
  }

  if(!number_comparable_check(n_obj)){
    return;
  }

  auto minmax_n_obj = n_obj;
  auto minmax_n = n_obj->coerce<Number::real_type>();

  for(auto i = next(begin(args)), e = end(args);
      i != e; ++i){
    n_obj = i->get<Number*>();
    if(!n_obj){
      number_type_check_failed(name, *i);
      return;
    }

    if(!number_comparable_check(n_obj)){
      return;
    }

    auto n2 = n_obj->coerce<Number::real_type>();

    if(fun(n2, minmax_n)){
      minmax_n_obj = n_obj;
      minmax_n = n2;
    }
  }

  VM.return_value = Lisp_ptr{minmax_n_obj};
}

void number_max(){
  number_minmax<std::greater>("max");
}

void number_min(){
  number_minmax<std::less>("min");
}

void plus_2(){
  auto args = pick_args<2>();

  VM.return_value = {};

  Number* n1 = args[0].get<Number*>();
  if(!n1){
    fprintf(zs::err, "native func '+': first arg is not number! %s\n",
            stringify(args[0].tag()));
    return;
  }

  Number* n2 = args[1].get<Number*>();
  if(!n2){
    fprintf(zs::err, "native func '+': second arg is not number! %s\n",
            stringify(args[1].tag()));
    return;
  }

  Number* newn = new Number(n1->get<long>() + n2->get<long>());
  VM.return_value = newn;
}

constexpr struct Entry {
  const char* name;
  const NProcedure func;

  constexpr Entry(const char* n, const NProcedure& f)
    : name(n), func(f){}
} builtin_numeric[] = {
  {"complex?", {
      complexp,
      Calling::function, {1, false}}},
  {"real?", {
      realp,
      Calling::function, {1, false}}},
  {"rational?", {
      rationalp,
      Calling::function, {1, false}}},
  {"integer?", {
      integerp,
      Calling::function, {1, false}}},

  {"exact?", {
      exactp,
      Calling::function, {1, false}}},
  {"inexact?", {
      inexactp,
      Calling::function, {1, false}}},

  {"=", {
      number_equal,
      Calling::function, {2, true}}},
  {"<", {
      number_less,
      Calling::function, {2, true}}},
  {">", {
      number_greater,
      Calling::function, {2, true}}},
  {"<=", {
      number_less_eq,
      Calling::function, {2, true}}},
  {">=", {
      number_greater_eq,
      Calling::function, {2, true}}},

  {"zero?", {
      zerop,
      Calling::function, {1, false}}},
  {"positive?", {
      positivep,
      Calling::function, {1, false}}},
  {"negative?", {
      negativep,
      Calling::function, {1, false}}},
  {"odd?", {
      oddp,
      Calling::function, {1, false}}},
  {"even?", {
      evenp,
      Calling::function, {1, false}}},

  {"max", {
      number_max,
      Calling::function, {2, true}}},
  {"min", {
      number_min,
      Calling::function, {2, true}}},

  {"+", {
      plus_2,
      Calling::function, {2, true}}}
};

} //namespace

void install_builtin_numeric(){
  for(auto& e : builtin_numeric){
    VM.set(intern(VM.symtable, e.name), {&e.func});
  }
}
<|endoftext|>


<commit_before>/*
* Copyright (C) 2010,2011  Toni Gundogdu <legatvs@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"

#include <fstream>
#include <cstring>

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

#ifndef foreach
#define foreach BOOST_FOREACH
#endif

#include "cclive/re.h"
#include "cclive/options.h"

namespace cclive
{

namespace opts = boost::program_options;
namespace fs   = boost::filesystem;

void
options::exec (int argc, char **argv)
{

  // Path to ccliverc.

  fs::path config_path (fs::initial_path<fs::path> ());

  const char *home = getenv ("HOME");

  if (home && strlen (home) > 0)
    config_path = fs::system_complete (fs::path (home));

  config_path /=
#ifndef _WIN32
    std::string (".") +
#endif
    std::string ("ccliverc");

  // Construct options.

  opts::options_description generic;

  generic.add_options()
  ("version",
   "Print version and exit")
  ("help",
   "Print help and exit")
  ("license",
   "Print license and exit")
  ("support",
   "Print supported websites and exit")
  ("verbose-libcurl",
   "Turn on libcurl verbose output")
  ("quiet,q",
   "Turn off all output, excl. errors")
#ifdef HAVE_FORK
  ("background,b",
   "Go to background")
#endif
  ("format,f",
   opts::value<std::string>()->default_value("default"),
   "Download video format")
  ("continue,c",
   "Resume partially downloaded video")
  ("overwrite,W",
   "Overwrite existing video")
  ("output-file,O",
   opts::value<std::string>(),
   "Write downloaded video to file")
  ("no-download,n",
   "Do not download video, print info only")
#ifdef HAVE_QUVIOPT_NOSHORTENED
  ("no-shortened,s",
   "Do not decompress shortened URLs")
#endif
  ("no-proxy",
   "Disable use of http proxy")
  ("log-file",
   opts::value<std::string>()->default_value("cclive_log"),
   "Write log output to arg")
  ("update-interval",
   opts::value<double>()->default_value(1.0),
   "Update interval of progressbar")
  ("config-file",
   opts::value<std::string>(&_config_file)
   ->default_value(config_path.string()),
   "File to read cclive arguments from")
  ;

  // Config.

  opts::options_description config ("Configuration");

  config.add_options ()
  ("filename-format",
   opts::value<std::string>()->default_value("%t.%s"),
   "Output video filename format")
  ("output-dir",
   opts::value<std::string>(),
   "Output directory for downloaded videos")
  ("regexp",
   opts::value<std::string>()->default_value("/(\\w|\\pL|\\s)/g"),
   "Regexp to clean up video title")
  ("subst", opts::value<std::string>(),
   "Replace matched occurences in filename")
  ("exec", opts::value<std::string>(),
   "Invoke arg after download finishes")
  ("agent",
   opts::value<std::string>()->default_value("Mozilla/5.0"),
   "Identify as arg to http servers")
  ("proxy", opts::value<std::string>(),
   "Use proxy for http connections")
  ("throttle", opts::value<int>()->default_value(0),
   "Limit download transfer rate to KB/s")
  ("connect-timeout", opts::value<int>()->default_value(30),
   "Seconds connecting allowed to take")
  ("transfer-timeout", opts::value<int>()->default_value(0),
   "Seconds transfer allowed to take")
  ("dns-cache-timeout", opts::value<int>()->default_value(60),
   "Seconds dns resolves kept in memory")
  ("max-retries", opts::value<int>()->default_value(5),
   "Max download attempts before giving up")
  ("retry-wait", opts::value<int>()->default_value(5),
   "Time to wait before retrying")
  ;

  // Hidden.

  opts::options_description hidden;

  hidden.add_options ()
  ("url", opts::value< std::vector<std::string> >(),
   "url")
  ;

  // Visible.

  _visible.add (generic).add (config);

  // Command line options.

  opts::options_description cmdline_options;

  cmdline_options.add (generic).add (config).add (hidden);

  // Config file options.

  opts::options_description config_file_options;

  config_file_options.add (config);

  // Positional.

  opts::positional_options_description p;
  p.add ("url", -1);

  // Parse.

  store(opts::command_line_parser (argc,argv)
        .options (cmdline_options).positional (p).run (), _map);

  notify (_map);

  // Read config.

  std::ifstream ifs (_config_file.c_str ());

  if (ifs)
    {
      store (parse_config_file (ifs, config_file_options), _map);
      notify (_map);
    }

  _verify ();
}

const opts::variables_map& options::map () const
{
  return _map;
}

std::ostream& operator<<(std::ostream& os, const options& o)
{
  return os << o._visible;
}

void
options::_verify ()
{

  std::string empty;

  if (_map.count ("regexp"))
    {

      std::string s = _map["regexp"].as<std::string>();

      if (!cclive::re::match (s, empty))
        {

          std::stringstream b;

          b << "invalid syntax (`" << s << "'), "
            << "expected perl syntax with --regexp,\n"
            << "\te.g.: \"/(\\w|\\s)/g\"";

          throw std::runtime_error (b.str ());
        }

    }

  if (_map.count ("subst"))
    {

      std::istringstream iss( _map["subst"].as<std::string>());
      std::vector<std::string> v;

      std::copy(
        std::istream_iterator<std::string >(iss),
        std::istream_iterator<std::string >(),
        std::back_inserter<std::vector<std::string> >(v)
      );

      foreach (std::string s, v)
      {

        if (!cclive::re::subst (s,empty))
          {

            std::stringstream b;

            b << "invalid syntax (`" << s << "'), "
              << "expected perl like syntax with --subst, e.g.:\n"
              << "\ts/old/{new}/g\n"
              << "\ts{old}/new/\n"
              << "\ts(old)<new>/i\n"
              << "\ts<old>{new}/";

            throw std::runtime_error (b.str ());
          }
      }
    }
}

} // End namespace.

// vim: set ts=2 sw=2 tw=72 expandtab:
<commit_msg>options.cpp: Remove extra newlines<commit_after>/*
* Copyright (C) 2010,2011  Toni Gundogdu <legatvs@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"

#include <fstream>
#include <cstring>

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

#ifndef foreach
#define foreach BOOST_FOREACH
#endif

#include "cclive/re.h"
#include "cclive/options.h"

namespace cclive
{

namespace opts = boost::program_options;
namespace fs   = boost::filesystem;

void
options::exec (int argc, char **argv)
{
  // Path to ccliverc.

  fs::path config_path (fs::initial_path<fs::path> ());

  const char *home = getenv ("HOME");

  if (home && strlen (home) > 0)
    config_path = fs::system_complete (fs::path (home));

  config_path /=
#ifndef _WIN32
    std::string (".") +
#endif
    std::string ("ccliverc");

  // Construct options.

  opts::options_description generic;

  generic.add_options()
  ("version",
   "Print version and exit")
  ("help",
   "Print help and exit")
  ("license",
   "Print license and exit")
  ("support",
   "Print supported websites and exit")
  ("verbose-libcurl",
   "Turn on libcurl verbose output")
  ("quiet,q",
   "Turn off all output, excl. errors")
#ifdef HAVE_FORK
  ("background,b",
   "Go to background")
#endif
  ("format,f",
   opts::value<std::string>()->default_value("default"),
   "Download video format")
  ("continue,c",
   "Resume partially downloaded video")
  ("overwrite,W",
   "Overwrite existing video")
  ("output-file,O",
   opts::value<std::string>(),
   "Write downloaded video to file")
  ("no-download,n",
   "Do not download video, print info only")
#ifdef HAVE_QUVIOPT_NOSHORTENED
  ("no-shortened,s",
   "Do not decompress shortened URLs")
#endif
  ("no-proxy",
   "Disable use of http proxy")
  ("log-file",
   opts::value<std::string>()->default_value("cclive_log"),
   "Write log output to arg")
  ("update-interval",
   opts::value<double>()->default_value(1.0),
   "Update interval of progressbar")
  ("config-file",
   opts::value<std::string>(&_config_file)
   ->default_value(config_path.string()),
   "File to read cclive arguments from")
  ;

  // Config.

  opts::options_description config ("Configuration");

  config.add_options ()
  ("filename-format",
   opts::value<std::string>()->default_value("%t.%s"),
   "Output video filename format")
  ("output-dir",
   opts::value<std::string>(),
   "Output directory for downloaded videos")
  ("regexp",
   opts::value<std::string>()->default_value("/(\\w|\\pL|\\s)/g"),
   "Regexp to clean up video title")
  ("subst", opts::value<std::string>(),
   "Replace matched occurences in filename")
  ("exec", opts::value<std::string>(),
   "Invoke arg after download finishes")
  ("agent",
   opts::value<std::string>()->default_value("Mozilla/5.0"),
   "Identify as arg to http servers")
  ("proxy", opts::value<std::string>(),
   "Use proxy for http connections")
  ("throttle", opts::value<int>()->default_value(0),
   "Limit download transfer rate to KB/s")
  ("connect-timeout", opts::value<int>()->default_value(30),
   "Seconds connecting allowed to take")
  ("transfer-timeout", opts::value<int>()->default_value(0),
   "Seconds transfer allowed to take")
  ("dns-cache-timeout", opts::value<int>()->default_value(60),
   "Seconds dns resolves kept in memory")
  ("max-retries", opts::value<int>()->default_value(5),
   "Max download attempts before giving up")
  ("retry-wait", opts::value<int>()->default_value(5),
   "Time to wait before retrying")
  ;

  // Hidden.

  opts::options_description hidden;

  hidden.add_options ()
  ("url", opts::value< std::vector<std::string> >(),
   "url")
  ;

  // Visible.

  _visible.add (generic).add (config);

  // Command line options.

  opts::options_description cmdline_options;

  cmdline_options.add (generic).add (config).add (hidden);

  // Config file options.

  opts::options_description config_file_options;

  config_file_options.add (config);

  // Positional.

  opts::positional_options_description p;
  p.add ("url", -1);

  // Parse.

  store(opts::command_line_parser (argc,argv)
        .options (cmdline_options).positional (p).run (), _map);

  notify (_map);

  // Read config.

  std::ifstream ifs (_config_file.c_str ());

  if (ifs)
    {
      store (parse_config_file (ifs, config_file_options), _map);
      notify (_map);
    }

  _verify ();
}

const opts::variables_map& options::map () const
{
  return _map;
}

std::ostream& operator<<(std::ostream& os, const options& o)
{
  return os << o._visible;
}

void
options::_verify ()
{
  std::string empty;

  if (_map.count ("regexp"))
    {
      std::string s = _map["regexp"].as<std::string>();

      if (!cclive::re::match (s, empty))
        {
          std::stringstream b;

          b << "invalid syntax (`" << s << "'), "
            << "expected perl syntax with --regexp,\n"
            << "\te.g.: \"/(\\w|\\s)/g\"";

          throw std::runtime_error (b.str ());
        }

    }

  if (_map.count ("subst"))
    {
      std::istringstream iss( _map["subst"].as<std::string>());
      std::vector<std::string> v;

      std::copy(
        std::istream_iterator<std::string >(iss),
        std::istream_iterator<std::string >(),
        std::back_inserter<std::vector<std::string> >(v)
      );

      foreach (std::string s, v)
      {
        if (!cclive::re::subst (s,empty))
          {
            std::stringstream b;

            b << "invalid syntax (`" << s << "'), "
              << "expected perl like syntax with --subst, e.g.:\n"
              << "\ts/old/{new}/g\n"
              << "\ts{old}/new/\n"
              << "\ts(old)<new>/i\n"
              << "\ts<old>{new}/";

            throw std::runtime_error (b.str ());
          }
      }
    }
}

} // End namespace.

// vim: set ts=2 sw=2 tw=72 expandtab:
<|endoftext|>