Browse Source

- Added option to have description on top
- renamed top most to always on top
- ChaiScript Support
- fixed spelling issues in freinds window

sabrogden 8 years ago
parent
commit
998fdfc8a0
74 changed files with 20157 additions and 52 deletions
  1. 32 0
      AdvGeneral.cpp
  2. 2 0
      AdvGeneral.h
  3. 94 13
      CP_Main.rc
  4. 12 0
      CP_Main.vcxproj
  5. 20 0
      CP_Main.vcxproj.filters
  6. 75 0
      ChaiScriptOnCopy.cpp
  7. 16 0
      ChaiScriptOnCopy.h
  8. 98 0
      ChaiScriptXml.cpp
  9. 30 0
      ChaiScriptXml.h
  10. 38 0
      Clip.cpp
  11. 72 4
      CopyProperties.cpp
  12. 2 0
      CopyProperties.h
  13. 4 4
      Debug/Language/English.xml
  14. 94 0
      DittoChaiScript.cpp
  15. 24 0
      DittoChaiScript.h
  16. 28 1
      Options.cpp
  17. 9 0
      Options.h
  18. 9 3
      QListCtrl.cpp
  19. 1 1
      QListCtrl.h
  20. 18 11
      QPasteWnd.cpp
  21. 1 0
      QPasteWnd.h
  22. 26 3
      Resource.h
  23. 241 0
      ScriptEditor.cpp
  24. 43 0
      ScriptEditor.h
  25. 2 2
      StdAfx.h
  26. 75 10
      ToolTipEx.cpp
  27. 5 0
      ToolTipEx.h
  28. 845 0
      chaiscript/chaiscript.hpp
  29. 39 0
      chaiscript/chaiscript_basic.hpp
  30. 237 0
      chaiscript/chaiscript_defines.hpp
  31. 71 0
      chaiscript/chaiscript_stdlib.hpp
  32. 173 0
      chaiscript/chaiscript_threading.hpp
  33. 164 0
      chaiscript/dispatchkit/any.hpp
  34. 73 0
      chaiscript/dispatchkit/bad_boxed_cast.hpp
  35. 85 0
      chaiscript/dispatchkit/bind_first.hpp
  36. 589 0
      chaiscript/dispatchkit/bootstrap.hpp
  37. 753 0
      chaiscript/dispatchkit/bootstrap_stl.hpp
  38. 113 0
      chaiscript/dispatchkit/boxed_cast.hpp
  39. 323 0
      chaiscript/dispatchkit/boxed_cast_helper.hpp
  40. 942 0
      chaiscript/dispatchkit/boxed_number.hpp
  41. 485 0
      chaiscript/dispatchkit/boxed_value.hpp
  42. 107 0
      chaiscript/dispatchkit/callable_traits.hpp
  43. 1572 0
      chaiscript/dispatchkit/dispatchkit.hpp
  44. 131 0
      chaiscript/dispatchkit/dynamic_object.hpp
  45. 237 0
      chaiscript/dispatchkit/dynamic_object_detail.hpp
  46. 117 0
      chaiscript/dispatchkit/exception_specification.hpp
  47. 132 0
      chaiscript/dispatchkit/function_call.hpp
  48. 171 0
      chaiscript/dispatchkit/function_call_detail.hpp
  49. 256 0
      chaiscript/dispatchkit/handle_return.hpp
  50. 224 0
      chaiscript/dispatchkit/operators.hpp
  51. 56 0
      chaiscript/dispatchkit/proxy_constructors.hpp
  52. 989 0
      chaiscript/dispatchkit/proxy_functions.hpp
  53. 139 0
      chaiscript/dispatchkit/proxy_functions_detail.hpp
  54. 152 0
      chaiscript/dispatchkit/register_function.hpp
  55. 159 0
      chaiscript/dispatchkit/short_alloc.hpp
  56. 649 0
      chaiscript/dispatchkit/type_conversions.hpp
  57. 244 0
      chaiscript/dispatchkit/type_info.hpp
  58. 108 0
      chaiscript/language/chaiscript_algebraic.hpp
  59. 753 0
      chaiscript/language/chaiscript_common.hpp
  60. 710 0
      chaiscript/language/chaiscript_engine.hpp
  61. 1522 0
      chaiscript/language/chaiscript_eval.hpp
  62. 449 0
      chaiscript/language/chaiscript_optimizer.hpp
  63. 2596 0
      chaiscript/language/chaiscript_parser.hpp
  64. 81 0
      chaiscript/language/chaiscript_posix.hpp
  65. 562 0
      chaiscript/language/chaiscript_prelude.hpp
  66. 830 0
      chaiscript/language/chaiscript_prelude_docs.hpp
  67. 46 0
      chaiscript/language/chaiscript_tracer.hpp
  68. 31 0
      chaiscript/language/chaiscript_unknown.hpp
  69. 133 0
      chaiscript/language/chaiscript_windows.hpp
  70. 50 0
      chaiscript/utility/fnv1a.hpp
  71. 674 0
      chaiscript/utility/json.hpp
  72. 154 0
      chaiscript/utility/json_wrap.hpp
  73. 37 0
      chaiscript/utility/static_string.hpp
  74. 123 0
      chaiscript/utility/utility.hpp

+ 32 - 0
AdvGeneral.cpp

@@ -5,6 +5,8 @@
 #include "CP_Main.h"
 #include "AdvGeneral.h"
 #include "afxdialogex.h"
+#include "ScriptEditor.h"
+#include "DimWnd.h"
 
 
 // CAdvGeneral dialog
@@ -32,6 +34,8 @@ BEGIN_MESSAGE_MAP(CAdvGeneral, CDialogEx)
 	ON_BN_CLICKED(IDOK, &CAdvGeneral::OnBnClickedOk)
 	ON_WM_SIZE()
 	ON_BN_CLICKED(IDC_BT_COMPACT_AND_REPAIR, &CAdvGeneral::OnBnClickedBtCompactAndRepair)
+	ON_BN_CLICKED(IDC_BUTTON_COPY_SCRIPTS, &CAdvGeneral::OnBnClickedButtonCopyScripts)
+	ON_BN_CLICKED(IDC_BUTTON_PASTE_SCRIPTS, &CAdvGeneral::OnBnClickedButtonPasteScripts2)
 END_MESSAGE_MAP()
 
 
@@ -112,6 +116,8 @@ BOOL CAdvGeneral::OnInitDialog()
 	m_Resize.AddControl(IDOK, DR_MoveTop | DR_MoveLeft);
 	m_Resize.AddControl(IDCANCEL, DR_MoveTop | DR_MoveLeft);
 	m_Resize.AddControl(IDC_BT_COMPACT_AND_REPAIR, DR_MoveTop);
+	m_Resize.AddControl(IDC_BUTTON_COPY_SCRIPTS, DR_MoveTop);
+	m_Resize.AddControl(IDC_BUTTON_PASTE_SCRIPTS, DR_MoveTop);
 
 	HDITEM hdItem;
 	hdItem.mask = HDI_WIDTH; // indicating cxy is width
@@ -597,3 +603,29 @@ void CAdvGeneral::OnBnClickedBtCompactAndRepair()
 	}
 	CATCH_SQLITE_EXCEPTION
 }
+
+
+void CAdvGeneral::OnBnClickedButtonCopyScripts()
+{
+	CDimWnd dim(this->GetParent());
+
+	CScriptEditor e(this);
+	e.m_xml.Load(CGetSetOptions::GetCopyScriptsXml());
+	if (e.DoModal() == IDOK)
+	{
+		CGetSetOptions::SetCopyScriptsXml(e.m_xml.Save());
+	}
+}
+
+
+void CAdvGeneral::OnBnClickedButtonPasteScripts2()
+{
+	CDimWnd dim(this->GetParent());
+
+	CScriptEditor e(this);
+	e.m_xml.Load(CGetSetOptions::GetPasteScriptsXml());
+	if (e.DoModal() == IDOK)
+	{
+		CGetSetOptions::SetPasteScriptsXml(e.m_xml.Save());
+	}
+}

+ 2 - 0
AdvGeneral.h

@@ -27,4 +27,6 @@ public:
 	afx_msg void OnBnClickedOk();
 	afx_msg void OnSize(UINT nType, int cx, int cy);
 	afx_msg void OnBnClickedBtCompactAndRepair();
+	afx_msg void OnBnClickedButtonCopyScripts();
+	afx_msg void OnBnClickedButtonPasteScripts2();
 };

+ 94 - 13
CP_Main.rc

@@ -332,6 +332,7 @@ BEGIN
         MENUITEM "Scale images to fit window",  ID_FIRST_SCALEIMAGESTOFITWINDOW
         MENUITEM "Hide description window on mouse clip selection", ID_FIRST_HIDEDESCRIPTIONWINDOWONM
         MENUITEM "Wrap Text",                   ID_FIRST_WRAPTEXT
+        MENUITEM "Always on top",               ID_FIRST_ALWAYSONTOP
     END
 END
 
@@ -705,7 +706,7 @@ BEGIN
     CONTROL         "Custom Clipboard Type",IDC_RADIO_CUSTOM_TYPE,"Button",BS_AUTORADIOBUTTON,7,35,93,10
 END
 
-IDD_COPY_PROPERTIES DIALOGEX 0, 0, 293, 282
+IDD_COPY_PROPERTIES DIALOGEX 0, 0, 293, 289
 STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
 CAPTION "Copy Properties"
 FONT 8, "MS Shell Dlg", 400, 0, 0x1
@@ -716,8 +717,8 @@ BEGIN
     COMBOBOX        IDC_COMBO1,71,91,215,128,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
     LISTBOX         IDC_COPY_DATA,7,205,279,43,LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP
     PUSHBUTTON      "Delete",IDC_DELETE_COPY_DATA,6,248,42,12
-    DEFPUSHBUTTON   "OK",IDOK,163,261,50,14
-    PUSHBUTTON      "Cancel",IDCANCEL,236,261,50,14
+    DEFPUSHBUTTON   "OK",IDOK,163,268,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,236,268,50,14
     LTEXT           "Item Title",IDC_STATIC_TITLE,7,108,168,8
     LTEXT           "Date Added",IDC_STATIC_DATE,8,38,43,13,SS_CENTERIMAGE
     LTEXT           "Hot Key",IDC_STATIC_HOT_KEY,7,7,35,13,SS_CENTERIMAGE
@@ -735,6 +736,8 @@ BEGIN
     CONTROL         "Hotkey available globally",IDC_HOT_KEY_GLOBAL_MOVE_TO_GROUP,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,23,100,14
     CONTROL         "Win",IDC_CHECK_WIN_MOVE_TO_GROUP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,149,23,33,14
+    LTEXT           "Selected format MD5",IDC_STATIC_MD5,65,250,68,14,SS_CENTERIMAGE
+    EDITTEXT        IDC_EDIT_MD5,139,249,147,14,ES_AUTOHSCROLL | ES_READONLY
 END
 
 IDD_ABOUT DIALOGEX 0, 0, 337, 206
@@ -764,7 +767,7 @@ CAPTION "Friends"
 FONT 8, "MS Shell Dlg", 0, 0, 0x1
 BEGIN
     EDITTEXT        IDC_EDIT_PLACE_ON_CLIPBOARD,96,39,210,13,ES_AUTOHSCROLL
-    CONTROL         "Disable Recieving Clips",IDC_CHECK_DISABLE_FRIENDS,
+    CONTROL         "Disable Receiving Clips",IDC_CHECK_DISABLE_FRIENDS,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,95,53,146,10
     EDITTEXT        IDC_EDIT_PASSWORD,96,66,210,13,ES_AUTOHSCROLL
     EDITTEXT        IDC_EDIT_ADDITIONAL,95,108,210,12,ES_AUTOHSCROLL
@@ -772,8 +775,8 @@ BEGIN
     CONTROL         "Log Send Receive Commands",IDC_CHECK_LOG_SEND_RECIEVE,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,260,353,12
     LTEXT           "If Send All Copies is selected then all copies from this computer will be sent to the other computer.  If that is not selected then it will just be in the right click menu to send a copy to that computer.",IDC_STATIC_6,7,159,353,24
-    LTEXT           "IP/Computer Names seperated by commas",IDC_STATIC_2,7,37,84,17
-    LTEXT           "When you recieve a sent copy if their ip or computer name is in this list then it will be put on the clipboard.  Otherwise it will be in the saved clips to use at a later time.",IDC_STATIC_1,7,10,353,26
+    LTEXT           "IP/Computer Names separated by commas",IDC_STATIC_2,7,37,84,17
+    LTEXT           "When you receive a sent copy if their ip or computer name is in this list then it will be put on the clipboard.  Otherwise it will be in the saved clips to use at a later time.",IDC_STATIC_1,7,10,353,26
     LTEXT           "Network Password",IDC_STATIC_3,7,69,79,9
     LTEXT           "If you are receiving clips from computers where your network password does not match, enter additional network passwords separated by commas",IDC_STATIC_4,7,83,353,22
     LTEXT           "Receive Passwords",IDC_STATIC_5,7,110,79,9
@@ -954,15 +957,46 @@ BEGIN
     LTEXT           "Description",IDC_STATIC_FRIEND_DESC,7,24,53,14,SS_CENTERIMAGE
 END
 
-IDD_ADV_OPTIONS DIALOGEX 0, 0, 262, 258
+IDD_ADV_OPTIONS DIALOGEX 0, 0, 262, 268
 STYLE DS_SETFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
 CAPTION "Dialog"
 FONT 10, "Segoe UI", 400, 0, 0x0
 BEGIN
-    DEFPUSHBUTTON   "OK",IDOK,147,240,50,11
-    PUSHBUTTON      "Cancel",IDCANCEL,205,239,50,12
-    CONTROL         "",IDC_MFCPROPERTYGRID1,"MfcPropertyGrid",0x100,7,7,248,228
-    PUSHBUTTON      "Compact and Repair Database",IDC_BT_COMPACT_AND_REPAIR,12,240,108,11
+    DEFPUSHBUTTON   "OK",IDOK,147,250,50,11
+    PUSHBUTTON      "Cancel",IDCANCEL,205,249,50,12
+    CONTROL         "",IDC_MFCPROPERTYGRID1,"MfcPropertyGrid",0x100,7,7,248,212
+    PUSHBUTTON      "Compact and Repair Database",IDC_BT_COMPACT_AND_REPAIR,7,251,108,11
+    PUSHBUTTON      "On Copy Scripts",IDC_BUTTON_COPY_SCRIPTS,7,224,108,11
+    PUSHBUTTON      "On Paste Scripts",IDC_BUTTON_PASTE_SCRIPTS,7,237,108,11
+END
+
+IDD_SCRIPT_EDITOR DIALOGEX 0, 0, 435, 297
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+CAPTION "Scripts"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    EDITTEXT        IDC_EDIT_NAME,142,7,284,14,ES_AUTOHSCROLL
+    EDITTEXT        IDC_EDIT_DESC,142,24,284,14,ES_AUTOHSCROLL
+    CONTROL         "",IDC_CHECK_ACTIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,42,16,10
+    EDITTEXT        IDC_EDIT_SCRIPT,100,67,326,106,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN
+    EDITTEXT        IDC_EDIT_INPUT,100,200,265,27,ES_AUTOHSCROLL | ES_WANTRETURN
+    EDITTEXT        IDC_EDIT_OUTPUT,100,243,326,27,ES_AUTOHSCROLL
+    PUSHBUTTON      "Run",IDC_BUTTON_RUN,376,205,50,14
+    DEFPUSHBUTTON   "OK",IDOK,321,276,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,376,276,50,14
+    LISTBOX         IDC_LIST_SCRIPTS,7,7,83,268,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+    PUSHBUTTON      "Add",IDC_BUTTON_ADD_SCRIPT,7,276,32,14
+    PUSHBUTTON      "Delete",IDC_BUTTON_DELETE_SCRIPT,58,276,32,14
+    LTEXT           "Name",IDC_STATIC_NAME,100,7,19,14,SS_CENTERIMAGE
+    LTEXT           "Desciption",IDC_STATIC_DESC,100,24,34,14,SS_CENTERIMAGE
+    LTEXT           "Sample Input",IDC_STATIC_INPUT,100,189,43,8
+    LTEXT           "Sample Output",IDC_STATIC_OUTPUT,100,232,48,8
+    LTEXT           "All function must return true/false.  Either to cancel saving the copy or cancel the paste.",IDC_STATIC_RETURN_DESC,100,177,326,8
+    LTEXT           "Script",IDC_STATIC_SCRIPT,100,56,30,10
+    LTEXT           "Active",IDC_STATIC_ACTIVE,100,42,21,14
+    CONTROL         "ChaiScript (http://chaiscript.com/)",IDC_MFCLINK_CHAISCRIPT,
+                    "MfcLink",WS_TABSTOP,142,54,119,10
+    CONTROL         "Examples",IDC_MFCLINK2_EXAMPLES,"MfcLink",WS_TABSTOP,294,54,60,10
 END
 
 
@@ -1085,7 +1119,7 @@ BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 286
         TOPMARGIN, 7
-        BOTTOMMARGIN, 275
+        BOTTOMMARGIN, 282
     END
 
     IDD_ABOUT, DIALOG
@@ -1181,7 +1215,15 @@ BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 255
         TOPMARGIN, 7
-        BOTTOMMARGIN, 251
+        BOTTOMMARGIN, 261
+    END
+
+    IDD_SCRIPT_EDITOR, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 426
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 290
     END
 END
 #endif    // APSTUDIO_INVOKED
@@ -1480,6 +1522,16 @@ BEGIN
     0
 END
 
+IDD_SCRIPT_EDITOR AFX_DIALOG_LAYOUT
+BEGIN
+    0
+END
+
+IDD_COPY_PROPERTIES AFX_DIALOG_LAYOUT
+BEGIN
+    0
+END
+
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -1523,6 +1575,35 @@ BEGIN
     0
 END
 
+IDD_SCRIPT_EDITOR DLGINIT
+BEGIN
+    IDC_MFCLINK_CHAISCRIPT, 0x37c, 179, 0
+0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x683e, 0x7474, 0x3a70, 
+0x2f2f, 0x6863, 0x6961, 0x6373, 0x6972, 0x7470, 0x632e, 0x6d6f, 0x3c2f, 
+0x4d2f, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x3c3e, 0x464d, 0x4c43, 
+0x6e69, 0x5f6b, 0x7255, 0x506c, 0x6572, 0x6966, 0x3e78, 0x2f3c, 0x464d, 
+0x4c43, 0x6e69, 0x5f6b, 0x7255, 0x506c, 0x6572, 0x6966, 0x3e78, 0x4d3c, 
+0x4346, 0x694c, 0x6b6e, 0x545f, 0x6f6f, 0x746c, 0x7069, 0x3c3e, 0x4d2f, 
+0x4346, 0x694c, 0x6b6e, 0x545f, 0x6f6f, 0x746c, 0x7069, 0x3c3e, 0x464d, 
+0x4c43, 0x6e69, 0x5f6b, 0x7546, 0x6c6c, 0x6554, 0x7478, 0x6f54, 0x6c6f, 
+0x6974, 0x3e70, 0x4146, 0x534c, 0x3c45, 0x4d2f, 0x4346, 0x694c, 0x6b6e, 
+0x465f, 0x6c75, 0x546c, 0x7865, 0x5474, 0x6f6f, 0x746c, 0x7069, "\076" 
+    IDC_MFCLINK2_EXAMPLES, 0x37c, 207, 0
+0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x683e, 0x7474, 0x7370, 
+0x2f3a, 0x732f, 0x756f, 0x6372, 0x6665, 0x726f, 0x6567, 0x6e2e, 0x7465, 
+0x702f, 0x642f, 0x7469, 0x6f74, 0x632d, 0x2f70, 0x6977, 0x696b, 0x532f, 
+0x7263, 0x7069, 0x6974, 0x676e, 0x3c2f, 0x4d2f, 0x4346, 0x694c, 0x6b6e, 
+0x555f, 0x6c72, 0x3c3e, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x7255, 0x506c, 
+0x6572, 0x6966, 0x3e78, 0x2f3c, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x7255, 
+0x506c, 0x6572, 0x6966, 0x3e78, 0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x545f, 
+0x6f6f, 0x746c, 0x7069, 0x3c3e, 0x4d2f, 0x4346, 0x694c, 0x6b6e, 0x545f, 
+0x6f6f, 0x746c, 0x7069, 0x3c3e, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x7546, 
+0x6c6c, 0x6554, 0x7478, 0x6f54, 0x6c6f, 0x6974, 0x3e70, 0x4146, 0x534c, 
+0x3c45, 0x4d2f, 0x4346, 0x694c, 0x6b6e, 0x465f, 0x6c75, 0x546c, 0x7865, 
+0x5474, 0x6f6f, 0x746c, 0x7069, "\076" 
+    0
+END
+
 
 /////////////////////////////////////////////////////////////////////////////
 //

+ 12 - 0
CP_Main.vcxproj

@@ -132,6 +132,7 @@
       <WarningLevel>Level3</WarningLevel>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <CompileAs>Default</CompileAs>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;Winmm.lib;Version.lib;EncryptDecrypt.lib;zlib\zlibstat.lib;focus.lib;Shlwapi.lib;sqlite\lib32\icuuc.lib;sqlite\lib32\icutu.lib;sqlite\lib32\icuio.lib;sqlite\lib32\icuin.lib;sqlite\lib32\icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -189,6 +190,7 @@
       <WarningLevel>Level3</WarningLevel>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <CompileAs>Default</CompileAs>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;Winmm.lib;Version.lib;EncryptDecrypt64.lib;zlib\zlibstat64.lib;focus64.lib;Shlwapi.lib;sqlite\lib64\icuuc.lib;sqlite\lib64\icutu.lib;sqlite\lib64\icuio.lib;sqlite\lib64\icuin.lib;sqlite\lib64\icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -242,6 +244,7 @@
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
       <CompileAs>Default</CompileAs>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;Winmm.lib;Pdh.lib;Version.lib;EncryptDecryptD.lib;zlib\zlibstat.lib;focus.lib;Shlwapi.lib;%(AdditionalDependencies);sqlite\lib32\icuuc.lib;sqlite\lib32\icutu.lib;sqlite\lib32\icuio.lib;sqlite\lib32\icuin.lib;sqlite\lib32\icudt.lib</AdditionalDependencies>
@@ -296,6 +299,7 @@
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <CompileAs>Default</CompileAs>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;Winmm.lib;Pdh.lib;Version.lib;EncryptDecryptD64.lib;zlib\zlibstat64.lib;focus64.lib;Shlwapi.lib;sqlite\lib64\icuuc.lib;sqlite\lib64\icutu.lib;sqlite\lib64\icuio.lib;sqlite\lib64\icuin.lib;sqlite\lib64\icudt.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -434,6 +438,8 @@
     <ClCompile Include="CF_HDropAggregator.cpp" />
     <ClCompile Include="CF_TextAggregator.cpp" />
     <ClCompile Include="CF_UnicodeTextAggregator.cpp" />
+    <ClCompile Include="ChaiScriptOnCopy.cpp" />
+    <ClCompile Include="ChaiScriptXml.cpp" />
     <ClCompile Include="Clip.cpp">
       <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -580,6 +586,7 @@
     <ClCompile Include="CustomFriendsHelper.cpp" />
     <ClCompile Include="DeleteClipData.cpp" />
     <ClCompile Include="DimWnd.cpp" />
+    <ClCompile Include="DittoChaiScript.cpp" />
     <ClCompile Include="DittoPopupWindow.cpp" />
     <ClCompile Include="DPI.cpp" />
     <ClCompile Include="DrawHTML.C">
@@ -661,6 +668,7 @@
     </ClCompile>
     <ClCompile Include="QuickPasteKeyboard.cpp" />
     <ClCompile Include="RegExFilterHelper.cpp" />
+    <ClCompile Include="ScriptEditor.cpp" />
     <ClCompile Include="ScrollHelper.cpp" />
     <ClCompile Include="Shared\TextConvert.cpp" />
     <ClCompile Include="Shared\Tokenizer.cpp" />
@@ -1881,12 +1889,15 @@
     <ClInclude Include="ArrayEx.h" />
     <ClInclude Include="AutoSendToClientThread.h" />
     <ClInclude Include="CGdiPlusBitmap.h" />
+    <ClInclude Include="ChaiScriptOnCopy.h" />
+    <ClInclude Include="ChaiScriptXml.h" />
     <ClInclude Include="ClipCompare.h" />
     <ClInclude Include="ClipFormatQListCtrl.h" />
     <ClInclude Include="CreateQRCodeImage.h" />
     <ClInclude Include="CustomFriendsHelper.h" />
     <ClInclude Include="DeleteClipData.h" />
     <ClInclude Include="DimWnd.h" />
+    <ClInclude Include="DittoChaiScript.h" />
     <ClInclude Include="DittoPopupWindow.h" />
     <ClInclude Include="DPI.h" />
     <ClInclude Include="DrawHTML.h" />
@@ -1926,6 +1937,7 @@
     <ClInclude Include="QuickPasteKeyboard.h" />
     <ClInclude Include="RegExFilterHelper.h" />
     <ClInclude Include="RichEditCtrlEx.h" />
+    <ClInclude Include="ScriptEditor.h" />
     <ClInclude Include="ScrollHelper.h" />
     <ClInclude Include="SearchEditBox.h" />
     <ClInclude Include="Shared\TextConvert.h" />

+ 20 - 0
CP_Main.vcxproj.filters

@@ -434,6 +434,16 @@
     <ClCompile Include="AdvGeneral.cpp" />
     <ClCompile Include="RegExFilterHelper.cpp" />
     <ClCompile Include="CustomFriendsHelper.cpp" />
+    <ClCompile Include="DittoChaiScript.cpp">
+      <Filter>ChaiScript</Filter>
+    </ClCompile>
+    <ClCompile Include="ChaiScriptOnCopy.cpp">
+      <Filter>ChaiScript</Filter>
+    </ClCompile>
+    <ClCompile Include="ChaiScriptXml.cpp">
+      <Filter>ChaiScript</Filter>
+    </ClCompile>
+    <ClCompile Include="ScriptEditor.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="sqlite\CppSQLite3.h">
@@ -908,6 +918,16 @@
     <ClInclude Include="AdvGeneral.h" />
     <ClInclude Include="RegExFilterHelper.h" />
     <ClInclude Include="CustomFriendsHelper.h" />
+    <ClInclude Include="DittoChaiScript.h">
+      <Filter>ChaiScript</Filter>
+    </ClInclude>
+    <ClInclude Include="ChaiScriptOnCopy.h">
+      <Filter>ChaiScript</Filter>
+    </ClInclude>
+    <ClInclude Include="ChaiScriptXml.h">
+      <Filter>ChaiScript</Filter>
+    </ClInclude>
+    <ClInclude Include="ScriptEditor.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="CP_Main.rc">

+ 75 - 0
ChaiScriptOnCopy.cpp

@@ -0,0 +1,75 @@
+#include "stdafx.h"
+#include "ChaiScriptOnCopy.h"
+#include "DittoChaiScript.h"
+#include "Shared\TextConvert.h"
+
+#include "chaiscript/chaiscript.hpp"
+
+using namespace chaiscript;
+
+
+ChaiScriptOnCopy::ChaiScriptOnCopy()
+{
+}
+
+
+ChaiScriptOnCopy::~ChaiScriptOnCopy()
+{
+}
+
+
+bool ChaiScriptOnCopy::ProcessScript(IClip *pClip, std::string script, std::string activeApp)
+{
+	m_lastError = _T("");
+	bool continueCopy = true;
+	
+	try
+	{
+		ChaiScript chai;
+
+		chai.add(chaiscript::fun(&CDittoChaiScript::GetClipMD5), "GetClipMD5");
+
+		CDittoChaiScript clipData(pClip, activeApp);
+		chai.add(chaiscript::var(&clipData), "clip");
+
+		//loop over all scripts
+		/*std::string script = R""(	
+									var md5 = clip.GetClipMD5(13)
+									return md5 == "4FF8DB22A28559FB23AB9EB90523AE3E"
+								)"";*/
+
+
+		Boxed_Value bv = chai.eval(script);
+		if (chaiscript::boxed_cast<bool> (bv) == true)
+		{
+			m_lastError = _T("Script return true, to cancel copy");
+			continueCopy = false;
+		}
+	}
+	catch (const chaiscript::exception::eval_error &ee)
+	{
+		std::string eString;
+		eString += ee.pretty_print();
+
+		m_lastError = CTextConvert::MultiByteToUnicodeString(eString.c_str());
+	}
+	catch (std::exception &e)
+	{
+		std::string eString;
+		eString += e.what();
+
+		m_lastError = CTextConvert::MultiByteToUnicodeString(eString.c_str());
+	}
+	catch (CException *ex)
+	{
+		TCHAR szCause[255];
+		ex->GetErrorMessage(szCause, 255);
+		m_lastError.Format(_T("ProcessScript exception: %s"), szCause);
+	}
+	catch (...)
+	{
+		m_lastError.Format(_T("ProcessScript exception"));
+	}
+
+	return continueCopy;
+}

+ 16 - 0
ChaiScriptOnCopy.h

@@ -0,0 +1,16 @@
+#pragma once
+
+#include "Shared\IClip.h"
+#include <string>
+
+class ChaiScriptOnCopy
+{
+public:
+	ChaiScriptOnCopy();
+	~ChaiScriptOnCopy();
+
+	bool ProcessScript(IClip *pClip, std::string script, std::string activeApp);
+
+	CString m_lastError;
+};
+

+ 98 - 0
ChaiScriptXml.cpp

@@ -0,0 +1,98 @@
+#include "stdafx.h"
+#include "ChaiScriptXml.h"
+
+#include "tinyxml\tinyxml.h"
+
+#include "Shared\TextConvert.h"
+
+
+CChaiScriptXml::CChaiScriptXml()
+{
+}
+
+
+CChaiScriptXml::~CChaiScriptXml()
+{
+}
+
+CString CChaiScriptXml::GetScript(CString name, BOOL &active)
+{
+	CString script;
+	for (auto & listItem : m_list)
+	{
+		if (listItem.m_name == name)
+		{
+			active = listItem.m_active;
+			script = listItem.m_name;
+			break;
+		}
+	}
+
+	return script;
+}
+
+void CChaiScriptXml::Load(CString values)
+{
+	m_list.clear();
+	
+	TiXmlDocument doc;
+	CStringA xmlA;
+	CTextConvert::ConvertToUTF8(values, xmlA);
+	doc.Parse(xmlA);
+
+	TiXmlElement *ItemHeader = doc.FirstChildElement("ChaiScripts");
+
+	if (ItemHeader != NULL)
+	{
+		TiXmlElement *ItemElement = ItemHeader->FirstChildElement();
+
+		while (ItemElement)
+		{
+			CDittoChaiScriptXmlItem array_item;
+			ItemElement->Attribute("active", &array_item.m_active);
+			array_item.m_name = ItemElement->Attribute("name");
+			array_item.m_description = ItemElement->Attribute("description");
+			array_item.m_script = ItemElement->Attribute("script");
+
+			m_list.push_back(array_item);
+
+			ItemElement = ItemElement->NextSiblingElement();
+		}
+	}
+}
+
+CString CChaiScriptXml::Save()
+{
+	TiXmlDocument doc;
+
+	TiXmlElement* friendOuter = new TiXmlElement("ChaiScripts");
+	doc.LinkEndChild(friendOuter);
+
+	for (auto & listItem : m_list)
+	{
+		TiXmlElement* friendElement = new TiXmlElement("ChaiScriptItem");
+
+		friendElement->SetAttribute("active", listItem.m_active);
+
+		CStringA name;
+		CTextConvert::ConvertToUTF8(listItem.m_name, name);
+		friendElement->SetAttribute("name", name);
+
+		CStringA desc;
+		CTextConvert::ConvertToUTF8(listItem.m_description, desc);
+		friendElement->SetAttribute("description", desc);
+
+		CStringA script;
+		CTextConvert::ConvertToUTF8(listItem.m_script, script);
+		friendElement->SetAttribute("script", script);
+
+		friendOuter->LinkEndChild(friendElement);
+	}
+
+	TiXmlPrinter printer;
+	printer.SetLineBreak("");
+	doc.Accept(&printer);
+	CString cs = printer.CStr();
+
+	return cs;
+}

+ 30 - 0
ChaiScriptXml.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <vector>
+
+class CDittoChaiScriptXmlItem
+{
+public:
+	CDittoChaiScriptXmlItem()
+	{
+		m_active = FALSE;
+	}
+	BOOL m_active;
+	CString m_name;
+	CString m_description;
+	CString m_script;
+};
+
+class CChaiScriptXml
+{
+public:
+	CChaiScriptXml();
+	~CChaiScriptXml();
+
+	CString Save();
+	void Load(CString values);
+
+	CString GetScript(CString name, BOOL &active);
+
+	std::vector<CDittoChaiScriptXmlItem> m_list;
+};

+ 38 - 0
Clip.cpp

@@ -12,6 +12,7 @@
 #include "zlib/zlib.h"
 #include "Misc.h"
 #include "Md5.h"
+#include "ChaiScriptOnCopy.h"
 
 #include <Mmsystem.h>
 
@@ -520,6 +521,43 @@ int CClip::LoadFromClipboard(CClipTypes* pClipTypes, bool checkClipboardIgnore,
 		return FALSE;
 	}
 
+	try
+	{
+		for (auto & listItem : g_Opt.m_copyScripts.m_list)
+		{
+			if (listItem.m_active)
+			{
+				Log(StrF(_T("Start of process copy name: %s, script: %s"), listItem.m_name, listItem.m_script));
+
+				ChaiScriptOnCopy onCopy;
+				if (onCopy.ProcessScript(this, (LPCSTR)CTextConvert::ConvertToChar(listItem.m_script), (LPCSTR)CTextConvert::ConvertToChar(activeApp)) == false)
+				{
+					Log(StrF(_T("End of process copy name: %s, returned false, not saving this copy to Ditto, last Error: %s"), listItem.m_name, onCopy.m_lastError));
+
+					return -1;
+				}
+
+				Log(StrF(_T("End of process copy name: %s, returned true, last Error: %s"), listItem.m_name, onCopy.m_lastError));
+			}
+			else
+			{
+				Log(StrF(_T("Script is not active, not processing name: %s, script: %s"), listItem.m_name, listItem.m_script));
+			}
+		}
+	}
+	catch (CException *ex)
+	{
+		TCHAR szCause[255];
+		ex->GetErrorMessage(szCause, 255);
+		CString cs;
+		cs.Format(_T("save copy exception: %s"), szCause);
+		Log(cs);
+	}
+	catch (...)
+	{
+		Log(_T("save copy exception 2"));
+	}
+
 	return TRUE;
 }
 

+ 72 - 4
CopyProperties.cpp

@@ -5,6 +5,8 @@
 #include "cp_main.h"
 #include "CopyProperties.h"
 #include ".\copyproperties.h"
+#include "Md5.h"
+#include "Shared\TextConvert.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -62,6 +64,7 @@ BEGIN_MESSAGE_MAP(CCopyProperties, CDialog)
 	ON_WM_SIZE()
 	//}}AFX_MSG_MAP
 	ON_WM_CTLCOLOR()
+	ON_LBN_SELCHANGE(IDC_COPY_DATA, &CCopyProperties::OnLbnSelchangeCopyData)
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
@@ -86,11 +89,10 @@ BOOL CCopyProperties::OnInitDialog()
 		}
 		else
 		{
-			CClip Clip;
-			if(Clip.LoadMainTable(m_lCopyID))
+			if(m_clip.LoadMainTable(m_lCopyID))
 			{
-				Clip.LoadFormats(m_lCopyID);
-				LoadDataFromCClip(Clip);			
+				m_clip.LoadFormats(m_lCopyID);
+				LoadDataFromCClip(m_clip);
 			}
 		}
 	}
@@ -501,3 +503,69 @@ HBRUSH CCopyProperties::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
 	// TODO:  Return a different brush if the default is not desired
 	return hbr;
 }
+
+
+void CCopyProperties::OnLbnSelchangeCopyData()
+{
+	int selCount = m_lCopyData.GetSelCount();
+	if (selCount > 0)
+	{
+		m_bDeletedData = true;
+
+		//Get the selected indexes
+		ARRAY items;
+		items.SetSize(selCount);
+		m_lCopyData.GetSelItems(selCount, items.GetData());
+
+		items.SortDescending();
+
+		for (int i = 0; i < selCount; i++)
+		{
+			int row = items[i];
+			int itemData = (int)m_lCopyData.GetItemData(row);
+
+			CClip *pClip = NULL;
+			if (m_lCopyID == -1 && m_pMemoryClip != NULL)
+			{
+				pClip = m_pMemoryClip;
+			}
+			else
+			{
+				pClip = &m_clip;
+			}
+
+			if (pClip != NULL)
+			{
+				CClipFormat* pCF;
+				INT_PTR dataCount = pClip->m_Formats.GetSize();
+				for (int i = 0; i < dataCount; i++)
+				{
+					pCF = &pClip->m_Formats.GetData()[i];
+					if (pCF)
+					{
+						if (pCF->m_dataId == itemData)
+						{
+							CMd5 md5;
+							md5.MD5Init();
+
+							SIZE_T size = ::GlobalSize(pCF->Data());
+							void* pv = GlobalLock(pCF->Data());
+							if (pv != NULL)
+							{
+								md5.MD5Update((unsigned char*)pv, (unsigned int)size);
+
+								GlobalUnlock(pCF->Data());
+
+								CStringA md5String = md5.MD5FinalToString();
+
+								this->SetDlgItemText(IDC_EDIT_MD5, CTextConvert::MultiByteToUnicodeString(md5String));
+							}
+						}
+					}
+				}
+			}
+
+			break;
+		}
+	}
+}

+ 2 - 0
CopyProperties.h

@@ -65,6 +65,7 @@ protected:
 	bool m_bSetToTopMost;
 	CClip *m_pMemoryClip;
 	CBrush m_brush;
+	CClip m_clip;
 
 	void LoadDataIntoCClip(CClip &Clip);
 	void LoadDataFromCClip(CClip &Clip);
@@ -83,6 +84,7 @@ protected:
 	DECLARE_MESSAGE_MAP()
 public:
 	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+	afx_msg void OnLbnSelchangeCopyData();
 };
 
 //{{AFX_INSERT_LOCATION}}

+ 4 - 4
Debug/Language/English.xml

@@ -173,7 +173,7 @@
 		<Item English_Text = "Multi-Paste clip separator ([LF] = line feed)" ID = "2090"></Item>
 		
 		//New item added 3/1/11
-		<Item English_Text = "Accepted Copy Applications (seperate by ;)" ID = "2098"></Item>
+		<Item English_Text = "Accepted Copy Applications (separate by ;)" ID = "2098"></Item>
 		<Item English_Text = "Include" ID = "2095"></Item>
 		<Item English_Text = "Exclude" ID = "2096"></Item>
 		
@@ -278,9 +278,9 @@
 		
 	</Ditto_Options_Quick_Paste>	
 	<Ditto_Options_Friends>
-		<Item English_Text = "When you recieve a sent copy if their ip or computer name is in this list then it will be put on the clipboard.  Otherwise it will be in the saved clips to use at a later time." ID = "2052"></Item>
+		<Item English_Text = "When you receive a sent copy if their ip or computer name is in this list then it will be put on the clipboard.  Otherwise it will be in the saved clips to use at a later time." ID = "2052"></Item>
 		<Item English_Text = "IP/Computer Names seperated by commas" ID = "2053"></Item>
-		<Item English_Text = "Disable Recieving Clips" ID = "2026"></Item>
+		<Item English_Text = "Disable Receiving Clips" ID = "2026"></Item>
 		<Item English_Text = "Group Password" ID = "2054"></Item>
 		<Item English_Text = "If you are receiving clips from computers where your group password does not match, enter additional group passwords separated by commas" ID = "2055"></Item>
 		<Item English_Text = "Receive Passwords" ID = "2056"></Item>
@@ -407,7 +407,7 @@
 		<Item English_Text = "Hot Key" ID = "QPHotKey"></Item>
 		<Item English_Text = "Command" ID = "QPCommand"></Item>
 		
-		<Item English_Text = "[Top Most Window]" ID = "top_most"></Item>
+		<Item English_Text = "[Always on top]" ID = "top_most"></Item>
 		
 		<Item English_Text = "[Disconnected]" ID = "disconnected"></Item>
 		

+ 94 - 0
DittoChaiScript.cpp

@@ -0,0 +1,94 @@
+#include "stdafx.h"
+#include "DittoChaiScript.h"
+
+#include "Md5.h"
+#include "Misc.h"
+
+CDittoChaiScript::CDittoChaiScript(IClip *pClip, std::string activeApp)
+{
+	m_pClip = pClip;
+	m_activeApp = activeApp;
+}
+
+
+CDittoChaiScript::~CDittoChaiScript()
+{
+}
+
+
+std::string CDittoChaiScript::GetAsciiString()
+{
+	std::string s = "";
+	if (m_pClip)
+	{
+		IClipFormat *pFormat = m_pClip->Clips()->FindFormatEx(CF_TEXT);
+		if (pFormat)
+		{
+			char *stringData = (char *)GlobalLock(pFormat->Data());
+			if (stringData != NULL)
+			{
+				s = stringData;
+
+				GlobalUnlock(pFormat->Data());
+			}
+		}
+	}
+
+	return s;
+}
+
+void CDittoChaiScript::SetAsciiString(std::string stringVal)
+{
+	if (m_pClip)
+	{
+		m_pClip->Clips()->DeleteAll();
+
+		//HGLOBAL hGlobal = ::NewGlobalP(stringVal.c_str, 4);
+	//	ASSERT(hGlobal);
+
+		//m_pClip->Clips()->AddNew(CF_TEXT, hGlobal);
+	}
+}
+
+std::string CDittoChaiScript::GetClipMD5(int clipboardFormat)
+{
+	CMd5 md5;
+	md5.MD5Init();
+
+	std::string md5String;
+
+	if (m_pClip)
+	{
+		IClipFormat *pFormat = m_pClip->Clips()->FindFormatEx(clipboardFormat);
+		if (pFormat)
+		{
+			SIZE_T size = ::GlobalSize(pFormat->Data());
+			void* pv = GlobalLock(pFormat->Data());
+			if (pv != NULL)
+			{				
+				md5.MD5Update((unsigned char*)pv, (unsigned int)size);
+
+				GlobalUnlock(pFormat->Data());
+
+				md5String = md5.MD5FinalToString();
+			}
+		}
+	}
+
+	return md5String;
+}
+
+SIZE_T CDittoChaiScript::GetClipSize(int clipboardFormat)
+{
+	SIZE_T size = 0;
+	if (m_pClip)
+	{
+		IClipFormat *pFormat = m_pClip->Clips()->FindFormatEx(clipboardFormat);
+		if (pFormat)
+		{
+			size = ::GlobalSize(pFormat->Data());
+		}
+	}
+
+	return size;
+}

+ 24 - 0
DittoChaiScript.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include "shared/IClip.h"
+#include <string>
+
+
+class CDittoChaiScript
+{
+public:
+	CDittoChaiScript(IClip *pClip, std::string activeApp);
+	~CDittoChaiScript();
+
+	IClip *m_pClip;
+	std::string m_activeApp;
+
+	std::string GetAsciiString();
+	void SetAsciiString(std::string stringVal);
+
+	std::string GetClipMD5(int clipboardFormat);
+	SIZE_T GetClipSize(int clipboardFormat);
+
+	std::string GetActiveApp() { return m_activeApp; }
+};
+

+ 28 - 1
Options.cpp

@@ -68,6 +68,8 @@ CGetSetOptions g_Opt;
 BOOL CGetSetOptions::m_bShowAlwaysOnTopWarning = TRUE;
 CRegExFilterHelper CGetSetOptions::m_regexHelper;
 BOOL CGetSetOptions::m_excludeCF_DIBInExcel = TRUE;
+CChaiScriptXml CGetSetOptions::m_copyScripts;
+CChaiScriptXml CGetSetOptions::m_pasteScripts;
 
 CGetSetOptions::CGetSetOptions()
 {
@@ -198,6 +200,9 @@ void CGetSetOptions::LoadSettings()
 	GetClientSendCount();
 
 	m_Theme.Load(GetTheme());
+
+	m_copyScripts.Load(GetCopyScriptsXml());
+	m_pasteScripts.Load(GetPasteScriptsXml());
 }
 
 void CGetSetOptions::CreateIniFile(CString path)
@@ -474,7 +479,7 @@ CString CGetSetOptions::GetProfileString(CString csName, CString csDefault, CStr
 			csApp = csNewPath;
 		}
 
-		TCHAR cString[MAX_PATH];
+		TCHAR cString[100000];
 		GetPrivateProfileString(csApp, csName, csDefault, cString, sizeof(cString), m_csIniFileName);
 
 		return cString;
@@ -2509,4 +2514,26 @@ BOOL CGetSetOptions::GetShowStartupMessage()
 void CGetSetOptions::SetShowStartupMessage(int val)
 {
 	SetProfileLong(_T("ShowStartupMessage"), val);
+}
+
+CString CGetSetOptions::GetCopyScriptsXml()
+{
+	return GetProfileString("CopyScriptsXml", "");
+}
+
+void CGetSetOptions::SetCopyScriptsXml(CString val)
+{
+	m_copyScripts.Load(val);
+	SetProfileString(_T("CopyScriptsXml"), val);
+}
+
+CString CGetSetOptions::GetPasteScriptsXml()
+{
+	return GetProfileString("PasteScriptsXml", "");
+}
+
+void CGetSetOptions::SetPasteScriptsXml(CString val)
+{
+	m_pasteScripts.Load(val);
+	SetProfileString(_T("PasteScriptsXml"), val);
 }

+ 9 - 0
Options.h

@@ -2,6 +2,7 @@
 
 #include "Theme.h"
 #include "RegExFilterHelper.h"
+#include "ChaiScriptXml.h"
 
 #define MAX_SEND_CLIENTS	15
 class CSendClients
@@ -574,6 +575,14 @@ public:
 	static BOOL GetShowStartupMessage();
 	static void SetShowStartupMessage(int val);
 
+	static CChaiScriptXml m_copyScripts;
+	static CString GetCopyScriptsXml();
+	static void SetCopyScriptsXml(CString val);
+
+	static CChaiScriptXml m_pasteScripts;
+	static CString GetPasteScriptsXml();
+	static void SetPasteScriptsXml(CString val);
+
 };
 
 // global for easy access and for initialization of fast access variables

+ 9 - 3
QListCtrl.cpp

@@ -1570,10 +1570,16 @@ void CQListCtrl::SetSearchText(CString text)
 	m_searchText = text;
 }
 
-void CQListCtrl::HidePopup()
+void CQListCtrl::HidePopup(bool checkShowPersistant)
 { 
-	if(VALID_TOOLTIP) 
-		m_pToolTip->Hide();	
+	if (VALID_TOOLTIP)
+	{
+		if (checkShowPersistant == false ||
+			m_pToolTip->GetShowPersistant() == false)
+		{
+			m_pToolTip->Hide();
+		}
+	}
 }
 
 BOOL CQListCtrl::IsToolTipWindowVisible() 

+ 1 - 1
QListCtrl.h

@@ -135,7 +135,7 @@ public:
 	bool ShowFullDescription(bool bFromAuto = false, bool fromNextPrev = false);
 	BOOL SetItemCountEx(int iCount, DWORD dwFlags = 0);
 
-	void HidePopup();
+	void HidePopup(bool checkShowPersistant);
 
 	void SetLogFont(LOGFONT &font);
 

+ 18 - 11
QPasteWnd.cpp

@@ -297,6 +297,7 @@ ON_COMMAND(ID_IMPORT_IMPORTCOPIEDFILE, &CQPasteWnd::OnImportImportcopiedfile)
 ON_UPDATE_COMMAND_UI(ID_IMPORT_IMPORTCOPIEDFILE, &CQPasteWnd::OnUpdateImportImportcopiedfile)
 ON_UPDATE_COMMAND_UI(32775, &CQPasteWnd::OnUpdate32775)
 ON_COMMAND_RANGE(CustomFriendStartId, (CustomFriendStartId+ MaxCustomFriends+1), OnCustomSendToFriend)
+ON_MESSAGE(WM_DPICHANGED, OnDpiChanged)
 END_MESSAGE_MAP()
 
 
@@ -653,7 +654,7 @@ void CQPasteWnd::OnActivate(UINT nState, CWnd *pWndOther, BOOL bMinimized)
             g_HotKeys.RegisterAll();
         }
 
-        m_lstHeader.HidePopup();
+        m_lstHeader.HidePopup(true);
     }
     else if(nState == WA_ACTIVE || nState == WA_CLICKACTIVE)
     {
@@ -698,7 +699,7 @@ BOOL CQPasteWnd::HideQPasteWindow (bool releaseFocus, bool clearSearchData)
 
     KillTimer(TIMER_FILL_CACHE);
 
-	m_lstHeader.HidePopup();
+	m_lstHeader.HidePopup(true);
 
 	m_popupMsg.Hide();
 
@@ -1138,7 +1139,7 @@ void CQPasteWnd::UpdateStatus(bool bRepaintImmediately)
 
 	if (g_Opt.m_bShowPersistent)
 	{
-		title = (StrF(_T("%s %s"), _T(QPASTE_TITLE), theApp.m_Language.GetString("top_window", "[Top Most Window]")));
+		title = (StrF(_T("%s %s"), _T(QPASTE_TITLE), theApp.m_Language.GetString("top_window", "[Always on top]")));
 	}
 
 	if (theApp.IsClipboardViewerConnected() == FALSE)
@@ -1191,7 +1192,7 @@ void CQPasteWnd::UpdateStatus(bool bRepaintImmediately)
 
 	if (g_Opt.m_bShowPersistent)
 	{
-		windowTitle += StrF(_T(" %s"), theApp.m_Language.GetString("top_window", "[Top Most Window]"));
+		windowTitle += StrF(_T(" %s"), theApp.m_Language.GetString("top_window", "[Always on top]"));
 	}
 
 	if (theApp.IsClipboardViewerConnected() == FALSE)
@@ -1205,7 +1206,7 @@ void CQPasteWnd::UpdateStatus(bool bRepaintImmediately)
 BOOL CQPasteWnd::FillList(CString csSQLSearch /*=""*/)
 {
     KillTimer(TIMER_DO_SEARCH);
-	m_lstHeader.HidePopup();
+	m_lstHeader.HidePopup(true);
 
     Log(StrF(_T("Start Fill List - %s"), csSQLSearch));
 
@@ -3250,7 +3251,7 @@ bool CQPasteWnd::DoActionCloseWindow()
 	{
 		if (m_lstHeader.IsToolTipWindowVisible())
 		{
-			m_lstHeader.HidePopup();
+			m_lstHeader.HidePopup(true);
 		}
 		else if (m_strSQLSearch.IsEmpty() == FALSE)
 		{
@@ -5121,7 +5122,7 @@ void CQPasteWnd::OnNcLButtonDblClk(UINT nHitTest, CPoint point)
 
 void CQPasteWnd::OnShowGroupsTop()
 {
-	m_lstHeader.HidePopup();
+	m_lstHeader.HidePopup(true);
 
     OnShowGroupsBottom();
     return ;
@@ -5145,7 +5146,7 @@ void CQPasteWnd::OnShowGroupsTop()
 
 void CQPasteWnd::OnShowGroupsBottom()
 {
-	m_lstHeader.HidePopup();
+	m_lstHeader.HidePopup(true);
 
     m_GroupTree.m_bHide = false;
     m_bHideWnd = false;
@@ -5311,7 +5312,7 @@ LRESULT CQPasteWnd::OnToolTipWndInactive(WPARAM wParam, LPARAM lParam)
 		CWnd *p = GetFocus();
 		if (p == NULL)
 		{
-			m_lstHeader.HidePopup();
+			m_lstHeader.HidePopup(true);
 		}
 	}
 
@@ -6054,7 +6055,7 @@ void CQPasteWnd::OnUpdateSpecialpasteSentence(CCmdUI *pCmdUI)
 
 void CQPasteWnd::OnSystemButton()
 {
-	m_lstHeader.HidePopup();
+	m_lstHeader.HidePopup(true);
 
 	POINT pp;
 	CMenu cmPopUp;
@@ -6332,7 +6333,13 @@ void CQPasteWnd::OnUpdateMenuNewclip32937(CCmdUI *pCmdUI)
 
 LRESULT CQPasteWnd::OnSearchFocused(WPARAM wParam, LPARAM lParam)
 {
-	m_lstHeader.HidePopup();
+	m_lstHeader.HidePopup(true);
+
+	return TRUE;
+}
+
+LRESULT CQPasteWnd::OnDpiChanged(WPARAM wParam, LPARAM lParam)
+{
 
 	return TRUE;
 }

+ 1 - 0
QPasteWnd.h

@@ -516,4 +516,5 @@ public:
 	afx_msg void OnImportImportcopiedfile();
 	afx_msg void OnUpdateImportImportcopiedfile(CCmdUI *pCmdUI);
 	afx_msg void OnUpdate32775(CCmdUI *pCmdUI);
+	afx_msg LRESULT OnDpiChanged(WPARAM wParam, LPARAM lParam);
 };

+ 26 - 3
Resource.h

@@ -188,6 +188,7 @@
 #define down_28                         331
 #define IDB_PNG20                       332
 #define down_32                         332
+#define IDD_SCRIPT_EDITOR               333
 #define IDC_PATH                        1000
 #define IDC_GET_PATH                    1001
 #define IDC_SELECT_SOUND                1002
@@ -254,9 +255,12 @@
 #define IDC_BUTTON_SEARCH               1040
 #define IDC_ASSIGN                      1040
 #define IDC_BUTTON_CLEAR                1040
+#define IDC_BUTTON_RUN                  1040
+#define IDC_BUTTON_COPY_SCRIPTS         1040
 #define IDC_PARSE_EDIT                  1041
 #define IDC_SEND_PASTE_MESSAGE          1041
 #define IDC_BUTTON_PROPERTIES           1041
+#define IDC_BUTTON_PASTE_SCRIPTS        1041
 #define IDC_CHECK1                      1042
 #define IDC_HISTORY_START_TOP           1042
 #define IDC_CHECK_SEND_PASTE            1042
@@ -265,6 +269,7 @@
 #define IDC_PLAY_SOUND_1                1042
 #define IDC_CHECK_CLIP_TITLE            1042
 #define IDC_CHECK_SHIFT_1               1042
+#define IDC_CHECK_ACTIVE                1042
 #define IDC_PLAY_SOUND_2                1043
 #define IDC_CHECK_MOVE_CLIPS_ON_PASTE   1043
 #define IDC_CHECK_CREATE_DATE           1043
@@ -483,7 +488,9 @@
 #define IDC_BUTTON_DIFF_BROWSE          2124
 #define IDC_BUTTON_REMOVE               2124
 #define IDC_BUTTON_ADVANCED             2124
+#define IDC_BUTTON_ADD_SCRIPT           2124
 #define IDC_STATIC_QUICK_PASTE          2125
+#define IDC_BUTTON_DELETE_SCRIPT        2125
 #define IDC_STATIC_DESC                 2126
 #define IDC_RADIO_USE_IP                2127
 #define IDC_RADIO_KEYBOARD              2127
@@ -528,6 +535,21 @@
 #define IDC_CHECK_SAVE                  2151
 #define IDC_STATIC_FRIEND_DESC          2152
 #define IDC_EDIT_NAME                   2153
+#define IDC_EDIT_SCRIPT                 2155
+#define IDC_STATIC_NAME                 2156
+#define IDC_EDIT_INPUT                  2157
+#define IDC_EDIT_OUTPUT                 2158
+#define IDC_STATIC_INPUT                2159
+#define IDC_STATIC_OUTPUT               2160
+#define IDC_LIST_SCRIPTS                2161
+#define IDC_STATIC_RETURN_DESC          2162
+#define IDC_STATIC_SCRIPT               2163
+#define IDC_STATIC_ACTIVE               2164
+#define IDC_MFCLINK1                    2165
+#define IDC_MFCLINK_CHAISCRIPT          2165
+#define IDC_MFCLINK2_EXAMPLES           2166
+#define IDC_STATIC_MD5                  2167
+#define IDC_EDIT_MD5                    2168
 #define ID_FIRST_OPTION                 32771
 #define ID_FIRST_EXIT                   32772
 #define ID_FIRST_SHOWQUICKPASTE         32773
@@ -690,15 +712,16 @@
 #define ID_SENDTO_PROMPTFORNAME         32939
 #define ID_MENU_SAVE                    32940
 #define ID_IMPORT_IMPORTCOPIEDFILE      32941
+#define ID_FIRST_ALWAYSONTOP            32942
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_3D_CONTROLS                     1
-#define _APS_NEXT_RESOURCE_VALUE        333
-#define _APS_NEXT_COMMAND_VALUE         32942
-#define _APS_NEXT_CONTROL_VALUE         2154
+#define _APS_NEXT_RESOURCE_VALUE        337
+#define _APS_NEXT_COMMAND_VALUE         32943
+#define _APS_NEXT_CONTROL_VALUE         2169
 #define _APS_NEXT_SYMED_VALUE           104
 #endif
 #endif

+ 241 - 0
ScriptEditor.cpp

@@ -0,0 +1,241 @@
+// ScriptEditor.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "CP_Main.h"
+#include "ScriptEditor.h"
+#include "afxdialogex.h"
+
+
+// CScriptEditor dialog
+
+IMPLEMENT_DYNAMIC(CScriptEditor, CDialogEx)
+
+CScriptEditor::CScriptEditor(CWnd* pParent /*=NULL*/)
+	: CDialogEx(IDD_SCRIPT_EDITOR, pParent)
+{
+
+}
+
+CScriptEditor::~CScriptEditor()
+{
+}
+
+void CScriptEditor::DoDataExchange(CDataExchange* pDX)
+{
+	CDialogEx::DoDataExchange(pDX);
+	DDX_Control(pDX, IDC_LIST_SCRIPTS, m_scriptsList);
+}
+
+
+BEGIN_MESSAGE_MAP(CScriptEditor, CDialogEx)
+	ON_LBN_SELCHANGE(IDC_LIST_SCRIPTS, &CScriptEditor::OnLbnSelchangeListScripts)
+	ON_BN_CLICKED(IDC_BUTTON_ADD_SCRIPT, &CScriptEditor::OnBnClickedButtonAddScript)
+	ON_EN_KILLFOCUS(IDC_EDIT_NAME, &CScriptEditor::OnEnKillfocusEditName)
+	ON_EN_KILLFOCUS(IDC_EDIT_DESC, &CScriptEditor::OnEnKillfocusEditDesc)
+	ON_EN_KILLFOCUS(IDC_EDIT_SCRIPT, &CScriptEditor::OnEnKillfocusEditScript)
+	ON_BN_CLICKED(IDC_CHECK_ACTIVE, &CScriptEditor::OnBnClickedCheckActive)
+	ON_WM_SIZE()
+	ON_BN_CLICKED(IDC_BUTTON_DELETE_SCRIPT, &CScriptEditor::OnBnClickedButtonDeleteScript)
+END_MESSAGE_MAP()
+
+
+// CScriptEditor message handlers
+
+BOOL CScriptEditor::OnInitDialog()
+{
+	CDialogEx::OnInitDialog();
+
+	int index = 0;
+	for (auto & listItem : m_xml.m_list)
+	{
+		int row = m_scriptsList.AddString(listItem.m_name);
+		m_scriptsList.SetItemData(row, index);
+		index++;
+	}
+
+	if (index > 0)
+	{
+		m_scriptsList.SetSel(0);
+		m_scriptsList.SetCurSel(0);
+		m_scriptsList.SetCaretIndex(0);
+		m_scriptsList.SetAnchorIndex(0);
+
+		OnLbnSelchangeListScripts();
+	}
+
+	m_resize.SetParent(m_hWnd);	
+	m_resize.AddControl(IDOK, DR_MoveTop | DR_MoveLeft);
+	m_resize.AddControl(IDCANCEL, DR_MoveTop | DR_MoveLeft);
+
+	m_resize.AddControl(IDC_LIST_SCRIPTS, DR_SizeHeight);
+	m_resize.AddControl(IDC_EDIT_DESC, DR_SizeWidth);
+	m_resize.AddControl(IDC_EDIT_NAME, DR_SizeWidth);
+	m_resize.AddControl(IDC_EDIT_SCRIPT, DR_SizeWidth | DR_SizeHeight);
+
+	m_resize.AddControl(IDC_STATIC_RETURN_DESC, DR_MoveTop | DR_SizeWidth);
+	m_resize.AddControl(IDC_STATIC_INPUT, DR_MoveTop);
+	m_resize.AddControl(IDD_SCRIPT_EDITOR, DR_MoveTop | DR_SizeWidth);
+	m_resize.AddControl(IDC_EDIT_OUTPUT, DR_MoveTop | DR_SizeWidth);
+	m_resize.AddControl(IDC_STATIC_OUTPUT, DR_MoveTop);	
+	m_resize.AddControl(IDC_EDIT_INPUT, DR_MoveTop | DR_SizeWidth);
+
+	m_resize.AddControl(IDC_BUTTON_RUN, DR_MoveTop | DR_MoveLeft);
+
+	m_resize.AddControl(IDC_BUTTON_DELETE_SCRIPT, DR_MoveTop);
+	m_resize.AddControl(IDC_BUTTON_ADD_SCRIPT, DR_MoveTop);
+	
+	
+
+	return FALSE;
+}
+
+void CScriptEditor::OnLbnSelchangeListScripts()
+{
+	int listIndex = m_scriptsList.GetItemData(m_scriptsList.GetCurSel());
+	if (listIndex >= 0 && listIndex < m_xml.m_list.size())
+	{
+		this->SetDlgItemText(IDC_EDIT_NAME, m_xml.m_list[listIndex].m_name);
+		this->SetDlgItemText(IDC_EDIT_DESC, m_xml.m_list[listIndex].m_description);
+		if (m_xml.m_list[listIndex].m_active)
+		{
+			this->CheckDlgButton(IDC_CHECK_ACTIVE, BST_CHECKED);
+		}
+		else
+		{
+			this->CheckDlgButton(IDC_CHECK_ACTIVE, BST_UNCHECKED);
+		}
+		this->SetDlgItemText(IDC_EDIT_SCRIPT, m_xml.m_list[listIndex].m_script);
+
+		this->GetDlgItem(IDC_EDIT_NAME)->SetFocus();
+	}
+}
+
+void CScriptEditor::OnBnClickedButtonAddScript()
+{
+	CDittoChaiScriptXmlItem newItem;
+	newItem.m_name = _T("New Script");
+	newItem.m_active = true;
+	m_xml.m_list.push_back(newItem);
+
+	int index = m_scriptsList.AddString(newItem.m_name);
+	{
+		int y = m_xml.m_list.size() - 1;
+		m_scriptsList.SetItemData(index, y);// );
+	}
+
+	int t = m_scriptsList.GetItemData(index);
+
+	m_scriptsList.SetSel(index);
+	m_scriptsList.SetCurSel(index);
+	m_scriptsList.SetCaretIndex(index);
+	m_scriptsList.SetAnchorIndex(index);
+
+	this->SetDlgItemText(IDC_EDIT_NAME, newItem.m_name);
+	this->SetDlgItemText(IDC_EDIT_DESC, _T(""));
+	this->SetDlgItemText(IDC_EDIT_SCRIPT, _T(""));
+	this->CheckDlgButton(IDC_CHECK_ACTIVE, BST_CHECKED);
+
+	this->GetDlgItem(IDC_EDIT_NAME)->SetFocus();
+}
+
+void CScriptEditor::OnEnKillfocusEditName()
+{
+	int selectedRow = m_scriptsList.GetCurSel();
+	int listIndex = m_scriptsList.GetItemData(selectedRow);
+	if (listIndex >= 0 && listIndex < m_xml.m_list.size())
+	{
+		CString name;
+		this->GetDlgItemText(IDC_EDIT_NAME, name);
+
+		if (m_xml.m_list[listIndex].m_name != name)
+		{
+			m_xml.m_list[listIndex].m_name = name;
+
+			m_scriptsList.SetRedraw(FALSE);
+
+			int itemData = m_scriptsList.GetItemData(selectedRow);
+			m_scriptsList.DeleteString(selectedRow);
+			m_scriptsList.InsertString(selectedRow, name);
+			m_scriptsList.SetItemData(selectedRow, itemData);
+			m_scriptsList.SetSel(selectedRow);
+			m_scriptsList.SetCurSel(selectedRow);
+			m_scriptsList.SetCaretIndex(selectedRow);
+			m_scriptsList.SetAnchorIndex(selectedRow);
+			m_scriptsList.SetRedraw(TRUE);
+			m_scriptsList.UpdateWindow();
+		}	
+	}
+}
+
+void CScriptEditor::OnEnKillfocusEditDesc()
+{
+	int listIndex = m_scriptsList.GetItemData(m_scriptsList.GetCurSel());
+	if (listIndex >= 0 && listIndex < m_xml.m_list.size())
+	{
+		CString desc;
+		this->GetDlgItemText(IDC_EDIT_DESC, desc);
+		m_xml.m_list[listIndex].m_description = desc;
+	}
+}
+
+void CScriptEditor::OnEnKillfocusEditScript()
+{
+	int listIndex = m_scriptsList.GetItemData(m_scriptsList.GetCurSel());
+	if (listIndex >= 0 && listIndex < m_xml.m_list.size())
+	{
+		CString script;
+		this->GetDlgItemText(IDC_EDIT_SCRIPT, script);
+		m_xml.m_list[listIndex].m_script = script;
+	}
+}
+
+void CScriptEditor::OnBnClickedCheckActive()
+{
+	int listIndex = m_scriptsList.GetItemData(m_scriptsList.GetCurSel());
+	if (listIndex >= 0 && listIndex < m_xml.m_list.size())
+	{
+		if (this->IsDlgButtonChecked(IDC_CHECK_ACTIVE) == BST_CHECKED)
+		{
+			m_xml.m_list[listIndex].m_active = true;
+		}
+		else
+		{ 
+			m_xml.m_list[listIndex].m_active = false;
+		}
+	}
+}
+
+void CScriptEditor::OnSize(UINT nType, int cx, int cy)
+{
+	CDialogEx::OnSize(nType, cx, cy);
+
+	m_resize.MoveControls(CSize(cx, cy));
+}
+
+
+void CScriptEditor::OnBnClickedButtonDeleteScript()
+{
+	int row = m_scriptsList.GetCurSel();
+	int listIndex = m_scriptsList.GetItemData(row);
+	if (listIndex >= 0 && listIndex < m_xml.m_list.size())
+	{
+		m_xml.m_list.erase(m_xml.m_list.begin() + listIndex);
+		m_scriptsList.DeleteString(row);
+
+		if (m_scriptsList.GetCount() <= row)
+		{
+			row--;
+		}
+
+		if (row >= 0)
+		{
+			m_scriptsList.SetSel(row);
+			m_scriptsList.SetCurSel(row);
+			m_scriptsList.SetCaretIndex(row);
+			m_scriptsList.SetAnchorIndex(row);
+
+			OnLbnSelchangeListScripts();
+		}
+	}
+}

+ 43 - 0
ScriptEditor.h

@@ -0,0 +1,43 @@
+#pragma once
+
+#include "ChaiScriptXml.h"
+#include "DialogResizer.h"
+
+
+// CScriptEditor dialog
+
+class CScriptEditor : public CDialogEx
+{
+	DECLARE_DYNAMIC(CScriptEditor)
+
+public:
+	CScriptEditor(CWnd* pParent = NULL);   // standard constructor
+	virtual ~CScriptEditor();
+
+	CChaiScriptXml m_xml;
+
+protected:
+	
+
+// Dialog Data
+#ifdef AFX_DESIGN_TIME
+	enum { IDD = IDD_SCRIPT_EDITOR };	
+#endif
+	CListBox	m_scriptsList;
+	CDialogResizer m_resize;
+
+protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+
+	DECLARE_MESSAGE_MAP()
+public:
+	virtual BOOL OnInitDialog();
+	afx_msg void OnLbnSelchangeListScripts();
+	afx_msg void OnBnClickedButtonAddScript();
+	afx_msg void OnEnKillfocusEditName();
+	afx_msg void OnEnKillfocusEditDesc();
+	afx_msg void OnEnKillfocusEditScript();
+	afx_msg void OnBnClickedCheckActive();
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnBnClickedButtonDeleteScript();
+};

+ 2 - 2
StdAfx.h

@@ -19,8 +19,8 @@
 
 #define HITTEST_RET LRESULT
 
-#define _WIN32_WINNT 0x0600
-#define WINVER 0x0600
+#define _WIN32_WINNT 0x0605
+#define WINVER 0x0605
 
 #define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
 

+ 75 - 10
ToolTipEx.cpp

@@ -29,6 +29,7 @@ CToolTipEx::CToolTipEx(): m_dwTextStyle(DT_EXPANDTABS | DT_EXTERNALLEADING |
                        DT_NOPREFIX | DT_WORDBREAK), m_rectMargin(2, 2, 3, 3),
                         m_pNotifyWnd(NULL), m_clipId(0), m_clipRow(-1)
 {
+	m_showPersistant = false;
 }
 
 CToolTipEx::~CToolTipEx()
@@ -45,7 +46,7 @@ ON_WM_SIZE()
 ON_WM_NCHITTEST()
 ON_WM_ACTIVATE()
 ON_WM_TIMER()
-
+ON_WM_NCLBUTTONDBLCLK()
 ON_WM_NCPAINT()
 ON_WM_NCCALCSIZE()
 ON_WM_NCLBUTTONDOWN()
@@ -61,6 +62,7 @@ ON_WM_SETFOCUS()
 ON_COMMAND(ID_FIRST_HIDEDESCRIPTIONWINDOWONM, &CToolTipEx::OnFirstHidedescriptionwindowonm)
 ON_COMMAND(ID_FIRST_WRAPTEXT, &CToolTipEx::OnFirstWraptext)
 ON_WM_WINDOWPOSCHANGING()
+ON_COMMAND(ID_FIRST_ALWAYSONTOP, &CToolTipEx::OnFirstAlwaysontop)
 END_MESSAGE_MAP()
 
 
@@ -243,7 +245,7 @@ BOOL CToolTipEx::Hide()
 {
 	DELETE_BITMAP
 
-		SaveWindowSize();
+	SaveWindowSize();
 
     ShowWindow(SW_HIDE);
 
@@ -252,10 +254,22 @@ BOOL CToolTipEx::Hide()
 	m_clipId = 0;
 	m_clipRow = -1;
 	m_searchText = _T("");	
+	m_showPersistant = false;
 
     return TRUE;
 }
 
+void CToolTipEx::OnNcLButtonDblClk(UINT nHitTest, CPoint point)
+{
+	// toggle ShowPersistent when we double click the caption
+	if (nHitTest == HTCAPTION)
+	{
+		OnFirstAlwaysontop();		
+	}
+
+	CWnd::OnNcLButtonDblClk(nHitTest, point);
+}
+
 void CToolTipEx::SaveWindowSize()
 {
 	if (::IsWindowVisible(m_hWnd))
@@ -289,6 +303,17 @@ BOOL CToolTipEx::PreTranslateMessage(MSG *pMsg)
             case VK_ESCAPE:
                 Hide();
                 return TRUE;
+			case 'W':
+				OnFirstWraptext();				
+				return TRUE;
+				break;
+			case VK_SPACE:
+				if (GetKeyState(VK_CONTROL) & 0x8000)
+				{
+					OnFirstAlwaysontop();
+					return TRUE;
+				}
+				break;
             case 'C':
                 if(GetKeyState(VK_CONTROL) &0x8000)
                 {
@@ -329,11 +354,14 @@ BOOL CToolTipEx::OnMsg(MSG *pMsg)
         case WM_WINDOWPOSCHANGING:
         case WM_LBUTTONDOWN:
             {
-				if (CGetSetOptions::GetMouseClickHidesDescription())
+				if (m_showPersistant == false)
 				{
-					if (!IsCursorInToolTip())
+					if (CGetSetOptions::GetMouseClickHidesDescription())
 					{
-						Hide();
+						if (!IsCursorInToolTip())
+						{
+							Hide();
+						}
 					}
 				}
             }
@@ -386,7 +414,10 @@ BOOL CToolTipEx::OnMsg(MSG *pMsg)
 					return FALSE;
 				}
 
-                Hide();
+				if (m_showPersistant == false)
+				{
+					Hide();
+				}
 
                 break;
             }
@@ -402,7 +433,10 @@ BOOL CToolTipEx::OnMsg(MSG *pMsg)
         case WM_NCMBUTTONDOWN:
         case WM_NCMBUTTONDBLCLK:
             {
-                Hide();
+				if (m_showPersistant == false)
+				{
+					Hide();
+				}
                 break;
             }
 
@@ -885,8 +919,20 @@ void CToolTipEx::OnOptions()
 
 		if (CGetSetOptions::GetWrapDescriptionText())
 			cmSubMenu->CheckMenuItem(ID_FIRST_WRAPTEXT, MF_CHECKED);
-		
-		
+
+		if (m_showPersistant)
+			cmSubMenu->CheckMenuItem(ID_FIRST_ALWAYSONTOP, MF_CHECKED);
+
+		CString cs;
+		cmSubMenu->GetMenuString(ID_FIRST_WRAPTEXT, cs, MF_BYCOMMAND);
+		CString shortcutText = 'W';
+		if (shortcutText != _T("") &&
+			cs.Find("\t" + shortcutText) < 0)
+		{
+			cs += "\t";
+			cs += shortcutText;
+			cmSubMenu->ModifyMenu(ID_FIRST_WRAPTEXT, MF_BYCOMMAND, ID_FIRST_WRAPTEXT, cs);
+		}
 		
 		cmSubMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, pp.x, pp.y, this, NULL);
 	}
@@ -980,4 +1026,23 @@ void CToolTipEx::OnWindowPosChanging(WINDOWPOS* lpwndpos)
 	CWnd::OnWindowPosChanging(lpwndpos);
 
 	m_DittoWindow.SnapToEdge(this, lpwndpos);
-}
+}
+
+
+void CToolTipEx::OnFirstAlwaysontop()
+{
+	m_showPersistant = !m_showPersistant;
+	if (m_showPersistant)
+	{
+		m_DittoWindow.m_customWindowTitle = _T("[Always on top]");
+		m_DittoWindow.m_useCustomWindowTitle = true;
+		::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
+	}
+	else
+	{
+		m_DittoWindow.m_customWindowTitle = _T("");
+		m_DittoWindow.m_useCustomWindowTitle = true;
+	}
+
+	::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);	
+}

+ 5 - 0
ToolTipEx.h

@@ -39,6 +39,8 @@ public:
 
 	void SetClipData(CString data) { m_clipData = data; }
 
+	bool GetShowPersistant() { return m_showPersistant; }
+
 // Overrides
 	// ClassWizard generated virtual function overrides
 	//{{AFX_VIRTUAL(CToolTipEx)
@@ -72,6 +74,7 @@ protected:
 	CFont m_clipDataFont;
 	bool m_saveWindowLockout;
 	int m_clipRow;
+	bool m_showPersistant;
 
 protected:
 	CString GetFieldFromString(CString ref, int nIndex, TCHAR ch);
@@ -108,4 +111,6 @@ public:
 	afx_msg void OnPaint();	
 	afx_msg void OnFirstHidedescriptionwindowonm();
 	afx_msg void OnFirstWraptext();
+	afx_msg void OnNcLButtonDblClk(UINT nHitTest, CPoint point);
+	afx_msg void OnFirstAlwaysontop();
 };

+ 845 - 0
chaiscript/chaiscript.hpp

@@ -0,0 +1,845 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_HPP_
+#define CHAISCRIPT_HPP_
+
+
+
+/// @mainpage
+/// [ChaiScript](http://www.chaiscript.com") is a scripting language designed specifically for integration with C++. It provides
+/// seamless integration with C++ on all levels, including shared_ptr objects, functors and exceptions.
+/// 
+/// The parts of the ChaiScript API that the average user will be concerned with are contained in the 
+/// chaiscript namespace and the chaiscript::ChaiScript class.
+///
+/// The end user parts of the API are extremely simple both in size and ease of use.
+///
+/// Currently, all source control and project management aspects of ChaiScript occur on [github](http://www.github.com/ChaiScript/ChaiScript").
+///
+/// ------------------------------------------------------------
+/// 
+/// @sa chaiscript
+/// @sa chaiscript::ChaiScript
+/// @sa ChaiScript_Language for Built in Functions
+/// @sa @ref LangGettingStarted
+/// @sa @ref LangKeywordRef
+/// @sa @ref LangInPlaceRef
+/// @sa @ref LangObjectSystemRef
+/// @sa http://www.chaiscript.com
+/// @sa http://www.github.com/ChaiScript/ChaiScript
+///
+/// -----------------------------------------------------------
+///
+/// @section gettingstarted API Getting Started
+///
+/// - @ref basics
+/// - @ref compiling
+/// - @ref eval
+/// - @ref adding_items
+/// - @ref operatoroverloading
+/// - @ref add_class
+/// - @ref pointer_conversions
+/// - @ref baseclasses
+/// - @ref functionobjects
+/// - @ref threading
+/// - @ref exceptions
+/// 
+/// 
+/// @subsection basics Basics
+/// 
+/// Basic simple example:
+///
+/// ~~~~~~~{.cpp}
+/// //main.cpp
+/// #include <chaiscript/chaiscript.hpp>
+/// 
+/// double function(int i, double j)
+/// {
+///   return i * j;
+/// }
+///
+/// int main()
+/// {
+///   chaiscript::ChaiScript chai;
+///   chai.add(chaiscript::fun(&function), "function");
+///
+///   double d = chai.eval<double>("function(3, 4.75);");
+/// } 
+/// ~~~~~~~
+///
+/// ------------------------------------------------------
+///
+/// @subsection compiling Compiling ChaiScript Applications
+///
+/// ChaiScript is a header only library with only one dependency: The
+/// operating system provided dynamic library loader, which has to be specified on some platforms.
+/// 
+/// @subsubsection compilinggcc Compiling with GCC
+/// 
+/// To compile the above application on a Unix like operating system (MacOS, Linux) with GCC you need to link
+/// the dynamic loader. For example:
+///
+/// ~~~~~~~~
+/// gcc main.cpp -I/path/to/chaiscript/headers -ldl 
+/// ~~~~~~~~
+///
+/// Alternatively, you may compile without threading support.
+///
+/// ~~~~~~~~
+/// gcc main.cpp -I/path/to/chaiscript/headers -ldl -DCHAISCRIPT_NO_THREADS
+/// ~~~~~~~~
+///
+/// ------------------------------------------
+///
+/// @subsection eval Evaluating Scripts
+///
+/// Scripts can be evaluated with the () operator, eval method or eval_file method.
+///
+/// @subsubsection parenoperator () Operator
+///
+/// operator() can be used as a handy shortcut for evaluating ChaiScript snippets.
+///
+/// ~~~~~~~~{.cpp}
+/// chaiscript::ChaiScript chai;
+/// chai("print(@"hello world@")");
+/// ~~~~~~~~
+/// 
+/// @sa chaiscript::ChaiScript::operator()(const std::string &)
+///
+/// @subsubsection evalmethod Method 'eval'
+/// 
+/// The eval method is somewhat more verbose and can be used to get type safely return values
+/// from the script.
+///
+/// ~~~~~~~~{.cpp}
+/// chaiscript::ChaiScript chai;
+/// chai.eval("callsomefunc()");
+/// int result = chai.eval<int>("1 + 3");
+/// // result now equals 4
+/// ~~~~~~~~
+///
+/// @sa chaiscript::ChaiScript::eval
+///
+/// @subsubsection evalfilemethod Method 'eval_file'
+/// 
+/// The 'eval_file' method loads a file from disk and executes the script in it
+/// 
+/// ~~~~~~~~{.cpp}
+/// chaiscript::ChaiScript chai;
+/// chai.eval_file("myfile.chai");
+/// std::string result = chai.eval_file<std::string>("myfile.chai") // extract the last value returned from the file
+/// ~~~~~~~~
+/// 
+/// @sa chaiscript::ChaiScript::eval_file
+///
+/// --------------------------------------------------
+///
+/// @subsection adding_items Adding Items to ChaiScript
+///
+/// ChaiScript supports 4 basic things that can be added: objects, functions, type infos and Modules
+///
+/// @subsubsection adding_objects Adding Objects
+///
+/// Named objects can be created with the chaiscript::var function. Note: adding a object
+/// adds it to the current thread scope, not to a global scope. If you have multiple 
+/// threads that need to access the same variables you will need to add them
+/// separately for each thread, from the thread itself.
+/// 
+/// ~~~~~~~~~{.cpp}
+/// using namespace chaiscript;
+/// ChaiScript chai;
+/// int i = 5;
+/// chai.add(var(i), "i");
+/// chai("print(i)");
+/// ~~~~~~~~~
+///
+/// Immutable objects can be created with the chaiscript::const_var function.
+///
+/// ~~~~~~~~~{.cpp}
+/// chai.add(const_var(i), "i");
+/// chai("i = 5"); // exception throw, cannot assign const var
+/// ~~~~~~~~~
+/// 
+/// Named variables can only be accessed from the context they are created in.
+/// If you want a global variable, it must be const, and created with the 
+/// chaiscript::ChaiScript::add_global_const function.
+///
+/// ~~~~~~~~~{.cpp}
+/// chai.add_global_const(const_var(i), "i");
+/// chai("def somefun() { print(i); }; somefun();");
+/// ~~~~~~~~~
+/// 
+/// @subsubsection adding_functions Adding Functions
+/// 
+/// Functions, methods and members are all added using the same function: chaiscript::fun.
+///
+/// ~~~~~~~~~{.cpp}
+/// using namespace chaiscript;
+/// 
+/// class MyClass {
+///   public:
+///     int memberdata;
+///     void method();
+///     void method2(int);
+///     static void staticmethod();
+///     void overloadedmethod();
+///     void overloadedmethod(const std::string &);
+/// };
+/// 
+/// ChaiScript chai;
+/// chai.add(fun(&MyClass::memberdata), "memberdata");
+/// chai.add(fun(&MyClass::method), "method");
+/// chai.add(fun(&MyClass::staticmethod), "staticmethod");
+/// ~~~~~~~~~
+///
+/// Overloaded methods will need some help, to hint the compiler as to which overload you want:
+///
+/// ~~~~~~~~~{.cpp}
+/// chai.add(fun<void (MyClass::*)()>(&MyClass::overloadedmethod), "overloadedmethod");
+/// chai.add(fun<void (MyClass::*)(const std::string &)>(&MyClass::overloadedmethod), "overloadedmethod");
+/// ~~~~~~~~~
+///
+/// There are also shortcuts built into chaiscript::fun for binding up to the first two parameters of the function.
+/// 
+/// ~~~~~~~~~{.cpp}
+/// MyClass obj;
+/// chai.add(fun(&MyClass::method, &obj), "method");
+/// chai("method()"); // equiv to obj.method()
+/// chai.add(fun(&MyClass::method2, &obj, 3), "method2");
+/// chai("method2()"); // equiv to obj.method2(3)
+/// ~~~~~~~~~
+///
+/// @subsubsection addingtypeinfo Adding Type Info
+///
+/// ChaiScript will automatically support any type implicitly provided to it in the form
+/// of objects and function parameters / return types. However, it can be nice to let ChaiScript 
+/// know more details about the types you are giving it. For instance, the "clone" functionality
+/// cannot work unless there is a copy constructor registered and the name of the type is known
+/// (so that ChaiScript can look up the copy constructor).
+///
+/// Continuing with the example "MyClass" from above:
+///
+/// ~~~~~~~~{.cpp}
+/// chai.add(user_type<MyClass>(), "MyClass");
+/// ~~~~~~~~
+///
+/// @subsubsection adding_modules Adding Modules
+/// 
+/// Modules are holders for collections of ChaiScript registrations.
+///
+/// ~~~~~~~~{.cpp}
+/// ModulePtr module = get_sum_module();
+/// chai.add(module);
+/// ~~~~~~~~
+/// 
+/// @sa chaiscript::Module
+///
+/// -----------------------------------------------------------------------
+///
+/// @subsection operatoroverloading Operator Overloading
+/// 
+/// Operators are just like any other function in ChaiScript, to overload an operator, simply register it.
+/// 
+/// ~~~~~~~~{.cpp} 
+/// class MyClass {
+///   MyClass operator+(const MyClass &) const;
+/// };
+///
+/// chai.add(fun(&MyClass::operator+), "+");
+///
+/// std::string append_string_int(const std::string &t_lhs, int t_rhs)
+/// {
+///   std::stringstream ss;
+///   ss << t_lhs << t_rhs;
+///   return ss.str();
+/// }
+///
+/// chai.add(fun(append_string_int), "+");
+/// ~~~~~~~~
+///
+/// @sa @ref adding_functions
+///
+/// -----------------------------------------------------------------------
+///
+/// @subsection add_class Class Helper Utility
+/// 
+/// Much of the work of adding new classes to ChaiScript can be reduced with the help
+/// of the add_class helper utility.
+///
+/// ~~~~~~~~{.cpp}
+/// class Test
+/// {
+///   public:
+///     void function() {}
+///     std::string function2() { return "Function2"; }
+///     void function3() {}
+///     std::string functionOverload(double) { return "double"; }
+///     std::string functionOverload(int) { return "int"; }
+/// };
+///
+/// int main()
+/// {
+///   chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module());
+///
+///   chaiscript::utility::add_class<chaiscript::Test>(*m,
+///      "Test",
+///      { constructor<Test()>(),
+///        constructor<Test(const Test &)>() },
+///      { {fun(&Test::function), "function"},
+///        {fun(&Test::function2), "function2"},
+///        {fun(&Test::function2), "function3"} 
+///        {fun(static_cast<std::string Test::*(double)>(&Test::functionOverload)), "functionOverload"} 
+///        {fun(static_cast<std::string Test::*(int)>(&Test::functionOverload)), "functionOverload"} }
+///      );
+///
+///
+///   chaiscript::ChaiScript chai;
+///   chai.add(m);
+/// }
+/// ~~~~~~~~
+/// 
+/// @sa @ref adding_modules
+///
+/// -----------------------------------------------------------------------
+///
+/// @subsection pointer_conversions Pointer / Object Conversions
+///
+/// As much as possible, ChaiScript attempts to convert between &, *, const &, const *, std::shared_ptr<T>,
+/// std::shared_ptr<const T>, std::reference_wrapper<T>, std::reference_wrapper<const T> and value types automatically.
+///
+/// If a chaiscript::var object was created in C++ from a pointer, it cannot be converted to a shared_ptr (this would add invalid reference counting).
+/// Const may be added, but never removed. 
+///
+/// The take away is that you can pretty much expect function calls to Just Work when you need them to.
+///
+/// ~~~~~~~~{.cpp}
+/// void fun1(const int *);
+/// void fun2(int *);
+/// void fun3(int);
+/// void fun4(int &);
+/// void fun5(const int &);
+/// void fun5(std::shared_ptr<int>);
+/// void fun6(std::shared_ptr<const int>);
+/// void fun7(const std::shared_ptr<int> &);
+/// void fun8(const std::shared_ptr<const int> &);
+/// void fun9(std::reference_wrapper<int>);
+/// void fun10(std::reference_wrapper<const int>);
+///
+/// int main()
+/// {
+///   using namespace chaiscript
+///   chaiscript::ChaiScript chai;
+///   chai.add(fun(fun1), "fun1");
+///   chai.add(fun(fun2), "fun2");
+///   chai.add(fun(fun3), "fun3");
+///   chai.add(fun(fun4), "fun4");
+///   chai.add(fun(fun5), "fun5");
+///   chai.add(fun(fun6), "fun6");
+///   chai.add(fun(fun7), "fun7");
+///   chai.add(fun(fun8), "fun8");
+///   chai.add(fun(fun9), "fun9");
+///   chai.add(fun(fun10), "fun10");
+///
+///   chai("var i = 10;");
+///   chai("fun1(i)");
+///   chai("fun2(i)");
+///   chai("fun3(i)");
+///   chai("fun4(i)");
+///   chai("fun5(i)");
+///   chai("fun6(i)");
+///   chai("fun7(i)");
+///   chai("fun8(i)");
+///   chai("fun9(i)");
+///   chai("fun10(i)");
+/// }  
+/// ~~~~~~~~
+///
+/// See the unit test unittests/boxed_cast_test.cpp for a complete breakdown of the automatic casts that 
+/// available and tested.
+/// 
+/// -----------------------------------------------------------------------
+///
+/// @subsection baseclasses Base Classes
+/// 
+/// ChaiScript supports handling of passing a derived class object to a function expecting a base class object.
+/// For the process to work, the base/derived relationship must be registered with the engine.
+///
+/// ~~~~~~~~{.cpp}
+/// class Base {};
+/// class Derived : public Base {};
+/// void myfunction(Base *b);
+///
+/// int main()
+/// {
+///   chaiscript::ChaiScript chai;
+///   chai.add(chaiscript::base_class<Base, Derived>());
+///   Derived d;
+///   chai.add(chaiscript::var(&d), "d");
+///   chai.add(chaiscript::fun(&myfunction), "myfunction");
+///   chai("myfunction(d)");
+/// }
+/// ~~~~~~~~
+/// 
+/// -----------------------------------------------------------------------
+///
+///
+/// @subsection functionobjects Function Objects
+///
+/// Functions are first class objects in ChaiScript and ChaiScript supports automatic conversion
+/// between ChaiScript functions and std::function objects.
+///
+/// ~~~~~~~~{.cpp}
+/// void callafunc(const std::function<void (const std::string &)> &t_func)
+/// {
+///   t_func("bob");
+/// }
+/// 
+/// int main()
+/// {
+///   chaiscript::ChaiScript chai;
+///   chai.add(chaiscript::fun(&callafunc), "callafunc");
+///   chai("callafunc(fun(x) { print(x); })"); // pass a lambda function to the registered function
+///                                            // which expects a typed std::function
+///
+///   std::function<void ()> f = chai.eval<std::function<void ()> >("dump_system");
+///   f(); // call the ChaiScript function dump_system, from C++
+/// }
+/// ~~~~~~~~
+/// 
+/// -----------------------------------------------------------------------
+///
+///
+/// @subsection threading Threading
+/// 
+/// Thread safety is automatically handled within the ChaiScript system. Objects can be added
+/// and scripts executed from multiple threads. For each thread that executes scripts, a new
+/// context is created and managed by the engine.
+/// 
+/// Thread safety can be disabled by defining CHAISCRIPT_NO_THREADS when using the library.
+///
+/// Disabling thread safety increases performance in many cases.
+///
+/// -----------------------------------------------------------------------
+///
+///
+/// @subsection exceptions Exception Handling
+/// 
+/// @subsubsection exceptionsbasics Exception Handling Basics
+///
+/// Exceptions can be thrown in ChaiScript and caught in C++ or thrown in C++ and caught in 
+/// ChaiScript.
+///
+/// ~~~~~~~~{.cpp}
+/// void throwexception()
+/// {
+///   throw std::runtime_error("err");
+/// }
+/// 
+/// int main()
+/// {
+///   // Throw in C++, catch in ChaiScript
+///   chaiscript::ChaiScript chai;
+///   chai.add(chaiscript::fun(&throwexception), "throwexception");
+///   chai("try { throwexception(); } catch (e) { print(e.what()); }"); // prints "err"
+/// 
+///   // Throw in ChaiScript, catch in C++
+///   try {
+///     chai("throw(1)");
+///   } catch (chaiscript::Boxed_Value bv) {
+///     int i = chaiscript::boxed_cast<int>(bv);
+///     // i == 1
+///   }
+/// }
+/// ~~~~~~~~
+/// 
+/// @subsubsection exceptionsautomatic Exception Handling Automatic Unboxing
+///
+/// As an alternative to the manual unboxing of exceptions shown above, exception specifications allow the user to tell 
+/// ChaiScript what possible exceptions are expected from the script being executed. 
+///
+/// Example:
+/// ~~~~~~~~{.cpp}
+/// chaiscript::ChaiScript chai;
+///
+/// try {
+///   chai.eval("throw(runtime_error(@"error@"))", chaiscript::exception_specification<int, double, float, const std::string &, const std::exception &>());
+/// } catch (const double e) {
+/// } catch (int) {
+/// } catch (float) {
+/// } catch (const std::string &) {
+/// } catch (const std::exception &e) {
+///   // This is the one what will be called in the specific throw() above
+/// }
+/// ~~~~~~~~
+///
+/// @sa chaiscript::Exception_Handler for details on automatic exception unboxing 
+/// @sa chaiscript::exception_specification
+
+
+
+/// @page LangObjectSystemRef ChaiScript Language Object Model Reference
+///
+///
+/// ChaiScript has an object system built in, for types defined within the ChaiScript system.
+///
+/// ~~~~~~~~~
+/// attr Rectangle::height
+/// attr Rectangle::width
+/// def Rectangle::Rectangle() { this.height = 10; this.width = 20 }
+/// def Rectangle::area() { this.height * this.width }
+/// 
+/// var rect = Rectangle()
+/// rect.height = 30
+/// print(rect.area())
+/// ~~~~~~~~~
+///
+/// Since ChaiScript 5.4.0 it has been possible to use the "class" keyword to simplify this code.
+///
+/// ~~~~~~~~~
+/// class Rectangle {
+///   attr height
+///   attr width
+///   def Rectangle() { this.height = 10; this.width = 20 }
+///   def area() { this.height * this.width }
+/// }
+///
+/// var rect = Rectangle()
+/// rect.height = 30
+/// print(rect.area())
+/// ~~~~~~~~~
+///
+/// @sa @ref keywordattr
+/// @sa @ref keyworddef
+
+/// @page LangInPlaceRef ChaiScript Language In-Place Creation Reference
+/// @section inplacevector Vector
+/// 
+/// ~~~~~~~~~
+/// In-place Vector ::= "[" [expression ("," expression)*]  "]"
+/// ~~~~~~~~~
+///
+/// @section inplacerangedvector Ranged Vector
+///
+/// ~~~~~~~~~
+/// In-place Ranged Vector ::= "[" value ".." value "]"
+/// ~~~~~~~~~
+///
+/// Creates a vector over a range (eg. 1..10)
+///
+/// @section inplacemap Map
+///
+/// ~~~~~~~~
+/// In-place Map ::= "[" (string ":" expression)+ "]"
+/// ~~~~~~~~
+
+/// @page LangGettingStarted ChaiScript Language Getting Started
+/// 
+/// ChaiScript is a simple language that should feel familiar to anyone who knows
+/// C++ or ECMAScript (JavaScript). 
+///
+/// -----------------------------------------------------------------------
+///
+/// @section chaiscriptloops Loops
+///
+/// Common looping constructs exist in ChaiScript
+///
+/// ~~~~~~~~
+/// var i = 0;
+/// while (i < 10)
+/// {
+///   // do something
+///   ++i;
+/// }
+/// ~~~~~~~~
+///
+/// ~~~~~~~~
+/// for (var i = 0; i < 10; ++i)
+/// {
+///   // do something
+/// }
+/// ~~~~~~~~
+///
+/// @sa @ref keywordfor
+/// @sa @ref keywordwhile
+///
+/// -----------------------------------------------------------------------
+///
+/// @section chaiscriptifs Conditionals
+///
+/// If statements work as expected
+/// 
+/// ~~~~~~~~
+/// var b = true;
+/// 
+/// if (b) {
+///   // do something
+/// } else if (c < 10) {
+///   // do something else
+/// } else {
+///   // or do this
+/// }
+/// ~~~~~~~~
+///
+/// @sa @ref keywordif
+///
+/// -----------------------------------------------------------------------
+///
+/// @section chaiscriptfunctions Functions
+/// 
+/// Functions are defined with the def keyword
+/// 
+/// ~~~~~~~~
+/// def myfun(x) { print(x); }
+///
+/// myfun(10);
+/// ~~~~~~~~
+///
+/// Functions may have "guards" which determine if which is called.
+///
+/// ~~~~~~~~
+/// eval> def myfun2(x) : x < 10 { print("less than 10"); }
+/// eval> def myfun2(x) : x >= 10 { print("10 or greater"); }
+/// eval> myfun2(5)
+/// less than 10
+/// eval> myfun2(12)
+/// 10 or greater
+/// ~~~~~~~~
+/// 
+/// @sa @ref keyworddef
+/// @sa @ref keywordattr
+/// @sa @ref LangObjectSystemRef
+///
+/// -----------------------------------------------------------------------
+///
+/// @section chaiscriptfunctionobjects Function Objects
+/// 
+/// Functions are first class types in ChaiScript and can be used as variables.
+///
+/// ~~~~~~~~
+/// eval> var p = print;
+/// eval> p(1);
+/// 1
+/// ~~~~~~~~
+/// 
+/// They can also be passed to functions. 
+///
+/// ~~~~~~~~
+/// eval> def callfunc(f, lhs, rhs) { return f(lhs, rhs); }
+/// eval> def do_something(lhs, rhs) { print("lhs: ${lhs}, rhs: ${rhs}"); }
+/// eval> callfunc(do_something, 1, 2);
+/// lhs: 1, rhs: 2
+/// ~~~~~~~~
+///
+/// Operators can also be treated as functions by using the back tick operator. Building on the above example:
+/// 
+/// ~~~~~~~~
+/// eval> callfunc(`+`, 1, 4);
+/// 5
+/// eval> callfunc(`*`, 3, 2);
+/// 6
+/// ~~~~~~~~
+///
+/// -----------------------------------------------------------------------
+///
+/// @sa @ref LangKeywordRef
+/// @sa ChaiScript_Language for Built in Functions
+
+
+/// @page LangKeywordRef ChaiScript Language Keyword Reference
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordattr attr
+/// Defines a ChaiScript object attribute
+/// 
+/// ~~~~~~~~ 
+/// Attribute Definition ::= "attr" class_name "::" attribute_name
+/// ~~~~~~~~
+///
+/// @sa @ref LangObjectSystemRef
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordauto auto
+///
+/// Defines a variable
+///
+/// ~~~~~~~~
+/// Variable ::= "auto" identifier
+/// ~~~~~~~~
+///
+/// Synonym for @ref keywordvar
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordbreak break
+/// Stops execution of a looping block.
+///
+/// ~~~~~~~~
+/// Break Statement ::= "break"
+/// ~~~~~~~~
+///
+/// @sa @ref keywordfor
+/// @sa @ref keywordwhile 
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keyworddef def
+/// Begins a function or method definition
+///
+/// ~~~~~~~~
+/// Function Definition ::= "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block
+/// Method Definition ::= "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block
+/// ~~~~~~~~
+/// 
+/// identifier: name of function. Required.
+/// args: comma-delimited list of parameter names with optional type specifiers. Optional.
+/// guards: guarding statement that act as a prerequisite for the function. Optional.
+/// { }: scoped block as function body. Required.
+///
+/// Functions return values in one of two ways:
+///
+/// By using an explicit return call, optionally passing the value to be returned.
+/// By implicitly returning the value of the last expression (if it is not a while or for loop).
+///
+/// Method definitions for known types extend those types with new methods. This includes C++ and ChaiScript defined types.
+/// Method definitions for unknown types implicitly define the named type.
+///
+/// @sa @ref LangObjectSystemRef
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordelse else
+/// @sa @ref keywordif 
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordfor for
+/// ~~~~~~~~
+/// For Block ::= "for" "(" [initial] ";" stop_condition ";" loop_expression ")" block
+/// ~~~~~~~~
+/// This loop can be broken using the @ref keywordbreak command.
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordfun fun
+/// Begins an anonymous function declaration (sometimes called a lambda).
+///
+/// ~~~~~~~~
+/// Lambda ::= "fun" "(" [variable] ("," variable)*  ")" block
+/// ~~~~~~~~
+///
+/// _Example_
+///
+/// ~~~~~~~~
+/// // Generate an anonymous function object that adds 2 to its parameter
+/// var f = fun(x) { x + 2; }
+/// ~~~~~~~~
+///
+/// @sa @ref keyworddef for more details on ChaiScript functions
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordif if
+/// Begins a conditional block of code that only executes if the condition evaluates as true.
+/// ~~~~~~~~
+/// If Block ::= "if" "(" condition ")" block
+/// Else If Block ::= "else if" "(" condition ")" block
+/// Else Block ::= "else" block
+/// ~~~~~~~~
+/// 
+/// _Example_
+///
+/// ~~~~~~~~
+/// if (true) {
+///   // do something
+/// } else if (false) {
+///   // do something else
+/// } else {
+///   // otherwise do this
+/// }
+/// ~~~~~~~~
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordtry try
+/// ~~~~~~~~
+/// Try Block ::= "try" block 
+///  ("catch" ["(" [type] variable ")"] [":" guards] block)+ 
+///    ["finally" block]
+/// ~~~~~~~~
+///
+/// @sa ChaiScript_Language::throw
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordwhile while
+/// 
+/// Begins a conditional block of code that loops 0 or more times, as long as the condition is true
+///
+/// ~~~~~~~~
+/// While Block ::= "while" "(" condition ")" block
+/// ~~~~~~~~
+///
+/// This loop can be broken using the @ref keywordbreak command.
+///
+///
+/// -----------------------------------------------------------------------
+///
+/// @section keywordvar var
+/// 
+/// Defines a variable
+///
+/// ~~~~~~~~
+/// Variable ::= "var" identifier
+/// ~~~~~~~~
+///
+/// Synonym for @ref keywordauto
+
+
+/// @namespace chaiscript
+/// @brief Namespace chaiscript contains every API call that the average user will be concerned with.
+
+/// @namespace chaiscript::detail
+/// @brief Classes and functions reserved for internal use. Items in this namespace are not supported.
+
+#include "chaiscript_basic.hpp"
+#include "language/chaiscript_parser.hpp"
+#include "chaiscript_stdlib.hpp"
+
+
+namespace chaiscript 
+{
+  class ChaiScript : public ChaiScript_Basic
+  {
+    public:
+      ChaiScript(std::vector<std::string> t_modulepaths = {},
+          std::vector<std::string> t_usepaths = {},
+          const std::vector<Options> &t_opts = chaiscript::default_options())
+        : ChaiScript_Basic(
+            chaiscript::Std_Lib::library(),
+            std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>(),
+            t_modulepaths, t_usepaths, t_opts)
+        {
+        }
+  };
+}
+
+#endif /* CHAISCRIPT_HPP_ */

+ 39 - 0
chaiscript/chaiscript_basic.hpp

@@ -0,0 +1,39 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_BASIC_HPP_
+#define CHAISCRIPT_BASIC_HPP_
+
+#include "chaiscript_defines.hpp"
+
+#include "dispatchkit/dispatchkit.hpp"
+#include "dispatchkit/function_call.hpp"
+#include "dispatchkit/dynamic_object.hpp"
+#include "dispatchkit/boxed_number.hpp"
+
+#include "language/chaiscript_eval.hpp"
+#include "language/chaiscript_engine.hpp"
+
+// This file includes all of the basic requirements for ChaiScript,
+// to use, you might do something like:
+//
+
+/*
+
+#include "chaiscript_stdlib.hpp"
+#include "language/chaiscript_parser.hpp"
+
+ChaiScript_Basic chai(
+          chaiscript::Std_Lib::library(),
+          std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>());
+
+*/
+
+// If you want a fully packaged ready to go ChaiScript, use chaiscript.hpp
+
+
+
+#endif /* CHAISCRIPT_BASIC_HPP_ */

+ 237 - 0
chaiscript/chaiscript_defines.hpp

@@ -0,0 +1,237 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_DEFINES_HPP_
+#define CHAISCRIPT_DEFINES_HPP_
+
+#ifdef _MSC_VER
+#define CHAISCRIPT_STRINGIZE(x) "" #x
+#define CHAISCRIPT_STRINGIZE_EXPANDED(x) CHAISCRIPT_STRINGIZE(x)
+#define CHAISCRIPT_COMPILER_VERSION CHAISCRIPT_STRINGIZE_EXPANDED(_MSC_FULL_VER)
+#define CHAISCRIPT_MSVC _MSC_VER
+#define CHAISCRIPT_HAS_DECLSPEC
+
+static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later required");
+
+#else
+#define CHAISCRIPT_COMPILER_VERSION __VERSION__
+#endif
+
+#include <vector>
+
+#if defined( _LIBCPP_VERSION )
+#define CHAISCRIPT_LIBCPP
+#endif
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define CHAISCRIPT_WINDOWS
+#endif
+
+#if defined(_WIN32)
+#if defined(__llvm__)
+#define CHAISCRIPT_COMPILER_NAME "clang(windows)"
+#elif defined(__GNUC__)
+#define CHAISCRIPT_COMPILER_NAME "gcc(mingw)"
+#else
+#define CHAISCRIPT_COMPILER_NAME "msvc"
+#endif
+#else
+#if defined(__llvm__)
+#define CHAISCRIPT_COMPILER_NAME "clang"
+#elif defined(__GNUC__)
+#define CHAISCRIPT_COMPILER_NAME "gcc"
+#else
+#define CHAISCRIPT_COMPILER_NAME "unknown"
+#endif
+#endif
+
+
+#if defined(__llvm__)
+#define CHAISCRIPT_CLANG
+#endif
+
+
+#ifdef  CHAISCRIPT_HAS_DECLSPEC
+#define CHAISCRIPT_MODULE_EXPORT extern "C" __declspec(dllexport)
+#else
+#define CHAISCRIPT_MODULE_EXPORT extern "C" 
+#endif
+
+#if defined(CHAISCRIPT_MSVC) || (defined(__GNUC__) && __GNUC__ >= 5) || defined(CHAISCRIPT_CLANG)
+#define CHAISCRIPT_UTF16_UTF32
+#endif
+
+#ifdef _DEBUG
+#define CHAISCRIPT_DEBUG true
+#else
+#define CHAISCRIPT_DEBUG false
+#endif
+
+#include <memory>
+#include <string>
+#include <cmath>
+
+namespace chaiscript {
+  static const int version_major = 6;
+  static const int version_minor = 0;
+  static const int version_patch = 0;
+
+  static const char *compiler_version = CHAISCRIPT_COMPILER_VERSION;
+  static const char *compiler_name = CHAISCRIPT_COMPILER_NAME;
+  static const bool debug_build = CHAISCRIPT_DEBUG;
+
+  template<typename B, typename D, typename ...Arg>
+  inline std::shared_ptr<B> make_shared(Arg && ... arg)
+  {
+#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED
+    return std::make_shared<D>(std::forward<Arg>(arg)...);
+#else
+    return std::shared_ptr<B>(static_cast<B*>(new D(std::forward<Arg>(arg)...)));
+#endif
+  }
+
+  template<typename B, typename D, typename ...Arg>
+  inline std::unique_ptr<B> make_unique(Arg && ... arg)
+  {
+#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED
+    return std::make_unique<D>(std::forward<Arg>(arg)...);
+#else
+    return std::unique_ptr<B>(static_cast<B*>(new D(std::forward<Arg>(arg)...)));
+#endif
+  }
+
+  struct Build_Info {
+    static int version_major()
+    {
+      return chaiscript::version_major;
+    }
+
+    static int version_minor()
+    {
+      return chaiscript::version_minor;
+    }
+
+    static int version_patch()
+    {
+      return chaiscript::version_patch;
+    }
+
+    static std::string version()
+    {
+      return std::to_string(version_major()) + '.' + std::to_string(version_minor()) + '.' + std::to_string(version_patch());
+    }
+
+    static std::string compiler_id()
+    {
+      return compiler_name() + '-' + compiler_version();
+    }
+
+    static std::string build_id()
+    {
+      return compiler_id() + (debug_build()?"-Debug":"-Release");
+    }
+
+    static std::string compiler_version()
+    {
+      return chaiscript::compiler_version;
+    }
+
+    static std::string compiler_name()
+    {
+      return chaiscript::compiler_name;
+    }
+
+    static bool debug_build()
+    {
+      return chaiscript::debug_build;
+    }
+  };
+
+
+  template<typename T>
+    auto parse_num(const char *t_str) -> typename std::enable_if<std::is_integral<T>::value, T>::type
+    {
+      T t = 0;
+      for (char c = *t_str; (c = *t_str) != 0; ++t_str) {
+        if (c < '0' || c > '9') {
+          return t;
+        }
+        t *= 10;
+        t += c - '0';
+      }
+      return t;
+    }
+
+
+  template<typename T>
+    auto parse_num(const char *t_str) -> typename std::enable_if<!std::is_integral<T>::value, T>::type
+    {
+      T t = 0;
+      T base = 0;
+      T decimal_place = 0;
+      bool exponent = false;
+      bool neg_exponent = false;
+
+      const auto final_value = [](const T val, const T baseval, const bool hasexp, const bool negexp) -> T {
+        if (!hasexp) {
+          return val;
+        } else {
+          return baseval * std::pow(T(10), val*T(negexp?-1:1));
+        }
+      };
+
+      for(; *t_str != '\0'; ++t_str) {
+        char c = *t_str;
+        if (c == '.') {
+          decimal_place = 10;
+        } else if (c == 'e' || c == 'E') {
+          exponent = true;
+          decimal_place = 0;
+          base = t;
+          t = 0;
+        } else if (c == '-' && exponent) {
+          neg_exponent = true;
+        } else if (c == '+' && exponent) {
+          neg_exponent = false;
+        } else if (c < '0' || c > '9') {
+          return final_value(t, base, exponent, neg_exponent);
+        } else if (decimal_place < T(10)) {
+          t *= T(10);
+          t += T(c - '0');
+        } else {
+          t += (T(c - '0') / (T(decimal_place)));
+          decimal_place *= 10;
+        }
+      }
+
+      return final_value(t, base, exponent, neg_exponent);
+    }
+
+  template<typename T>
+    T parse_num(const std::string &t_str)
+    {
+      return parse_num<T>(t_str.c_str());
+    }
+
+  enum class Options
+  {
+    No_Load_Modules,
+    Load_Modules,
+    No_External_Scripts,
+    External_Scripts
+  };
+
+  static inline std::vector<Options> default_options()
+  {
+#ifdef CHAISCRIPT_NO_DYNLOAD
+    return {Options::No_Load_Modules, Options::External_Scripts};
+#else
+    return {Options::Load_Modules, Options::External_Scripts};
+#endif
+  }
+}
+#endif
+

+ 71 - 0
chaiscript/chaiscript_stdlib.hpp

@@ -0,0 +1,71 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// and Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_STDLIB_HPP_
+#define CHAISCRIPT_STDLIB_HPP_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "chaiscript_defines.hpp"
+#include "language/chaiscript_common.hpp"
+
+#include "dispatchkit/function_call.hpp"
+
+//#include "dispatchkit/dispatchkit.hpp"
+#include "dispatchkit/operators.hpp"
+#include "dispatchkit/bootstrap.hpp"
+#include "dispatchkit/bootstrap_stl.hpp"
+//#include "dispatchkit/boxed_value.hpp"
+#include "language/chaiscript_prelude.hpp"
+#include "dispatchkit/register_function.hpp"
+#include "utility/json_wrap.hpp"
+
+#ifndef CHAISCRIPT_NO_THREADS
+#include <future>
+#endif
+
+
+/// @file
+///
+/// This file generates the standard library that normal ChaiScript usage requires.
+
+namespace chaiscript
+{
+  class Std_Lib
+  {
+    public:
+
+      static ModulePtr library()
+      {
+        auto lib = std::make_shared<Module>();
+        bootstrap::Bootstrap::bootstrap(*lib);
+
+        bootstrap::standard_library::vector_type<std::vector<Boxed_Value> >("Vector", *lib);
+        bootstrap::standard_library::string_type<std::string>("string", *lib);
+        bootstrap::standard_library::map_type<std::map<std::string, Boxed_Value> >("Map", *lib);
+        bootstrap::standard_library::pair_type<std::pair<Boxed_Value, Boxed_Value > >("Pair", *lib);
+
+#ifndef CHAISCRIPT_NO_THREADS
+        bootstrap::standard_library::future_type<std::future<chaiscript::Boxed_Value>>("future", *lib);
+        lib->add(chaiscript::fun([](const std::function<chaiscript::Boxed_Value ()> &t_func){ return std::async(std::launch::async, t_func);}), "async");
+#endif
+
+        json_wrap::library(*lib);
+
+        lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ );
+
+        return lib;
+      }
+
+  };
+}
+
+#endif
+

+ 173 - 0
chaiscript/chaiscript_threading.hpp

@@ -0,0 +1,173 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_THREADING_HPP_
+#define CHAISCRIPT_THREADING_HPP_
+
+
+#include <unordered_map>
+
+#ifndef CHAISCRIPT_NO_THREADS
+#include <thread>
+#include <mutex>
+#else
+#ifndef CHAISCRIPT_NO_THREADS_WARNING
+#pragma message ("ChaiScript is compiling without thread safety.")
+#endif
+#endif
+
+#include "chaiscript_defines.hpp"
+
+/// \file
+///
+/// This file contains code necessary for thread support in ChaiScript.
+/// If the compiler definition CHAISCRIPT_NO_THREADS is defined then thread safety
+/// is disabled in ChaiScript. This has the result that some code is faster, because mutex locks are not required.
+/// It also has the side effect that the chaiscript::ChaiScript object may not be accessed from more than
+/// one thread simultaneously.
+
+namespace chaiscript
+{
+  namespace detail
+  {
+    /// If threading is enabled, then this namespace contains std thread classes.
+    /// If threading is not enabled, then stubbed in wrappers that do nothing are provided.
+    /// This allows us to avoid \#ifdef code in the sections that need thread safety.
+    namespace threading
+    {
+
+#ifndef CHAISCRIPT_NO_THREADS
+
+      template<typename T>
+        using unique_lock = std::unique_lock<T>;
+
+      template<typename T>
+        using shared_lock = std::unique_lock<T>;
+
+      template<typename T>
+        using lock_guard = std::lock_guard<T>;
+
+
+      using shared_mutex = std::mutex;
+
+      using std::mutex;
+
+      using std::recursive_mutex;
+
+      /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If
+      /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from.
+      template<typename T>
+        class Thread_Storage
+        {
+          public:
+            Thread_Storage() = default;
+            Thread_Storage(const Thread_Storage &) = delete;
+            Thread_Storage(Thread_Storage &&) = delete;
+            Thread_Storage &operator=(const Thread_Storage &) = delete;
+            Thread_Storage &operator=(Thread_Storage &&) = delete;
+
+            ~Thread_Storage()
+            {
+              t().erase(this);
+            }
+
+            inline const T *operator->() const
+            {
+              return &(t()[this]);
+            }
+
+            inline const T &operator*() const
+            {
+              return t()[this];
+            }
+
+            inline T *operator->()
+            {
+              return &(t()[this]);
+            }
+
+            inline T &operator*()
+            {
+              return t()[this];
+            }
+
+
+            void *m_key;
+
+          private:
+            static std::unordered_map<const void*, T> &t()
+            {
+              thread_local std::unordered_map<const void *, T> my_t;
+              return my_t;
+            }
+        };
+
+#else // threading disabled
+      template<typename T>
+      class unique_lock 
+      {
+        public:
+          explicit unique_lock(T &) {}
+          void lock() {}
+          void unlock() {}
+      };
+
+      template<typename T>
+      class shared_lock 
+      {
+        public:
+          explicit shared_lock(T &) {}
+          void lock() {}
+          void unlock() {}
+      };
+
+      template<typename T>
+      class lock_guard 
+      {
+        public:
+          explicit lock_guard(T &) {}
+      };
+
+      class shared_mutex { };
+
+      class recursive_mutex {};
+
+
+      template<typename T>
+        class Thread_Storage
+        {
+          public:
+            explicit Thread_Storage()
+            {
+            }
+
+            inline T *operator->() const
+            {
+              return &obj;
+            }
+
+            inline T &operator*() const
+            {
+              return obj;
+            }
+
+          private:
+            mutable T obj;
+        };
+
+#endif
+    }
+  }
+}
+
+
+
+#endif
+

+ 164 - 0
chaiscript/dispatchkit/any.hpp

@@ -0,0 +1,164 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// and Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_ANY_HPP_
+#define CHAISCRIPT_ANY_HPP_
+
+#include <utility>
+
+namespace chaiscript {
+  namespace detail {
+    namespace exception
+    {
+      /// \brief Thrown in the event that an Any cannot be cast to the desired type
+      ///
+      /// It is used internally during function dispatch.
+      ///
+      /// \sa chaiscript::detail::Any
+      class bad_any_cast : public std::bad_cast
+      {
+        public:
+          bad_any_cast() = default;
+
+          bad_any_cast(const bad_any_cast &) = default;
+
+          ~bad_any_cast() noexcept override = default;
+
+          /// \brief Description of what error occurred
+          const char * what() const noexcept override
+          {
+            return m_what.c_str();
+          }
+
+        private:
+          std::string m_what = "bad any cast";
+      };
+    }
+  
+
+    class Any {
+      private:
+        struct Data
+        {
+          explicit Data(const std::type_info &t_type) 
+            : m_type(t_type)
+          {
+          }
+
+          Data &operator=(const Data &) = delete;
+
+          virtual ~Data() = default;
+
+          virtual void *data() = 0;
+
+          const std::type_info &type() const
+          {
+            return m_type;
+          }
+
+          virtual std::unique_ptr<Data> clone() const = 0;
+          const std::type_info &m_type;
+        };
+
+        template<typename T>
+          struct Data_Impl : Data
+          {
+            explicit Data_Impl(T t_type)
+              : Data(typeid(T)),
+                m_data(std::move(t_type))
+            {
+            }
+
+            void *data() override
+            {
+              return &m_data;
+            }
+
+            std::unique_ptr<Data> clone() const override
+            {
+              return std::unique_ptr<Data>(new Data_Impl<T>(m_data));
+            }
+
+            Data_Impl &operator=(const Data_Impl&) = delete;
+
+            T m_data;
+          };
+
+        std::unique_ptr<Data> m_data;
+
+      public:
+        // construct/copy/destruct
+        Any() = default;
+        Any(Any &&) = default;
+        Any &operator=(Any &&t_any) = default;
+
+        Any(const Any &t_any) 
+        { 
+          if (!t_any.empty())
+          {
+            m_data = t_any.m_data->clone(); 
+          } else {
+            m_data.reset();
+          }
+        }
+
+
+        template<typename ValueType,
+          typename = typename std::enable_if<!std::is_same<Any, typename std::decay<ValueType>::type>::value>::type>
+        explicit Any(ValueType &&t_value)
+          : m_data(std::unique_ptr<Data>(new Data_Impl<typename std::decay<ValueType>::type>(std::forward<ValueType>(t_value))))
+        {
+        }
+
+
+        Any & operator=(const Any &t_any)
+        {
+          Any copy(t_any);
+          swap(copy);
+          return *this; 
+        }
+
+        template<typename ToType>
+          ToType &cast() const
+          {
+            if (m_data && typeid(ToType) == m_data->type())
+            {
+              return *static_cast<ToType *>(m_data->data());
+            } else {
+              throw chaiscript::detail::exception::bad_any_cast();
+            }
+          }
+
+
+        // modifiers
+        Any & swap(Any &t_other)
+        {
+          std::swap(t_other.m_data, m_data);
+          return *this;
+        }
+
+        // queries
+        bool empty() const
+        {
+          return !bool(m_data);
+        }
+
+        const std::type_info & type() const
+        {
+          if (m_data) {
+            return m_data->type();
+          } else {
+            return typeid(void);
+          }
+        }
+    };
+
+  }
+}
+
+#endif
+
+

+ 73 - 0
chaiscript/dispatchkit/bad_boxed_cast.hpp

@@ -0,0 +1,73 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_BAD_BOXED_CAST_HPP_
+#define CHAISCRIPT_BAD_BOXED_CAST_HPP_
+
+#include <string>
+#include <typeinfo>
+
+#include "../chaiscript_defines.hpp"
+#include "type_info.hpp"
+
+namespace chaiscript {
+class Type_Info;
+}  // namespace chaiscript
+
+namespace chaiscript 
+{
+  namespace exception
+  {
+    /// \brief Thrown in the event that a Boxed_Value cannot be cast to the desired type
+    ///
+    /// It is used internally during function dispatch and may be used by the end user.
+    ///
+    /// \sa chaiscript::boxed_cast
+    class bad_boxed_cast : public std::bad_cast
+    {
+      public:
+        bad_boxed_cast(Type_Info t_from, const std::type_info &t_to,
+            std::string t_what) noexcept
+          : from(t_from), to(&t_to), m_what(std::move(t_what))
+        {
+        }
+
+        bad_boxed_cast(Type_Info t_from, const std::type_info &t_to)
+          : from(t_from), to(&t_to), m_what("Cannot perform boxed_cast: " + t_from.name() + " to: " + t_to.name())
+        {
+        }
+
+        explicit bad_boxed_cast(std::string t_what) noexcept
+          : m_what(std::move(t_what))
+        {
+        }
+
+        bad_boxed_cast(const bad_boxed_cast &) = default;
+        ~bad_boxed_cast() noexcept override = default;
+
+        /// \brief Description of what error occurred
+        const char * what() const noexcept override
+        {
+          return m_what.c_str();
+        }
+
+        Type_Info from; ///< Type_Info contained in the Boxed_Value
+        const std::type_info *to = nullptr; ///< std::type_info of the desired (but failed) result type
+
+      private:
+        std::string m_what;
+    };
+  }
+}
+
+
+
+#endif
+

+ 85 - 0
chaiscript/dispatchkit/bind_first.hpp

@@ -0,0 +1,85 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_BIND_FIRST_HPP_
+#define CHAISCRIPT_BIND_FIRST_HPP_
+
+#include <functional>
+
+namespace chaiscript
+{
+  namespace detail
+  {
+
+    template<typename T>
+      T* get_pointer(T *t)
+      {
+        return t;
+      }
+
+    template<typename T>
+      T* get_pointer(const std::reference_wrapper<T> &t)
+      {
+        return &t.get();
+      }
+
+    template<typename O, typename Ret, typename P1, typename ... Param>
+      auto bind_first(Ret (*f)(P1, Param...), O&& o)
+      {
+        return [f, o](Param...param) -> Ret {
+          return f(std::forward<O>(o), std::forward<Param>(param)...);
+        };
+      }
+
+    template<typename O, typename Ret, typename Class, typename ... Param>
+      auto bind_first(Ret (Class::*f)(Param...), O&& o)
+      {
+        return [f, o](Param...param) -> Ret {
+          return (get_pointer(o)->*f)(std::forward<Param>(param)...);
+        };
+      }
+
+    template<typename O, typename Ret, typename Class, typename ... Param>
+      auto bind_first(Ret (Class::*f)(Param...) const, O&& o)
+      {
+        return [f, o](Param...param) -> Ret {
+          return (get_pointer(o)->*f)(std::forward<Param>(param)...);
+        };
+
+      }
+
+    template<typename O, typename Ret, typename P1, typename ... Param>
+      auto bind_first(const std::function<Ret (P1, Param...)> &f, O&& o)
+      {
+        return [f, o](Param...param) -> Ret {
+          return f(o, std::forward<Param>(param)...);
+        };
+      }
+
+    template<typename F, typename O, typename Ret, typename Class, typename P1, typename ... Param>
+      auto bind_first(const F &fo, O&& o, Ret (Class::*f)(P1, Param...) const)
+      {
+        return [fo, o, f](Param ...param) -> Ret {
+          return (fo.*f)(o, std::forward<Param>(param)...);
+        };
+
+      }
+
+    template<typename F, typename O>
+      auto bind_first(const F &f, O&& o)
+      {
+        return bind_first(f, std::forward<O>(o), &F::operator());
+      }
+
+  }
+}
+
+
+#endif

+ 589 - 0
chaiscript/dispatchkit/bootstrap.hpp

@@ -0,0 +1,589 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_BOOTSTRAP_HPP_
+#define CHAISCRIPT_BOOTSTRAP_HPP_
+
+#include "../utility/utility.hpp"
+#include "register_function.hpp"
+
+namespace chaiscript 
+{
+  /// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types
+  namespace bootstrap
+  {
+    template<typename T, typename = typename std::enable_if<std::is_array<T>::value>::type >
+      void array(const std::string &type, Module& m)
+      {
+        typedef typename std::remove_extent<T>::type ReturnType;
+        const auto extent = std::extent<T>::value;
+        m.add(user_type<T>(), type);
+        m.add(fun(
+              [extent](T& t, size_t index)->ReturnType &{
+                if (extent > 0 && index >= extent) {
+                  throw std::range_error("Array index out of range. Received: " + std::to_string(index)  + " expected < " + std::to_string(extent));
+                } else {
+                  return t[index];
+                }
+              }
+              ), "[]"
+            );
+
+        m.add(fun(
+              [extent](const T &t, size_t index)->const ReturnType &{
+                if (extent > 0 && index >= extent) {
+                  throw std::range_error("Array index out of range. Received: " + std::to_string(index)  + " expected < " + std::to_string(extent));
+                } else {
+                  return t[index];
+                }
+              }
+              ), "[]"
+            );
+
+        m.add(fun(
+              [extent](const T &) {
+                return extent;
+              }), "size");
+      }
+
+    /// \brief Adds a copy constructor for the given type to the given Model
+    /// \param[in] type The name of the type. The copy constructor will be named "type".
+    /// \param[in,out] m The Module to add the copy constructor to
+    /// \tparam T The type to add a copy constructor for
+    /// \returns The passed in Module
+    template<typename T>
+    void copy_constructor(const std::string &type, Module& m)
+    {
+      m.add(constructor<T (const T &)>(), type);
+    }
+
+    /// \brief Add all comparison operators for the templated type. Used during bootstrap, also available to users.
+    /// \tparam T Type to create comparison operators for
+    /// \param[in,out] m module to add comparison operators to
+    /// \returns the passed in Module.
+    template<typename T>
+    void opers_comparison(Module& m)
+    {
+      operators::equal<T>(m);
+      operators::greater_than<T>(m);
+      operators::greater_than_equal<T>(m);
+      operators::less_than<T>(m);
+      operators::less_than_equal<T>(m);
+      operators::not_equal<T>(m);
+    }
+
+
+
+    /// \brief Adds default and copy constructors for the given type
+    /// \param[in] type The name of the type to add the constructors for.
+    /// \param[in,out] m The Module to add the basic constructors to
+    /// \tparam T Type to generate basic constructors for
+    /// \returns The passed in Module
+    /// \sa copy_constructor
+    /// \sa constructor
+    template<typename T>
+    void basic_constructors(const std::string &type, Module& m)
+    {
+      m.add(constructor<T ()>(), type);
+      copy_constructor<T>(type, m);
+    }
+
+    /// \brief Adds a constructor for a POD type 
+    /// \tparam T The type to add the constructor for
+    /// \param[in] type The name of the type
+    /// \param[in,out] m The Module to add the constructor to
+    template<typename T>
+    void construct_pod(const std::string &type, Module& m)
+    {
+      m.add(fun([](const Boxed_Number &bn){ return bn.get_as<T>(); }), type);
+    }
+
+
+    /// Internal function for converting from a string to a value
+    /// uses ostream operator >> to perform the conversion
+    template<typename Input>
+    auto parse_string(const std::string &i)
+      -> typename std::enable_if<
+             !std::is_same<Input, wchar_t>::value
+             && !std::is_same<Input, char16_t>::value
+             && !std::is_same<Input, char32_t>::value,
+      Input>::type
+    {
+      std::stringstream ss(i);
+      Input t;
+      ss >> t;
+      return t;
+    }
+
+    template<typename Input>
+    auto parse_string(const std::string &) 
+      -> typename std::enable_if<
+             std::is_same<Input, wchar_t>::value
+             || std::is_same<Input, char16_t>::value
+             || std::is_same<Input, char32_t>::value,
+      Input>::type
+    {
+      throw std::runtime_error("Parsing of wide characters is not yet supported");
+    }
+
+
+    /// Add all common functions for a POD type. All operators, and
+    /// common conversions
+    template<typename T>
+    void bootstrap_pod_type(const std::string &name, Module& m)
+    {
+      m.add(user_type<T>(), name);
+      m.add(constructor<T()>(), name);
+      construct_pod<T>(name, m);
+
+      m.add(fun(&parse_string<T>), "to_" + name);
+    }
+
+
+    /// "clone" function for a shared_ptr type. This is used in the case
+    /// where you do not want to make a deep copy of an object during cloning
+    /// but want to instead maintain the shared_ptr. It is needed internally
+    /// for handling of Proxy_Function object (that is,
+    /// function variables.
+    template<typename Type>
+    auto shared_ptr_clone(const std::shared_ptr<Type> &p)
+    {
+      return p;
+    }
+
+    /// Specific version of shared_ptr_clone just for Proxy_Functions
+    template<typename Type>
+    std::shared_ptr<typename std::remove_const<Type>::type> shared_ptr_unconst_clone(const std::shared_ptr<typename std::add_const<Type>::type> &p)
+    {
+      return std::const_pointer_cast<typename std::remove_const<Type>::type>(p);
+    }
+
+
+
+    /// Assignment function for shared_ptr objects, does not perform a copy of the
+    /// object pointed to, instead maintains the shared_ptr concept.
+    /// Similar to shared_ptr_clone. Used for Proxy_Function.
+    template<typename Type>
+    Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr<Type> &rhs)
+    {
+      if (lhs.is_undef() 
+          || (!lhs.get_type_info().is_const() && lhs.get_type_info().bare_equal(chaiscript::detail::Get_Type_Info<Type>::get())))
+      {
+        lhs.assign(Boxed_Value(rhs));
+        return lhs;
+      } else {
+        throw exception::bad_boxed_cast("type mismatch in pointer assignment");
+      }
+    }
+
+    /// Class consisting of only static functions. All default bootstrapping occurs
+    /// from this class.
+    class Bootstrap
+    {
+    private:
+      /// Function allowing for assignment of an unknown type to any other value
+      static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs)
+      {
+        if (lhs.is_undef())
+        {
+          return (lhs.assign(rhs));
+        } else {
+          throw exception::bad_boxed_cast("boxed_value has a set type already");
+        }
+      }
+
+      static void print(const std::string &s)
+      {
+        fwrite(s.c_str(), 1, s.size(), stdout);
+      }
+
+      static void println(const std::string &s)
+      {
+        puts(s.c_str());
+      }
+
+
+      /// Add all arithmetic operators for PODs
+      static void opers_arithmetic_pod(Module& m)
+      {
+        m.add(fun(&Boxed_Number::equals), "==");
+        m.add(fun(&Boxed_Number::less_than), "<");
+        m.add(fun(&Boxed_Number::greater_than), ">");
+        m.add(fun(&Boxed_Number::greater_than_equal), ">=");
+        m.add(fun(&Boxed_Number::less_than_equal), "<=");
+        m.add(fun(&Boxed_Number::not_equal), "!=");
+
+        m.add(fun(&Boxed_Number::pre_decrement), "--");
+        m.add(fun(&Boxed_Number::pre_increment), "++");
+        m.add(fun(&Boxed_Number::sum), "+");
+        m.add(fun(&Boxed_Number::unary_plus), "+");
+        m.add(fun(&Boxed_Number::unary_minus), "-");
+        m.add(fun(&Boxed_Number::difference), "-");
+        m.add(fun(&Boxed_Number::assign_bitwise_and), "&=");
+        m.add(fun(&Boxed_Number::assign), "=");
+        m.add(fun(&Boxed_Number::assign_bitwise_or), "|=");
+        m.add(fun(&Boxed_Number::assign_bitwise_xor), "^=");
+        m.add(fun(&Boxed_Number::assign_remainder), "%=");
+        m.add(fun(&Boxed_Number::assign_shift_left), "<<=");
+        m.add(fun(&Boxed_Number::assign_shift_right), ">>=");
+        m.add(fun(&Boxed_Number::bitwise_and), "&");
+        m.add(fun(&Boxed_Number::bitwise_complement), "~");
+        m.add(fun(&Boxed_Number::bitwise_xor), "^");
+        m.add(fun(&Boxed_Number::bitwise_or), "|");
+        m.add(fun(&Boxed_Number::assign_product), "*=");
+        m.add(fun(&Boxed_Number::assign_quotient), "/=");
+        m.add(fun(&Boxed_Number::assign_sum), "+=");
+        m.add(fun(&Boxed_Number::assign_difference), "-=");
+        m.add(fun(&Boxed_Number::quotient), "/");
+        m.add(fun(&Boxed_Number::shift_left), "<<");
+        m.add(fun(&Boxed_Number::product), "*");
+        m.add(fun(&Boxed_Number::remainder), "%");
+        m.add(fun(&Boxed_Number::shift_right), ">>");
+     }
+
+      /// Create a bound function object. The first param is the function to bind
+      /// the remaining parameters are the args to bind into the result
+      static Boxed_Value bind_function(const std::vector<Boxed_Value> &params)
+      {
+        if (params.empty()) {
+          throw exception::arity_error(0, 1);
+        }
+
+        Const_Proxy_Function f = boxed_cast<Const_Proxy_Function>(params[0]);
+
+        if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1)
+        {
+          throw exception::arity_error(static_cast<int>(params.size()), f->get_arity());
+        }
+
+        return Boxed_Value(Const_Proxy_Function(std::make_shared<dispatch::Bound_Function>(std::move(f),
+          std::vector<Boxed_Value>(params.begin() + 1, params.end()))));
+      }
+
+
+      static bool has_guard(const Const_Proxy_Function &t_pf)
+      {
+        auto pf = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_pf);
+        return pf && pf->get_guard();
+      }
+
+      static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf)
+      {
+        const auto pf = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_pf);
+        if (pf && pf->get_guard())
+        {
+          return pf->get_guard();
+        } else {
+          throw std::runtime_error("Function does not have a guard");
+        }
+      }
+
+      template<typename FunctionType>
+        static std::vector<Boxed_Value> do_return_boxed_value_vector(FunctionType f,
+            const dispatch::Proxy_Function_Base *b)
+        {
+          auto v = (b->*f)();
+ 
+          std::vector<Boxed_Value> vbv;
+
+          for (const auto &o: v)
+          {
+            vbv.push_back(const_var(o));
+          }
+
+          return vbv;
+        }
+
+
+      static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
+      {
+        const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
+        return bool(pf);
+      }
+
+      static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf)
+      {
+        const auto pf = std::dynamic_pointer_cast<const chaiscript::dispatch::Dynamic_Proxy_Function>(t_pf);
+        if (pf)
+        {
+          return pf->get_parse_tree();
+        } else {
+          throw std::runtime_error("Function does not have a parse tree");
+        }
+      }
+
+      template<typename Function>
+      static auto return_boxed_value_vector(const Function &f)
+      {
+        return [f](const dispatch::Proxy_Function_Base *b) {
+          return do_return_boxed_value_vector(f, b);
+        };
+      }
+
+
+    public:
+      /// \brief perform all common bootstrap functions for std::string, void and POD types
+      /// \param[in,out] m Module to add bootstrapped functions to
+      /// \returns passed in Module
+      static void bootstrap(Module& m)
+      {
+        m.add(user_type<void>(), "void");
+        m.add(user_type<bool>(), "bool");
+        m.add(user_type<Boxed_Value>(), "Object");
+        m.add(user_type<Boxed_Number>(), "Number");
+        m.add(user_type<Proxy_Function>(), "Function");
+        m.add(user_type<dispatch::Assignable_Proxy_Function>(), "Assignable_Function");
+        m.add(user_type<std::exception>(), "exception");
+
+        m.add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity");
+        m.add(fun(&dispatch::Proxy_Function_Base::operator==), "==");
+
+
+        m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types");
+        m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions");
+
+        m.add(fun([](const std::exception &e){ return std::string(e.what()); }), "what");
+
+        m.add(user_type<std::out_of_range>(), "out_of_range");
+        m.add(user_type<std::logic_error>(), "logic_error");
+        m.add(chaiscript::base_class<std::exception, std::logic_error>());
+        m.add(chaiscript::base_class<std::logic_error, std::out_of_range>());
+        m.add(chaiscript::base_class<std::exception, std::out_of_range>());
+
+        m.add(user_type<std::runtime_error>(), "runtime_error");
+        m.add(chaiscript::base_class<std::exception, std::runtime_error>());
+
+        m.add(constructor<std::runtime_error (const std::string &)>(), "runtime_error");
+
+        m.add(user_type<dispatch::Dynamic_Object>(), "Dynamic_Object");
+        m.add(constructor<dispatch::Dynamic_Object (const std::string &)>(), "Dynamic_Object");
+        m.add(constructor<dispatch::Dynamic_Object ()>(), "Dynamic_Object");
+        m.add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name");
+        m.add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs");
+        m.add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit");
+        m.add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit");
+        m.add(fun(&dispatch::Dynamic_Object::has_attr), "has_attr");
+
+        m.add(fun(static_cast<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "get_attr");
+        m.add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), "get_attr");
+
+        m.add(fun(static_cast<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::method_missing)), "method_missing");
+        m.add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::method_missing)), "method_missing");
+
+        m.add(fun(static_cast<Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &)>(&dispatch::Dynamic_Object::get_attr)), "[]");
+        m.add(fun(static_cast<const Boxed_Value & (dispatch::Dynamic_Object::*)(const std::string &) const>(&dispatch::Dynamic_Object::get_attr)), "[]");
+
+        m.eval(R"chaiscript(
+          def Dynamic_Object::clone() { 
+            auto &new_o = Dynamic_Object(this.get_type_name()); 
+            for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); 
+            new_o; 
+          }
+
+          def `=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name()
+          {
+            for_each(rhs.get_attrs(), fun[lhs](x) { lhs.get_attr(x.first) = clone(x.second); } ); 
+          }
+
+          def `!=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name()
+          {
+            var rhs_attrs := rhs.get_attrs();
+            var lhs_attrs := lhs.get_attrs();
+
+            if (rhs_attrs.size() != lhs_attrs.size()) {
+              true;
+            } else {
+              return any_of(rhs_attrs, fun[lhs](x) { !lhs.has_attr(x.first) || lhs.get_attr(x.first) != x.second; } );
+            }
+          }
+
+          def `==`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name()
+          {
+            var rhs_attrs := rhs.get_attrs();
+            var lhs_attrs := lhs.get_attrs();
+
+            if (rhs_attrs.size() != lhs_attrs.size()) {
+              false;
+            } else {
+              return all_of(rhs_attrs, fun[lhs](x) { lhs.has_attr(x.first) && lhs.get_attr(x.first) == x.second; } );
+            }
+          }
+        )chaiscript");
+
+        m.add(fun(&has_guard), "has_guard");
+        m.add(fun(&get_guard), "get_guard");
+
+        m.add(fun(&Boxed_Value::is_undef), "is_var_undef");
+        m.add(fun(&Boxed_Value::is_null), "is_var_null");
+        m.add(fun(&Boxed_Value::is_const), "is_var_const");
+        m.add(fun(&Boxed_Value::is_ref), "is_var_reference");
+        m.add(fun(&Boxed_Value::is_pointer), "is_var_pointer");
+        m.add(fun(&Boxed_Value::is_return_value), "is_var_return_value");
+        m.add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value");
+        m.add(fun(&Boxed_Value::is_type), "is_type");
+        m.add(fun(&Boxed_Value::get_attr), "get_var_attr");
+        m.add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs");
+        m.add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs");
+
+        m.add(fun(&Boxed_Value::get_type_info), "get_type_info");
+        m.add(user_type<Type_Info>(), "Type_Info");
+        m.add(constructor<Type_Info (const Type_Info &)>(), "Type_Info");
+
+
+        operators::equal<Type_Info>(m);
+
+        m.add(fun(&Type_Info::is_const), "is_type_const");
+        m.add(fun(&Type_Info::is_reference), "is_type_reference");
+        m.add(fun(&Type_Info::is_void), "is_type_void");
+        m.add(fun(&Type_Info::is_undef), "is_type_undef");
+        m.add(fun(&Type_Info::is_pointer), "is_type_pointer");
+        m.add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic");
+        m.add(fun(&Type_Info::name), "cpp_name");
+        m.add(fun(&Type_Info::bare_name), "cpp_bare_name");
+        m.add(fun(&Type_Info::bare_equal), "bare_equal");
+
+
+        basic_constructors<bool>("bool", m);
+        operators::assign<bool>(m);
+        operators::equal<bool>(m);
+        operators::not_equal<bool>(m);
+
+        m.add(fun([](const std::string &s) { return s; }), "to_string");
+        m.add(fun([](const bool b) { return std::string(b?"true":"false"); }), "to_string");
+        m.add(fun(&unknown_assign), "=");
+        m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw");
+
+        m.add(fun([](const char c) { return std::string(1, c); }), "to_string");
+        m.add(fun(&Boxed_Number::to_string), "to_string");
+
+        
+        bootstrap_pod_type<double>("double", m);
+        bootstrap_pod_type<long double>("long_double", m);
+        bootstrap_pod_type<float>("float", m);
+        bootstrap_pod_type<int>("int", m);
+        bootstrap_pod_type<long>("long", m);
+        bootstrap_pod_type<unsigned int>("unsigned_int", m);
+        bootstrap_pod_type<unsigned long>("unsigned_long", m);
+        bootstrap_pod_type<long long>("long_long", m);
+        bootstrap_pod_type<unsigned long long>("unsigned_long_long", m);
+        bootstrap_pod_type<size_t>("size_t", m);
+        bootstrap_pod_type<char>("char", m);
+        bootstrap_pod_type<wchar_t>("wchar_t", m);
+        bootstrap_pod_type<char16_t>("char16_t", m);
+        bootstrap_pod_type<char32_t>("char32_t", m);
+        bootstrap_pod_type<std::int8_t>("int8_t", m);
+        bootstrap_pod_type<std::int16_t>("int16_t", m);
+        bootstrap_pod_type<std::int32_t>("int32_t", m);
+        bootstrap_pod_type<std::int64_t>("int64_t", m);
+        bootstrap_pod_type<std::uint8_t>("uint8_t", m);
+        bootstrap_pod_type<std::uint16_t>("uint16_t", m);
+        bootstrap_pod_type<std::uint32_t>("uint32_t", m);
+        bootstrap_pod_type<std::uint64_t>("uint64_t", m);
+
+
+        operators::logical_compliment<bool>(m);
+
+        opers_arithmetic_pod(m);
+
+        
+        m.add(fun(&Build_Info::version_major), "version_major");
+        m.add(fun(&Build_Info::version_minor), "version_minor");
+        m.add(fun(&Build_Info::version_patch), "version_patch");
+        m.add(fun(&Build_Info::version), "version");
+        m.add(fun(&Build_Info::compiler_version), "compiler_version");
+        m.add(fun(&Build_Info::compiler_name), "compiler_name");
+        m.add(fun(&Build_Info::compiler_id), "compiler_id");
+        m.add(fun(&Build_Info::debug_build), "debug_build");
+
+
+        m.add(fun(&print), "print_string");
+        m.add(fun(&println), "println_string");
+
+        m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind");
+
+        m.add(fun(&shared_ptr_unconst_clone<dispatch::Proxy_Function_Base>), "clone");
+        m.add(fun(&ptr_assign<std::remove_const<dispatch::Proxy_Function_Base>::type>), "=");
+        m.add(fun(&ptr_assign<std::add_const<dispatch::Proxy_Function_Base>::type>), "=");
+        m.add(chaiscript::base_class<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function>());
+        m.add(fun(
+                  [](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr<const dispatch::Proxy_Function_Base> &t_rhs) {
+                    t_lhs.assign(t_rhs);
+                  }
+                ), "="
+              );
+
+        m.add(fun(&Boxed_Value::type_match), "type_match");
+
+
+        m.add(chaiscript::fun(&has_parse_tree), "has_parse_tree");
+        m.add(chaiscript::fun(&get_parse_tree), "get_parse_tree");
+
+        m.add(chaiscript::base_class<std::runtime_error, chaiscript::exception::eval_error>());
+        m.add(chaiscript::base_class<std::exception, chaiscript::exception::eval_error>());
+
+        m.add(chaiscript::user_type<chaiscript::exception::arithmetic_error>(), "arithmetic_error");
+        m.add(chaiscript::base_class<std::runtime_error, chaiscript::exception::arithmetic_error>());
+        m.add(chaiscript::base_class<std::exception, chaiscript::exception::arithmetic_error>());
+
+
+//        chaiscript::bootstrap::standard_library::vector_type<std::vector<std::shared_ptr<chaiscript::AST_Node> > >("AST_NodeVector", m);
+
+
+        chaiscript::utility::add_class<chaiscript::exception::eval_error>(m,
+            "eval_error",
+            { },
+            { {fun(&chaiscript::exception::eval_error::reason), "reason"},
+              {fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"},
+              {fun([](const chaiscript::exception::eval_error &t_eval_error) { 
+                  std::vector<Boxed_Value> retval;
+                  std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(),
+                                 std::back_inserter(retval),
+                                 &chaiscript::var<const chaiscript::AST_Node_Trace &>);
+                  return retval;
+                }), "call_stack"} }
+            );
+
+
+        chaiscript::utility::add_class<chaiscript::File_Position>(m,
+            "File_Position",
+            { constructor<File_Position()>(),
+              constructor<File_Position(int, int)>() },
+            { {fun(&File_Position::line), "line"},
+              {fun(&File_Position::column), "column"} }
+            );
+
+
+        chaiscript::utility::add_class<AST_Node>(m,
+            "AST_Node",
+            {  },
+            { {fun(&AST_Node::text), "text"},
+              {fun(&AST_Node::identifier), "identifier"},
+              {fun(&AST_Node::filename), "filename"},
+              {fun(&AST_Node::start), "start"},
+              {fun(&AST_Node::end), "end"},
+              {fun(&AST_Node::to_string), "to_string"},
+              {fun([](const chaiscript::AST_Node &t_node) -> std::vector<Boxed_Value> { 
+                std::vector<Boxed_Value> retval;
+                const auto children = t_node.get_children();
+                std::transform(children.begin(), children.end(),
+                               std::back_inserter(retval),
+                               &chaiscript::var<const std::reference_wrapper<chaiscript::AST_Node> &>);
+                return retval;
+              }), "children"}
+            }
+            );
+
+      }
+    };
+  }
+}
+
+#endif
+

+ 753 - 0
chaiscript/dispatchkit/bootstrap_stl.hpp

@@ -0,0 +1,753 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+/// \file
+/// This file contains utility functions for registration of STL container
+/// classes. The methodology used is based on the SGI STL concepts.
+/// http://www.sgi.com/tech/stl/table_of_contents.html
+
+
+#ifndef CHAISCRIPT_BOOTSTRAP_STL_HPP_
+#define CHAISCRIPT_BOOTSTRAP_STL_HPP_
+
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <typeinfo>
+#include <vector>
+
+#include "bootstrap.hpp"
+#include "boxed_value.hpp"
+#include "dispatchkit.hpp"
+#include "operators.hpp"
+#include "proxy_constructors.hpp"
+#include "register_function.hpp"
+#include "type_info.hpp"
+
+namespace chaiscript 
+{
+  namespace bootstrap
+  {
+    namespace standard_library
+    {
+
+      /// Bidir_Range, based on the D concept of ranges.
+      /// \todo Update the Range code to base its capabilities on
+      ///       the user_typetraits of the iterator passed in
+      template<typename Container, typename IterType>
+        struct Bidir_Range
+        {
+          typedef Container container_type;
+
+          Bidir_Range(Container &c)
+            : m_begin(c.begin()), m_end(c.end())
+          {
+          }
+
+          bool empty() const
+          {
+            return m_begin == m_end;
+          }
+
+          void pop_front()
+          {
+            if (empty())
+            {
+              throw std::range_error("Range empty");
+            }
+            ++m_begin;
+          }
+
+          void pop_back()
+          {
+            if (empty())
+            {
+              throw std::range_error("Range empty");
+            }
+            --m_end;
+          }
+
+          decltype(auto) front() const
+          {
+            if (empty())
+            {
+              throw std::range_error("Range empty");
+            }
+            return (*m_begin);
+          }
+
+          decltype(auto) back() const
+          {
+            if (empty())
+            {
+              throw std::range_error("Range empty");
+            }
+            auto pos = m_end;
+            --pos;
+            return (*(pos));
+          }
+
+          IterType m_begin;
+          IterType m_end;
+        };
+
+      namespace detail {
+
+        template<typename T>
+        size_t count(const T &t_target, const typename T::key_type &t_key)
+        {
+          return t_target.count(t_key);
+        }
+
+        template<typename T>
+          void insert(T &t_target, const T &t_other)
+          {
+            t_target.insert(t_other.begin(), t_other.end());
+          }
+
+        template<typename T>
+          void insert_ref(T &t_target, const typename T::value_type &t_val)
+          {
+            t_target.insert(t_val);
+          }
+
+
+
+        /// Add Bidir_Range support for the given ContainerType
+        template<typename Bidir_Type>
+          void input_range_type_impl(const std::string &type, Module& m)
+          {
+            m.add(user_type<Bidir_Type>(), type + "_Range");
+
+            copy_constructor<Bidir_Type>(type + "_Range", m);
+
+            m.add(constructor<Bidir_Type (typename Bidir_Type::container_type &)>(), "range_internal");
+
+            m.add(fun(&Bidir_Type::empty), "empty");
+            m.add(fun(&Bidir_Type::pop_front), "pop_front");
+            m.add(fun(&Bidir_Type::front), "front");
+            m.add(fun(&Bidir_Type::pop_back), "pop_back");
+            m.add(fun(&Bidir_Type::back), "back");
+          }
+
+
+        /// Algorithm for inserting at a specific position into a container
+        template<typename Type>
+          void insert_at(Type &container, int pos, const typename Type::value_type &v)
+          {
+            auto itr = container.begin();
+            auto end = container.end();
+
+            if (pos < 0 || std::distance(itr, end) < pos)
+            {
+              throw std::range_error("Cannot insert past end of range");
+            }
+
+            std::advance(itr, pos);
+            container.insert(itr, v);
+          }
+
+
+        /// Algorithm for erasing a specific position from a container
+        template<typename Type>
+          void erase_at(Type &container, int pos)
+          {
+            auto itr = container.begin();
+            auto end = container.end();
+
+            if (pos < 0 || std::distance(itr, end) < (pos-1))
+            {
+              throw std::range_error("Cannot erase past end of range");
+            }
+
+            std::advance(itr, pos);
+            container.erase(itr);
+          }
+      }
+
+      template<typename ContainerType>
+        void input_range_type(const std::string &type, Module& m)
+        {
+          detail::input_range_type_impl<Bidir_Range<ContainerType, typename ContainerType::iterator> >(type,m);
+          detail::input_range_type_impl<Bidir_Range<const ContainerType, typename ContainerType::const_iterator> >("Const_" + type,m);
+        }
+      template<typename ContainerType>
+        ModulePtr input_range_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          input_range_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+      /// Add random_access_container concept to the given ContainerType
+      /// http://www.sgi.com/tech/stl/RandomAccessContainer.html
+      template<typename ContainerType>
+        void random_access_container_type(const std::string &/*type*/, Module& m)
+        {
+          //In the interest of runtime safety for the m, we prefer the at() method for [] access,
+          //to throw an exception in an out of bounds condition.
+          m.add(
+              fun(
+                [](ContainerType &c, int index) -> typename ContainerType::reference {
+                  /// \todo we are prefering to keep the key as 'int' to avoid runtime conversions
+                  /// during dispatch. reevaluate
+                  return c.at(static_cast<typename ContainerType::size_type>(index));
+                }), "[]");
+
+          m.add(
+              fun(
+                [](const ContainerType &c, int index) -> typename ContainerType::const_reference {
+                  /// \todo we are prefering to keep the key as 'int' to avoid runtime conversions
+                  /// during dispatch. reevaluate
+                  return c.at(static_cast<typename ContainerType::size_type>(index));
+                }), "[]");
+        }
+      template<typename ContainerType>
+        ModulePtr random_access_container_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          random_access_container_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+
+      /// Add assignable concept to the given ContainerType
+      /// http://www.sgi.com/tech/stl/Assignable.html
+      template<typename ContainerType>
+        void assignable_type(const std::string &type, Module& m)
+        {
+          copy_constructor<ContainerType>(type, m);
+          operators::assign<ContainerType>(m);
+        }
+      template<typename ContainerType>
+        ModulePtr assignable_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          assignable_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+      /// Add container resize concept to the given ContainerType
+      /// http://www.cplusplus.com/reference/stl/
+      template<typename ContainerType>
+        void resizable_type(const std::string &/*type*/, Module& m)
+        {
+          m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type& val) { return a->resize(n, val); } ), "resize");
+          m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); } ), "resize");
+        }
+      template<typename ContainerType>
+        ModulePtr resizable_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          resizable_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+      /// Add container reserve concept to the given ContainerType
+      /// http://www.cplusplus.com/reference/stl/
+      template<typename ContainerType>
+        void reservable_type(const std::string &/*type*/, Module& m)
+        {
+          m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->reserve(n); } ), "reserve");
+          m.add(fun([](const ContainerType *a) { return a->capacity(); } ), "capacity");
+        }
+      template<typename ContainerType>
+        ModulePtr reservable_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          reservable_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+      /// Add container concept to the given ContainerType
+      /// http://www.sgi.com/tech/stl/Container.html
+      template<typename ContainerType>
+        void container_type(const std::string &/*type*/, Module& m)
+        {
+          m.add(fun([](const ContainerType *a) { return a->size(); } ), "size");
+          m.add(fun([](const ContainerType *a) { return a->empty(); } ), "empty");
+          m.add(fun([](ContainerType *a) { a->clear(); } ), "clear");
+        }
+      template <typename ContainerType>
+        ModulePtr container_type(const std::string& type)
+      {
+        auto m = std::make_shared<Module>();
+        container_type<ContainerType>(type, *m);
+        return m;
+      }
+
+
+      /// Add default constructable concept to the given Type
+      /// http://www.sgi.com/tech/stl/DefaultConstructible.html
+      template<typename Type>
+        void default_constructible_type(const std::string &type, Module& m)
+        {
+          m.add(constructor<Type ()>(), type);
+        }
+      template <typename Type>
+        ModulePtr default_constructible_type(const std::string& type)
+        {
+          auto m = std::make_shared<Module>();
+          default_constructible_type<Type>(type, *m);
+          return m;
+        }
+
+
+
+      /// Add sequence concept to the given ContainerType
+      /// http://www.sgi.com/tech/stl/Sequence.html
+      template<typename ContainerType>
+        void sequence_type(const std::string &/*type*/, Module& m)
+        {
+          m.add(fun(&detail::insert_at<ContainerType>),
+              []()->std::string{
+                if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
+                  return "insert_ref_at";
+                } else {
+                  return "insert_at";
+                }
+              }());
+
+          m.add(fun(&detail::erase_at<ContainerType>), "erase_at");
+        }
+      template <typename ContainerType>
+        ModulePtr sequence_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          sequence_type<ContainerType>(type, *m);
+          return m;
+        }
+
+      /// Add back insertion sequence concept to the given ContainerType
+      /// http://www.sgi.com/tech/stl/BackInsertionSequence.html
+      template<typename ContainerType>
+        void back_insertion_sequence_type(const std::string &type, Module& m)
+        {
+          m.add(fun([](ContainerType &container)->decltype(auto){ 
+                      if (container.empty()) {
+                        throw std::range_error("Container empty");
+                      } else {
+                        return (container.back());
+                      }
+                    }
+                  )
+                , "back");
+          m.add(fun([](const ContainerType &container)->decltype(auto){ 
+                      if (container.empty()) {
+                        throw std::range_error("Container empty");
+                      } else {
+                        return (container.back());
+                      }
+                    }
+                  )
+                , "back");
+
+
+          typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &);
+          m.add(fun(static_cast<push_back>(&ContainerType::push_back)),
+              [&]()->std::string{
+              if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
+                m.eval(
+                    "# Pushes the second value onto the container while making a clone of the value\n"
+                    "def push_back(" + type + " container, x)\n"
+                    "{ \n"
+                    "  if (x.is_var_return_value()) {\n"
+                    "    x.reset_var_return_value() \n"
+                    "    container.push_back_ref(x) \n"
+                    "  } else { \n"
+                    "    container.push_back_ref(clone(x)); \n"
+                    "  }\n"
+                    "} \n"
+                    );
+
+                  return "push_back_ref";
+                } else {
+                  return "push_back";
+                }
+              }());
+
+          m.add(fun(&ContainerType::pop_back), "pop_back");
+        }
+      template<typename ContainerType>
+        ModulePtr back_insertion_sequence_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          back_insertion_sequence_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+
+      /// Front insertion sequence
+      /// http://www.sgi.com/tech/stl/FrontInsertionSequence.html
+      template<typename ContainerType>
+        void front_insertion_sequence_type(const std::string &type, Module& m)
+        {
+          typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference);
+          typedef void (ContainerType::*pop_ptr)();
+
+          m.add(fun([](ContainerType &container)->decltype(auto){ 
+                      if (container.empty()) {
+                        throw std::range_error("Container empty");
+                      } else {
+                        return (container.front());
+                      }
+                    }
+                  )
+                , "front");
+
+          m.add(fun([](const ContainerType &container)->decltype(auto){ 
+                      if (container.empty()) {
+                        throw std::range_error("Container empty");
+                      } else {
+                        return (container.front());
+                      }
+                    }
+                  )
+                , "front");
+
+
+          m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)),
+              [&]()->std::string{
+                if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
+                  m.eval(
+                      "# Pushes the second value onto the front of container while making a clone of the value\n"
+                      "def push_front(" + type + " container, x)\n"
+                      "{ \n"
+                      "  if (x.is_var_return_value()) {\n"
+                      "    x.reset_var_return_value() \n"
+                      "    container.push_front_ref(x) \n"
+                      "  } else { \n"
+                      "    container.push_front_ref(clone(x)); \n"
+                      "  }\n"
+                      "} \n"
+                      );
+                  return "push_front_ref";
+                } else {
+                  return "push_front";
+                }
+              }());
+
+          m.add(fun(static_cast<pop_ptr>(&ContainerType::pop_front)), "pop_front");
+        }
+      template<typename ContainerType>
+        ModulePtr front_insertion_sequence_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          front_insertion_sequence_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+      /// bootstrap a given PairType
+      /// http://www.sgi.com/tech/stl/pair.html
+      template<typename PairType>
+        void pair_type(const std::string &type, Module& m)
+        {
+          m.add(user_type<PairType>(), type);
+
+          m.add(fun(&PairType::first), "first");
+          m.add(fun(&PairType::second), "second");
+
+          basic_constructors<PairType>(type, m);
+          m.add(constructor<PairType (const typename PairType::first_type &, const typename PairType::second_type &)>(), type);
+        }
+      template<typename PairType>
+        ModulePtr pair_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          pair_type<PairType>(type, *m);
+          return m;
+        }
+
+
+
+      /// Add pair associative container concept to the given ContainerType
+      /// http://www.sgi.com/tech/stl/PairAssociativeContainer.html
+
+      template<typename ContainerType>
+        void pair_associative_container_type(const std::string &type, Module& m)
+        {
+          pair_type<typename ContainerType::value_type>(type + "_Pair", m);
+        }
+      template<typename ContainerType>
+        ModulePtr pair_associative_container_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          pair_associative_container_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+      /// Add unique associative container concept to the given ContainerType
+      /// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html
+      template<typename ContainerType>
+        void unique_associative_container_type(const std::string &/*type*/, Module& m)
+        {
+          m.add(fun(detail::count<ContainerType>), "count");
+
+          typedef size_t (ContainerType::*erase_ptr)(const typename ContainerType::key_type &);
+
+          m.add(fun(static_cast<erase_ptr>(&ContainerType::erase)), "erase");
+
+          m.add(fun(&detail::insert<ContainerType>), "insert");
+
+          m.add(fun(&detail::insert_ref<ContainerType>),
+              []()->std::string{
+                if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) {
+                  return "insert_ref";
+                } else {
+                  return "insert";
+                }
+              }());
+        }
+      template<typename ContainerType>
+        ModulePtr unique_associative_container_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          unique_associative_container_type<ContainerType>(type, *m);
+          return m;
+        }
+
+
+      /// Add a MapType container
+      /// http://www.sgi.com/tech/stl/Map.html
+      template<typename MapType>
+        void map_type(const std::string &type, Module& m)
+        {
+          m.add(user_type<MapType>(), type);
+
+          typedef typename MapType::mapped_type &(MapType::*elem_access)(const typename MapType::key_type &);
+          typedef const typename MapType::mapped_type &(MapType::*const_elem_access)(const typename MapType::key_type &) const;
+
+          m.add(fun(static_cast<elem_access>(&MapType::operator[])), "[]");
+
+          m.add(fun(static_cast<elem_access>(&MapType::at)), "at");
+          m.add(fun(static_cast<const_elem_access>(&MapType::at)), "at");
+
+          if (typeid(MapType) == typeid(std::map<std::string, Boxed_Value>))
+          {
+            m.eval(R"(
+                    def Map::`==`(Map rhs) {
+                       if ( rhs.size() != this.size() ) {
+                         return false;
+                       } else {
+                         auto r1 = range(this);
+                         auto r2 = range(rhs);
+                         while (!r1.empty())
+                         {
+                           if (!eq(r1.front().first, r2.front().first) || !eq(r1.front().second, r2.front().second))
+                           {
+                             return false;
+                           }
+                           r1.pop_front();
+                           r2.pop_front();
+                         }
+                         true;
+                       }
+                   } )"
+                 );
+          } 
+
+          container_type<MapType>(type, m);
+          default_constructible_type<MapType>(type, m);
+          assignable_type<MapType>(type, m);
+          unique_associative_container_type<MapType>(type, m);
+          pair_associative_container_type<MapType>(type, m);
+          input_range_type<MapType>(type, m);
+        }
+      template<typename MapType>
+        ModulePtr map_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          map_type<MapType>(type, *m);
+          return m;
+        }
+
+
+      /// http://www.sgi.com/tech/stl/List.html
+      template<typename ListType>
+        void list_type(const std::string &type, Module& m)
+        {
+          m.add(user_type<ListType>(), type);
+
+          front_insertion_sequence_type<ListType>(type, m);
+          back_insertion_sequence_type<ListType>(type, m);
+          sequence_type<ListType>(type, m);
+          resizable_type<ListType>(type, m);
+          container_type<ListType>(type, m);
+          default_constructible_type<ListType>(type, m);
+          assignable_type<ListType>(type, m);
+          input_range_type<ListType>(type, m);
+        }
+      template<typename ListType>
+        ModulePtr list_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          list_type<ListType>(type, m);
+          return m;
+        }
+
+
+      /// Create a vector type with associated concepts
+      /// http://www.sgi.com/tech/stl/Vector.html
+      template<typename VectorType>
+        void vector_type(const std::string &type, Module& m)
+        {
+          m.add(user_type<VectorType>(), type);
+
+          m.add(fun([](VectorType &container)->decltype(auto){ 
+                      if (container.empty()) {
+                        throw std::range_error("Container empty");
+                      } else {
+                        return (container.front());
+                      }
+                    }
+                  )
+                , "front");
+
+          m.add(fun([](const VectorType &container)->decltype(auto){ 
+                      if (container.empty()) {
+                        throw std::range_error("Container empty");
+                      } else {
+                        return (container.front());
+                      }
+                    }
+                  )
+                , "front");
+
+
+
+
+          back_insertion_sequence_type<VectorType>(type, m);
+          sequence_type<VectorType>(type, m);
+          random_access_container_type<VectorType>(type, m);
+          resizable_type<VectorType>(type, m);
+          reservable_type<VectorType>(type, m);
+          container_type<VectorType>(type, m);
+          default_constructible_type<VectorType>(type, m);
+          assignable_type<VectorType>(type, m);
+          input_range_type<VectorType>(type, m);
+
+          if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
+          {
+            m.eval(R"(
+                    def Vector::`==`(Vector rhs) {
+                       if ( rhs.size() != this.size() ) {
+                         return false;
+                       } else {
+                         auto r1 = range(this);
+                         auto r2 = range(rhs);
+                         while (!r1.empty())
+                         {
+                           if (!eq(r1.front(), r2.front()))
+                           {
+                             return false;
+                           }
+                           r1.pop_front();
+                           r2.pop_front();
+                         }
+                         true;
+                       }
+                   } )"
+                 );
+          } 
+        }
+      template<typename VectorType>
+        ModulePtr vector_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          vector_type<VectorType>(type, *m);
+          return m;
+        }
+
+      /// Add a String container
+      /// http://www.sgi.com/tech/stl/basic_string.html
+      template<typename String>
+        void string_type(const std::string &type, Module& m)
+        {
+          m.add(user_type<String>(), type);
+          operators::addition<String>(m);
+          operators::assign_sum<String>(m);
+          opers_comparison<String>(m);
+          random_access_container_type<String>(type, m);
+          sequence_type<String>(type, m);
+          default_constructible_type<String>(type, m);
+          // container_type<String>(type, m);
+          assignable_type<String>(type, m);
+          input_range_type<String>(type, m);
+
+          //Special case: add push_back to string (which doesn't support other back_insertion operations
+          m.add(fun(&String::push_back),
+              []()->std::string{
+                if (typeid(typename String::value_type) == typeid(Boxed_Value)) {
+                  return "push_back_ref";
+                } else {
+                  return "push_back";
+                }
+              }());
+
+
+          m.add(fun([](const String *s, const String &f, size_t pos) { return s->find(f, pos); } ), "find");
+          m.add(fun([](const String *s, const String &f, size_t pos) { return s->rfind(f, pos); } ), "rfind");
+          m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_of(f, pos); } ), "find_first_of");
+          m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); } ), "find_last_of");
+          m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); } ), "find_last_not_of");
+          m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); } ), "find_first_not_of");
+
+          m.add(fun([](String *s) { s->clear(); } ), "clear");
+          m.add(fun([](const String *s) { return s->empty(); } ), "empty");
+          m.add(fun([](const String *s) { return s->size(); } ), "size");
+
+          m.add(fun([](const String *s) { return s->c_str(); } ), "c_str");
+          m.add(fun([](const String *s) { return s->data(); } ), "data");
+          m.add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); } ), "substr");
+        }
+      template<typename String>
+        ModulePtr string_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          string_type<String>(type, *m);
+          return m;
+        }
+
+
+
+      /// Add a MapType container
+      /// http://www.sgi.com/tech/stl/Map.html
+      template<typename FutureType>
+        void future_type(const std::string &type, Module& m)
+        {
+          m.add(user_type<FutureType>(), type);
+
+          m.add(fun([](const FutureType &t) { return t.valid(); }), "valid");
+          m.add(fun(&FutureType::get), "get");
+          m.add(fun(&FutureType::wait), "wait");
+        }
+      template<typename FutureType>
+        ModulePtr future_type(const std::string &type)
+        {
+          auto m = std::make_shared<Module>();
+          future_type<FutureType>(type, *m);
+          return m;
+        }
+    }
+  }
+}
+
+
+#endif
+
+

+ 113 - 0
chaiscript/dispatchkit/boxed_cast.hpp

@@ -0,0 +1,113 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_BOXED_CAST_HPP_
+#define CHAISCRIPT_BOXED_CAST_HPP_
+
+#include "../chaiscript_defines.hpp"
+#include "bad_boxed_cast.hpp"
+#include "boxed_cast_helper.hpp"
+#include "boxed_value.hpp"
+#include "type_conversions.hpp"
+#include "type_info.hpp"
+
+namespace chaiscript {
+class Type_Conversions;
+namespace detail {
+namespace exception {
+class bad_any_cast;
+}  // namespace exception
+}  // namespace detail
+}  // namespace chaiscript
+
+namespace chaiscript 
+{
+ 
+  /// \brief Function for extracting a value stored in a Boxed_Value object
+  /// \tparam Type The type to extract from the Boxed_Value
+  /// \param[in] bv The Boxed_Value to extract a typed value from
+  /// \returns Type equivalent to the requested type 
+  /// \throws exception::bad_boxed_cast If the requested conversion is not possible
+  /// 
+  /// boxed_cast will attempt to make conversions between value, &, *, std::shared_ptr, std::reference_wrapper,
+  /// and std::function (const and non-const) where possible. boxed_cast is used internally during function
+  /// dispatch. This means that all of these conversions will be attempted automatically for you during
+  /// ChaiScript function calls.
+  ///
+  /// \li non-const values can be extracted as const or non-const
+  /// \li const values can be extracted only as const
+  /// \li Boxed_Value constructed from pointer or std::reference_wrapper can be extracted as reference,
+  ///     pointer or value types
+  /// \li Boxed_Value constructed from std::shared_ptr or value types can be extracted as reference,
+  ///     pointer, value, or std::shared_ptr types
+  ///
+  /// Conversions to std::function objects are attempted as well
+  ///
+  /// Example:
+  /// \code
+  /// // All of the following should succeed
+  /// chaiscript::Boxed_Value bv(1);
+  /// std::shared_ptr<int> spi = chaiscript::boxed_cast<std::shared_ptr<int> >(bv);
+  /// int i = chaiscript::boxed_cast<int>(bv);
+  /// int *ip = chaiscript::boxed_cast<int *>(bv);
+  /// int &ir = chaiscript::boxed_cast<int &>(bv);
+  /// std::shared_ptr<const int> cspi = chaiscript::boxed_cast<std::shared_ptr<const int> >(bv);
+  /// const int ci = chaiscript::boxed_cast<const int>(bv);
+  /// const int *cip = chaiscript::boxed_cast<const int *>(bv);
+  /// const int &cir = chaiscript::boxed_cast<const int &>(bv);
+  /// \endcode
+  ///
+  /// std::function conversion example
+  /// \code
+  /// chaiscript::ChaiScript chai;
+  /// Boxed_Value bv = chai.eval("`+`"); // Get the functor for the + operator which is built in 
+  /// std::function<int (int, int)> f = chaiscript::boxed_cast<std::function<int (int, int)> >(bv);
+  /// int i = f(2,3);
+  /// assert(i == 5);
+  /// \endcode
+  template<typename Type>
+  decltype(auto) boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions = nullptr)
+  {
+    if (!t_conversions || bv.get_type_info().bare_equal(user_type<Type>()) || (t_conversions && !(*t_conversions)->convertable_type<Type>())) {
+      try {
+        return(detail::Cast_Helper<Type>::cast(bv, t_conversions));
+      } catch (const chaiscript::detail::exception::bad_any_cast &) {
+      }
+    }
+
+
+    if (t_conversions && (*t_conversions)->convertable_type<Type>())
+    {
+      try {
+        // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it
+        // either way, we are not responsible if it doesn't work
+        return(detail::Cast_Helper<Type>::cast((*t_conversions)->boxed_type_conversion<Type>(t_conversions->saves(), bv), t_conversions));
+      } catch (...) {
+        try {
+          // try going the other way
+          return(detail::Cast_Helper<Type>::cast((*t_conversions)->boxed_type_down_conversion<Type>(t_conversions->saves(), bv), t_conversions));
+        } catch (const chaiscript::detail::exception::bad_any_cast &) {
+          throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type));
+        }
+      }
+    } else {
+      // If it's not convertable, just throw the error, don't waste the time on the 
+      // attempted dynamic_cast
+      throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type));
+    }
+
+  }
+
+}
+
+
+
+#endif
+

+ 323 - 0
chaiscript/dispatchkit/boxed_cast_helper.hpp

@@ -0,0 +1,323 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_BOXED_CAST_HELPER_HPP_
+#define CHAISCRIPT_BOXED_CAST_HELPER_HPP_
+
+#include <memory>
+#include <type_traits>
+
+#include "boxed_value.hpp"
+#include "type_info.hpp"
+
+
+namespace chaiscript 
+{
+  class Type_Conversions_State;
+
+  namespace detail
+  {
+    // Cast_Helper_Inner helper classes
+
+    template<typename T>
+      T* throw_if_null(T *t)
+      {
+        if (t) { return t; }
+        throw std::runtime_error("Attempted to dereference null Boxed_Value");
+      }
+
+    template<typename T>
+    static const T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) {
+      if (ob.get_type_info() == ti) {
+        return ptr;
+      } else {
+        throw chaiscript::detail::exception::bad_any_cast();
+      }
+    }
+
+    template<typename T>
+    static T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, T *ptr) {
+      if (!ob.is_const() && ob.get_type_info() == ti) {
+        return ptr;
+      } else {
+        throw chaiscript::detail::exception::bad_any_cast();
+      }
+    }
+
+
+    template<typename T>
+    static const T *verify_type(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) {
+      if (ob.get_type_info().bare_equal_type_info(ti)) {
+        return throw_if_null(ptr);
+      } else {
+        throw chaiscript::detail::exception::bad_any_cast();
+      }
+    }
+
+    template<typename T>
+    static T *verify_type(const Boxed_Value &ob, const std::type_info &ti, T *ptr) {
+      if (!ob.is_const() && ob.get_type_info().bare_equal_type_info(ti)) {
+        return throw_if_null(ptr);
+      } else {
+        throw chaiscript::detail::exception::bad_any_cast();
+      }
+    }
+
+    /// Generic Cast_Helper_Inner, for casting to any type
+    template<typename Result>
+      struct Cast_Helper_Inner
+      {
+        static Result cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return *static_cast<const Result *>(verify_type(ob, typeid(Result), ob.get_const_ptr()));
+        }
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<const Result> : Cast_Helper_Inner<Result>
+      {
+      };
+
+
+    /// Cast_Helper_Inner for casting to a const * type
+    template<typename Result>
+      struct Cast_Helper_Inner<const Result *>
+      {
+        static const Result * cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return static_cast<const Result *>(verify_type_no_throw(ob, typeid(Result), ob.get_const_ptr()));
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a * type
+    template<typename Result>
+      struct Cast_Helper_Inner<Result *>
+      {
+        static Result * cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return static_cast<Result *>(verify_type_no_throw(ob, typeid(Result), ob.get_ptr()));
+        }
+      };
+
+    template<typename Result>
+    struct Cast_Helper_Inner<Result * const &> : public Cast_Helper_Inner<Result *>
+    {
+    };
+
+    template<typename Result>
+    struct Cast_Helper_Inner<const Result * const &> : public Cast_Helper_Inner<const Result *>
+    {
+    };
+
+
+    /// Cast_Helper_Inner for casting to a & type
+    template<typename Result>
+      struct Cast_Helper_Inner<const Result &>
+      {
+        static const Result & cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return *static_cast<const Result *>(verify_type(ob, typeid(Result), ob.get_const_ptr()));
+        }
+      };
+
+
+
+    /// Cast_Helper_Inner for casting to a & type
+    template<typename Result>
+      struct Cast_Helper_Inner<Result &>
+      {
+        static Result& cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return *static_cast<Result *>(verify_type(ob, typeid(Result), ob.get_ptr()));
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a && type
+    template<typename Result>
+      struct Cast_Helper_Inner<Result &&>
+      {
+        static Result&& cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return std::move(*static_cast<Result *>(verify_type(ob, typeid(Result), ob.get_ptr())));
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a std::unique_ptr<> && type
+    /// \todo Fix the fact that this has to be in a shared_ptr for now
+    template<typename Result>
+      struct Cast_Helper_Inner<std::unique_ptr<Result> &&>
+      {
+        static std::unique_ptr<Result> &&cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return std::move(*(ob.get().cast<std::shared_ptr<std::unique_ptr<Result>>>()));
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a std::unique_ptr<> & type
+    /// \todo Fix the fact that this has to be in a shared_ptr for now
+    template<typename Result>
+      struct Cast_Helper_Inner<std::unique_ptr<Result> &>
+      {
+        static std::unique_ptr<Result> &cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return *(ob.get().cast<std::shared_ptr<std::unique_ptr<Result>>>());
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a std::unique_ptr<> & type
+    /// \todo Fix the fact that this has to be in a shared_ptr for now
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::unique_ptr<Result> &>
+      {
+        static std::unique_ptr<Result> &cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return *(ob.get().cast<std::shared_ptr<std::unique_ptr<Result>>>());
+        }
+      };
+
+
+    /// Cast_Helper_Inner for casting to a std::shared_ptr<> type
+    template<typename Result>
+      struct Cast_Helper_Inner<std::shared_ptr<Result> >
+      {
+        static auto cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return ob.get().cast<std::shared_ptr<Result> >();
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a std::shared_ptr<const> type
+    template<typename Result>
+      struct Cast_Helper_Inner<std::shared_ptr<const Result> >
+      {
+        static auto cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          if (!ob.get_type_info().is_const())
+          {
+            return std::const_pointer_cast<const Result>(ob.get().cast<std::shared_ptr<Result> >());
+          } else {
+            return ob.get().cast<std::shared_ptr<const Result> >();
+          }
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a const std::shared_ptr<> & type
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::shared_ptr<Result> > : Cast_Helper_Inner<std::shared_ptr<Result> >
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::shared_ptr<Result> &> : Cast_Helper_Inner<std::shared_ptr<Result> >
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<std::shared_ptr<Result> &>
+      {
+        static_assert(!std::is_const<Result>::value, "Non-const reference to std::shared_ptr<const T> is not supported");
+        static auto cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          std::shared_ptr<Result> &res = ob.get().cast<std::shared_ptr<Result> >();
+          return ob.pointer_sentinel(res);
+        }
+      };
+
+
+    /// Cast_Helper_Inner for casting to a const std::shared_ptr<const> & type
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::shared_ptr<const Result> > : Cast_Helper_Inner<std::shared_ptr<const Result> >
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::shared_ptr<const Result> &> : Cast_Helper_Inner<std::shared_ptr<const Result> >
+      {
+      };
+
+
+    /// Cast_Helper_Inner for casting to a Boxed_Value type
+    template<>
+      struct Cast_Helper_Inner<Boxed_Value>
+      {
+        static Boxed_Value cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return ob;
+        }
+      };
+
+    /// Cast_Helper_Inner for casting to a Boxed_Value & type
+    template<>
+      struct Cast_Helper_Inner<Boxed_Value &>
+      {
+        static std::reference_wrapper<Boxed_Value> cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return std::ref(const_cast<Boxed_Value &>(ob));
+        }
+      };
+
+
+    /// Cast_Helper_Inner for casting to a const Boxed_Value & type
+    template<>
+      struct Cast_Helper_Inner<const Boxed_Value> : Cast_Helper_Inner<Boxed_Value>
+      {
+      };
+
+    template<>
+      struct Cast_Helper_Inner<const Boxed_Value &> : Cast_Helper_Inner<Boxed_Value>
+      {
+      };
+
+
+    /// Cast_Helper_Inner for casting to a std::reference_wrapper type
+    template<typename Result>
+      struct Cast_Helper_Inner<std::reference_wrapper<Result> > : Cast_Helper_Inner<Result &>
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::reference_wrapper<Result> > : Cast_Helper_Inner<Result &>
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::reference_wrapper<Result> &> : Cast_Helper_Inner<Result &>
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<std::reference_wrapper<const Result> > : Cast_Helper_Inner<const Result &>
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::reference_wrapper<const Result> > : Cast_Helper_Inner<const Result &>
+      {
+      };
+
+    template<typename Result>
+      struct Cast_Helper_Inner<const std::reference_wrapper<const Result> & > : Cast_Helper_Inner<const Result &>
+      {
+      };
+
+    /// The exposed Cast_Helper object that by default just calls the Cast_Helper_Inner
+    template<typename T>
+      struct Cast_Helper
+      {
+        static decltype(auto) cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions)
+        {
+          return(Cast_Helper_Inner<T>::cast(ob, t_conversions));
+        }
+      };
+  }
+  
+}
+
+#endif

+ 942 - 0
chaiscript/dispatchkit/boxed_number.hpp

@@ -0,0 +1,942 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#ifndef CHAISCRIPT_BOXED_NUMERIC_HPP_
+#define CHAISCRIPT_BOXED_NUMERIC_HPP_
+
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+#include "../language/chaiscript_algebraic.hpp"
+#include "any.hpp"
+#include "boxed_cast.hpp"
+#include "boxed_cast_helper.hpp"
+#include "boxed_value.hpp"
+#include "type_info.hpp"
+
+namespace chaiscript {
+class Type_Conversions;
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  namespace exception
+  {
+    struct arithmetic_error : std::runtime_error
+    {
+      explicit arithmetic_error(const std::string& reason) : std::runtime_error("Arithmetic error: " + reason) {}
+      arithmetic_error(const arithmetic_error &) = default;
+      ~arithmetic_error() noexcept override = default;
+    };
+  }
+}
+
+namespace chaiscript 
+{
+
+// Due to the nature of generating every possible arithmetic operation, there
+// are going to be warnings generated on every platform regarding size and sign,
+// this is OK, so we're disabling size/and sign type warnings
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4244 4018 4389 4146 4365 4267 4242)
+#endif
+
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wfloat-conversion"
+#endif
+
+  /// \brief Represents any numeric type, generically. Used internally for generic operations between POD values
+  class Boxed_Number
+  {
+    private:
+      enum class Common_Types {
+        t_int32,
+        t_double,
+        t_uint8,
+        t_int8,
+        t_uint16,
+        t_int16,
+        t_uint32,
+        t_uint64,
+        t_int64,
+        t_float,
+        t_long_double
+      };
+
+      template<typename T>
+      static inline void check_divide_by_zero(T t, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr)
+      {
+#ifndef CHAISCRIPT_NO_PROTECT_DIVIDEBYZERO
+        if (t == 0) {
+          throw chaiscript::exception::arithmetic_error("divide by zero");
+        }
+#endif
+      }
+
+      template<typename T>
+      static inline void check_divide_by_zero(T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr)
+      {
+      }
+
+      static constexpr Common_Types get_common_type(size_t t_size, bool t_signed)
+      {
+        return   (t_size == 1 && t_signed)?(Common_Types::t_int8)
+                :(t_size == 1)?(Common_Types::t_uint8)
+                :(t_size == 2 && t_signed)?(Common_Types::t_int16)
+                :(t_size == 2)?(Common_Types::t_uint16)
+                :(t_size == 4 && t_signed)?(Common_Types::t_int32)
+                :(t_size == 4)?(Common_Types::t_uint32)
+                :(t_size == 8 && t_signed)?(Common_Types::t_int64)
+                :(Common_Types::t_uint64);
+      }
+
+
+      static Common_Types get_common_type(const Boxed_Value &t_bv)
+      {
+        const Type_Info &inp_ = t_bv.get_type_info();
+
+        if (inp_ == typeid(int)) {
+          return get_common_type(sizeof(int), true);
+        } else if (inp_ == typeid(double)) {
+          return Common_Types::t_double;
+        } else if (inp_ == typeid(long double)) {
+          return Common_Types::t_long_double;
+        } else if (inp_ == typeid(float)) {
+          return Common_Types::t_float;
+        } else if (inp_ == typeid(char)) {
+          return get_common_type(sizeof(char), std::is_signed<char>::value);
+        } else if (inp_ == typeid(unsigned char)) {
+          return get_common_type(sizeof(unsigned char), false);
+        } else if (inp_ == typeid(unsigned int)) {
+          return get_common_type(sizeof(unsigned int), false);
+        } else if (inp_ == typeid(long)) {
+          return get_common_type(sizeof(long), true);
+        } else if (inp_ == typeid(long long)) {
+          return get_common_type(sizeof(long long), true);
+        } else if (inp_ == typeid(unsigned long)) {
+          return get_common_type(sizeof(unsigned long), false);
+        } else if (inp_ == typeid(unsigned long long)) {
+          return get_common_type(sizeof(unsigned long long), false);
+        } else if (inp_ == typeid(std::int8_t)) {
+          return Common_Types::t_int8;
+        } else if (inp_ == typeid(std::int16_t)) {
+          return Common_Types::t_int16;
+        } else if (inp_ == typeid(std::int32_t)) {
+          return Common_Types::t_int32;
+        } else if (inp_ == typeid(std::int64_t)) {
+          return Common_Types::t_int64;
+        } else if (inp_ == typeid(std::uint8_t)) {
+          return Common_Types::t_uint8;
+        } else if (inp_ == typeid(std::uint16_t)) {
+          return Common_Types::t_uint16;
+        } else if (inp_ == typeid(std::uint32_t)) {
+          return Common_Types::t_uint32;
+        } else if (inp_ == typeid(std::uint64_t)) {
+          return Common_Types::t_uint64;
+        } else if (inp_ == typeid(wchar_t)) {
+          return get_common_type(sizeof(wchar_t), std::is_signed<wchar_t>::value);
+        } else if (inp_ == typeid(char16_t)) {
+          return get_common_type(sizeof(char16_t), std::is_signed<char16_t>::value);
+        } else if (inp_ == typeid(char32_t)) {
+          return get_common_type(sizeof(char32_t), std::is_signed<char32_t>::value);
+        } else  {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename T>
+      static Boxed_Value boolean_go(Operators::Opers t_oper, const T &t, const T &u)
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::equals:
+            return const_var(t == u);
+          case Operators::Opers::less_than:
+            return const_var(t < u);
+          case Operators::Opers::greater_than:
+            return const_var(t > u);
+          case Operators::Opers::less_than_equal:
+            return const_var(t <= u);
+          case Operators::Opers::greater_than_equal:
+            return const_var(t >= u);
+          case Operators::Opers::not_equal:
+            return const_var(t != u);
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename T>
+      static Boxed_Value unary_go(Operators::Opers t_oper, T &t, const Boxed_Value &t_lhs) 
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::pre_increment:
+            ++t;
+            break;
+          case Operators::Opers::pre_decrement:
+            --t;
+            break;
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+
+        return t_lhs;
+      }
+
+      template<typename T, typename U>
+      static Boxed_Value binary_go(Operators::Opers t_oper, T &t, const U &u, const Boxed_Value &t_lhs) 
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::assign:
+            t = u;
+            break;
+          case Operators::Opers::assign_product:
+            t *= u;
+            break;
+          case Operators::Opers::assign_sum:
+            t += u;
+            break;
+          case Operators::Opers::assign_quotient:
+            check_divide_by_zero(u);
+            t /= u;
+            break;
+          case Operators::Opers::assign_difference:
+            t -= u;
+            break;
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+
+        return t_lhs;
+      }
+
+      template<typename T, typename U>
+      static Boxed_Value binary_int_go(Operators::Opers t_oper, T &t, const U &u, const Boxed_Value &t_lhs) 
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::assign_bitwise_and:
+            t &= u;
+            break;
+          case Operators::Opers::assign_bitwise_or:
+            t |= u;
+            break;
+          case Operators::Opers::assign_shift_left:
+            t <<= u;
+            break;
+          case Operators::Opers::assign_shift_right:
+            t >>= u;
+            break;
+          case Operators::Opers::assign_remainder:
+            check_divide_by_zero(u);
+            t %= u;
+            break;
+          case Operators::Opers::assign_bitwise_xor:
+            t ^= u;
+            break;
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+        return t_lhs;
+      }
+
+      template<typename T>
+      static Boxed_Value const_unary_int_go(Operators::Opers t_oper, const T &t) 
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::bitwise_complement:
+            return const_var(~t);
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename T>
+      static Boxed_Value const_binary_int_go(Operators::Opers t_oper, const T &t, const T &u) 
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::shift_left:
+            return const_var(t << u);
+          case Operators::Opers::shift_right:
+            return const_var(t >> u);
+          case Operators::Opers::remainder:
+            check_divide_by_zero(u);
+            return const_var(t % u);
+          case Operators::Opers::bitwise_and:
+            return const_var(t & u);
+          case Operators::Opers::bitwise_or:
+            return const_var(t | u);
+          case Operators::Opers::bitwise_xor:
+            return const_var(t ^ u);
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename T>
+      static Boxed_Value const_unary_go(Operators::Opers t_oper, const T &t)
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::unary_minus:
+            return const_var(-t);
+          case Operators::Opers::unary_plus:
+            return const_var(+t);
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename T>
+      static Boxed_Value const_binary_go(Operators::Opers t_oper, const T &t, const T &u) 
+      {
+        switch (t_oper)
+        {
+          case Operators::Opers::sum:
+            return const_var(t + u);
+          case Operators::Opers::quotient:
+            check_divide_by_zero(u);
+            return const_var(t / u);
+          case Operators::Opers::product:
+            return const_var(t * u);
+          case Operators::Opers::difference:
+            return const_var(t - u);
+          default:
+            throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename LHS, typename RHS>
+      static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs)
+          -> typename std::enable_if<!std::is_floating_point<LHS>::value && !std::is_floating_point<RHS>::value, Boxed_Value>::type
+      {
+        typedef typename std::common_type<LHS, RHS>::type common_type;
+        if (t_oper > Operators::Opers::boolean_flag && t_oper < Operators::Opers::non_const_flag)
+        {
+          return boolean_go(t_oper, get_as_aux<common_type, LHS>(t_lhs), get_as_aux<common_type, RHS>(t_rhs));
+        } else if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) {
+          return binary_go(t_oper, *static_cast<LHS *>(t_lhs.get_ptr()), get_as_aux<common_type, RHS>(t_rhs), t_lhs);
+        } else if (t_oper > Operators::Opers::non_const_int_flag && t_oper < Operators::Opers::const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) {
+          return binary_int_go(t_oper, *static_cast<LHS *>(t_lhs.get_ptr()), get_as_aux<common_type, RHS>(t_rhs), t_lhs);
+        } else if (t_oper > Operators::Opers::const_int_flag && t_oper < Operators::Opers::const_flag) {
+          return const_binary_int_go(t_oper, get_as_aux<common_type, LHS>(t_lhs), get_as_aux<common_type, RHS>(t_rhs));
+        } else if (t_oper > Operators::Opers::const_flag) {
+          return const_binary_go(t_oper, get_as_aux<common_type, LHS>(t_lhs), get_as_aux<common_type, RHS>(t_rhs));
+        } else {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename LHS, typename RHS>
+      static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) 
+          -> typename std::enable_if<std::is_floating_point<LHS>::value || std::is_floating_point<RHS>::value, Boxed_Value>::type
+      {
+        typedef typename std::common_type<LHS, RHS>::type common_type;
+        if (t_oper > Operators::Opers::boolean_flag && t_oper < Operators::Opers::non_const_flag)
+        {
+          return boolean_go(t_oper, get_as_aux<common_type, LHS>(t_lhs), get_as_aux<common_type, RHS>(t_rhs));
+        } else if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) {
+          return binary_go(t_oper, *static_cast<LHS *>(t_lhs.get_ptr()), get_as_aux<common_type, RHS>(t_rhs), t_lhs);
+        } else if (t_oper > Operators::Opers::const_flag) {
+          return const_binary_go(t_oper, get_as_aux<common_type, LHS>(t_lhs), get_as_aux<common_type, RHS>(t_rhs));
+        } else {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      // Unary 
+      template<typename LHS>
+      static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs)
+          -> typename std::enable_if<!std::is_floating_point<LHS>::value, Boxed_Value>::type
+      {
+        if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) {
+          return unary_go(t_oper, *static_cast<LHS *>(t_lhs.get_ptr()), t_lhs);
+        } else if (t_oper > Operators::Opers::const_int_flag && t_oper < Operators::Opers::const_flag) {
+          return const_unary_int_go(t_oper, *static_cast<const LHS *>(t_lhs.get_const_ptr()));
+        } else if (t_oper > Operators::Opers::const_flag) {
+          return const_unary_go(t_oper, *static_cast<const LHS *>(t_lhs.get_const_ptr()));
+        } else {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename LHS>
+      static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs) 
+          -> typename std::enable_if<std::is_floating_point<LHS>::value, Boxed_Value>::type
+      {
+        if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) {
+          return unary_go(t_oper, *static_cast<LHS *>(t_lhs.get_ptr()), t_lhs);
+        } else if (t_oper > Operators::Opers::const_flag) {
+          return const_unary_go(t_oper, *static_cast<const LHS *>(t_lhs.get_const_ptr()));
+        } else {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+      template<typename LHS>
+        inline static Boxed_Value oper_rhs(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs)
+        {
+          switch (get_common_type(t_rhs)) {
+            case Common_Types::t_int32:
+              return go<LHS, int32_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint8:
+              return go<LHS, uint8_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_int8:
+              return go<LHS, int8_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint16:
+              return go<LHS, uint16_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_int16:
+              return go<LHS, int16_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint32:
+              return go<LHS, uint32_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint64:
+              return go<LHS, uint64_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_int64:
+              return go<LHS, int64_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_double:
+              return go<LHS, double>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_float:
+              return go<LHS, float>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_long_double:
+              return go<LHS, long double>(t_oper, t_lhs, t_rhs);
+          }
+
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+
+        inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs)
+        {
+          switch (get_common_type(t_lhs)) {
+            case Common_Types::t_int32:
+              return go<int32_t>(t_oper, t_lhs);
+            case Common_Types::t_uint8:
+              return go<uint8_t>(t_oper, t_lhs);
+            case Common_Types::t_int8:
+              return go<int8_t>(t_oper, t_lhs);
+            case Common_Types::t_uint16:
+              return go<uint16_t>(t_oper, t_lhs);
+            case Common_Types::t_int16:
+              return go<int16_t>(t_oper, t_lhs);
+            case Common_Types::t_uint32:
+              return go<uint32_t>(t_oper, t_lhs);
+            case Common_Types::t_uint64:
+              return go<uint64_t>(t_oper, t_lhs);
+            case Common_Types::t_int64:
+              return go<int64_t>(t_oper, t_lhs);
+            case Common_Types::t_double:
+              return go<double>(t_oper, t_lhs);
+            case Common_Types::t_float:
+              return go<float>(t_oper, t_lhs);
+            case Common_Types::t_long_double:
+              return go<long double>(t_oper, t_lhs);
+          }
+
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+
+
+        inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs)
+        {
+          switch (get_common_type(t_lhs)) {
+            case Common_Types::t_int32:
+              return oper_rhs<int32_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint8:
+              return oper_rhs<uint8_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_int8:
+              return oper_rhs<int8_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint16:
+              return oper_rhs<uint16_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_int16:
+              return oper_rhs<int16_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint32:
+              return oper_rhs<uint32_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_uint64:
+              return oper_rhs<uint64_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_int64:
+              return oper_rhs<int64_t>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_double:
+              return oper_rhs<double>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_float:
+              return oper_rhs<float>(t_oper, t_lhs, t_rhs);
+            case Common_Types::t_long_double:
+              return oper_rhs<long double>(t_oper, t_lhs, t_rhs);
+          }
+
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+
+        template<typename Target, typename Source>
+          static inline Target get_as_aux(const Boxed_Value &t_bv)
+          {
+            return static_cast<Target>(*static_cast<const Source *>(t_bv.get_const_ptr()));
+          }
+
+        template<typename Source>
+         static std::string to_string_aux(const Boxed_Value &v)
+        {
+          std::ostringstream oss;
+          oss << *static_cast<const Source *>(v.get_const_ptr());
+          return oss.str();
+        }
+
+    public:
+      Boxed_Number()
+        : bv(Boxed_Value(0))
+      {
+      }
+
+      explicit Boxed_Number(Boxed_Value v)
+        : bv(std::move(v))
+      {
+        validate_boxed_number(bv);
+      }
+
+      Boxed_Number(const Boxed_Number &) = default;
+      Boxed_Number(Boxed_Number &&) = default;
+      Boxed_Number& operator=(Boxed_Number &&) = default;
+
+      template<typename T> explicit Boxed_Number(T t)
+        : bv(Boxed_Value(t))
+      {
+        validate_boxed_number(bv);
+      }
+
+      static bool is_floating_point(const Boxed_Value &t_bv)
+      {
+        const Type_Info &inp_ = t_bv.get_type_info();
+
+        if (inp_ == typeid(double)) {
+          return true;
+        } else if (inp_ == typeid(long double)) {
+          return true;
+        } else if (inp_ == typeid(float)) {
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      Boxed_Number get_as(const Type_Info &inp_) const
+      {
+        if (inp_.bare_equal_type_info(typeid(int))) {
+          return Boxed_Number(get_as<int>());
+        } else if (inp_.bare_equal_type_info(typeid(double))) {
+          return Boxed_Number(get_as<double>());
+        } else if (inp_.bare_equal_type_info(typeid(float))) {
+          return Boxed_Number(get_as<float>());
+        } else if (inp_.bare_equal_type_info(typeid(long double))) {
+          return Boxed_Number(get_as<long double>());
+        } else if (inp_.bare_equal_type_info(typeid(char))) {
+          return Boxed_Number(get_as<char>());
+        } else if (inp_.bare_equal_type_info(typeid(unsigned char))) {
+          return Boxed_Number(get_as<unsigned char>());
+        } else if (inp_.bare_equal_type_info(typeid(wchar_t))) {
+          return Boxed_Number(get_as<wchar_t>());
+        } else if (inp_.bare_equal_type_info(typeid(char16_t))) {
+          return Boxed_Number(get_as<char16_t>());
+        } else if (inp_.bare_equal_type_info(typeid(char32_t))) {
+          return Boxed_Number(get_as<char32_t>());
+        } else if (inp_.bare_equal_type_info(typeid(unsigned int))) {
+          return Boxed_Number(get_as<unsigned int>());
+        } else if (inp_.bare_equal_type_info(typeid(long))) {
+          return Boxed_Number(get_as<long>());
+        } else if (inp_.bare_equal_type_info(typeid(long long))) {
+          return Boxed_Number(get_as<long long>());
+        } else if (inp_.bare_equal_type_info(typeid(unsigned long))) {
+          return Boxed_Number(get_as<unsigned long>());
+        } else if (inp_.bare_equal_type_info(typeid(unsigned long long))) {
+          return Boxed_Number(get_as<unsigned long long>());
+        } else if (inp_.bare_equal_type_info(typeid(int8_t))) {
+          return Boxed_Number(get_as<int8_t>());
+        } else if (inp_.bare_equal_type_info(typeid(int16_t))) {
+          return Boxed_Number(get_as<int16_t>());
+        } else if (inp_.bare_equal_type_info(typeid(int32_t))) {
+          return Boxed_Number(get_as<int32_t>());
+        } else if (inp_.bare_equal_type_info(typeid(int64_t))) {
+          return Boxed_Number(get_as<int64_t>());
+        } else if (inp_.bare_equal_type_info(typeid(uint8_t))) {
+          return Boxed_Number(get_as<uint8_t>());
+        } else if (inp_.bare_equal_type_info(typeid(uint16_t))) {
+          return Boxed_Number(get_as<uint16_t>());
+        } else if (inp_.bare_equal_type_info(typeid(uint32_t))) {
+          return Boxed_Number(get_as<uint32_t>());
+        } else if (inp_.bare_equal_type_info(typeid(uint64_t))) {
+          return Boxed_Number(get_as<uint64_t>());
+        } else {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+
+      }
+
+      template<typename Source, typename Target> 
+      static void check_type()
+      {
+#ifdef CHAISCRIPT_MSVC
+// MSVC complains about this being redundant / tautologica l
+#pragma warning(push)
+#pragma warning(disable : 4127 6287)
+#endif
+        if (sizeof(Source) != sizeof(Target)
+            || std::is_signed<Source>() != std::is_signed<Target>()
+            || std::is_floating_point<Source>() != std::is_floating_point<Target>())
+        {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+      }
+
+      template<typename Target> Target get_as_checked() const
+      {
+        switch (get_common_type(bv)) {
+          case Common_Types::t_int32:
+            check_type<int32_t, Target>();
+            return get_as_aux<Target, int32_t>(bv);
+          case Common_Types::t_uint8:
+            check_type<uint8_t, Target>();
+            return get_as_aux<Target, uint8_t>(bv);
+          case Common_Types::t_int8:
+            check_type<int8_t, Target>();
+            return get_as_aux<Target, int8_t>(bv);
+          case Common_Types::t_uint16:
+            check_type<uint16_t, Target>();
+            return get_as_aux<Target, uint16_t>(bv);
+          case Common_Types::t_int16:
+            check_type<int16_t, Target>();
+            return get_as_aux<Target, int16_t>(bv);
+          case Common_Types::t_uint32:
+            check_type<uint32_t, Target>();
+            return get_as_aux<Target, uint32_t>(bv);
+          case Common_Types::t_uint64:
+            check_type<uint64_t, Target>();
+            return get_as_aux<Target, uint64_t>(bv);
+          case Common_Types::t_int64:
+            check_type<int64_t, Target>();
+            return get_as_aux<Target, int64_t>(bv);
+          case Common_Types::t_double:
+            check_type<double, Target>();
+            return get_as_aux<Target, double>(bv);
+          case Common_Types::t_float:
+            check_type<float, Target>();
+            return get_as_aux<Target, float>(bv);
+          case Common_Types::t_long_double:
+            check_type<long double, Target>();
+            return get_as_aux<Target, long double>(bv);
+        }
+
+        throw chaiscript::detail::exception::bad_any_cast();
+      }
+
+
+      template<typename Target> Target get_as() const
+      {
+        switch (get_common_type(bv)) {
+          case Common_Types::t_int32:
+            return get_as_aux<Target, int32_t>(bv);
+          case Common_Types::t_uint8:
+            return get_as_aux<Target, uint8_t>(bv);
+          case Common_Types::t_int8:
+            return get_as_aux<Target, int8_t>(bv);
+          case Common_Types::t_uint16:
+            return get_as_aux<Target, uint16_t>(bv);
+          case Common_Types::t_int16:
+            return get_as_aux<Target, int16_t>(bv);
+          case Common_Types::t_uint32:
+            return get_as_aux<Target, uint32_t>(bv);
+          case Common_Types::t_uint64:
+            return get_as_aux<Target, uint64_t>(bv);
+          case Common_Types::t_int64:
+            return get_as_aux<Target, int64_t>(bv);
+          case Common_Types::t_double:
+            return get_as_aux<Target, double>(bv);
+          case Common_Types::t_float:
+            return get_as_aux<Target, float>(bv);
+          case Common_Types::t_long_double:
+            return get_as_aux<Target, long double>(bv);
+        }
+
+        throw chaiscript::detail::exception::bad_any_cast();
+      }
+
+      std::string to_string() const
+      {
+        switch (get_common_type(bv)) {
+          case Common_Types::t_int32:
+            return std::to_string(get_as<int32_t>());
+          case Common_Types::t_uint8:
+            return std::to_string(get_as<uint32_t>());
+          case Common_Types::t_int8:
+            return std::to_string(get_as<int32_t>());
+          case Common_Types::t_uint16:
+            return std::to_string(get_as<uint16_t>());
+          case Common_Types::t_int16:
+            return std::to_string(get_as<int16_t>());
+          case Common_Types::t_uint32:
+            return std::to_string(get_as<uint32_t>());
+          case Common_Types::t_uint64:
+            return std::to_string(get_as<uint64_t>());
+          case Common_Types::t_int64:
+            return std::to_string(get_as<int64_t>());
+          case Common_Types::t_double:
+            return to_string_aux<double>(bv);
+          case Common_Types::t_float:
+            return to_string_aux<float>(bv);
+          case Common_Types::t_long_double:
+            return to_string_aux<long double>(bv);
+        }
+
+        throw chaiscript::detail::exception::bad_any_cast();
+      }
+
+      static void validate_boxed_number(const Boxed_Value &v)
+      {
+        const Type_Info &inp_ = v.get_type_info();
+        if (inp_ == typeid(bool))
+        {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+
+        if (!inp_.is_arithmetic())
+        {
+          throw chaiscript::detail::exception::bad_any_cast();
+        }
+      }
+
+
+
+      static bool equals(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return boxed_cast<bool>(oper(Operators::Opers::equals, t_lhs.bv, t_rhs.bv));
+      }
+
+      static bool less_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return boxed_cast<bool>(oper(Operators::Opers::less_than, t_lhs.bv, t_rhs.bv));
+      }
+
+      static bool greater_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) 
+      {
+        return boxed_cast<bool>(oper(Operators::Opers::greater_than, t_lhs.bv, t_rhs.bv));
+      }
+
+      static bool greater_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return boxed_cast<bool>(oper(Operators::Opers::greater_than_equal, t_lhs.bv, t_rhs.bv));
+      }
+
+      static bool less_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return boxed_cast<bool>(oper(Operators::Opers::less_than_equal, t_lhs.bv, t_rhs.bv));
+      }
+
+      static bool not_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) 
+      {
+        return boxed_cast<bool>(oper(Operators::Opers::not_equal, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number pre_decrement(Boxed_Number t_lhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::pre_decrement, t_lhs.bv));
+      }
+
+      static Boxed_Number pre_increment(Boxed_Number t_lhs) 
+      {
+        return Boxed_Number(oper(Operators::Opers::pre_increment, t_lhs.bv));
+      }
+
+      static const Boxed_Number sum(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) 
+      {
+        return Boxed_Number(oper(Operators::Opers::sum, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number unary_plus(const Boxed_Number &t_lhs) 
+      {
+        return Boxed_Number(oper(Operators::Opers::unary_plus, t_lhs.bv));
+      }
+
+      static const Boxed_Number unary_minus(const Boxed_Number &t_lhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::unary_minus, t_lhs.bv));
+      }
+
+      static const Boxed_Number difference(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) 
+      {
+        return Boxed_Number(oper(Operators::Opers::difference, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_bitwise_and(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_bitwise_and, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign(Boxed_Number t_lhs, const Boxed_Number &t_rhs) 
+      {
+        return Boxed_Number(oper(Operators::Opers::assign, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_bitwise_or(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_bitwise_or, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_bitwise_xor(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_bitwise_xor, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_remainder(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_remainder, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_shift_left(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_shift_left, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_shift_right(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_shift_right, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number bitwise_and(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::bitwise_and, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number bitwise_complement(const Boxed_Number &t_lhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::bitwise_complement, t_lhs.bv, Boxed_Value(0)));
+      }
+
+      static const Boxed_Number bitwise_xor(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::bitwise_xor, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number bitwise_or(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::bitwise_or, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_product(Boxed_Number t_lhs, const Boxed_Number &t_rhs) 
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_product, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_quotient(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_quotient, t_lhs.bv, t_rhs.bv));
+      }
+
+      static Boxed_Number assign_sum(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_sum, t_lhs.bv, t_rhs.bv));
+      }
+      static Boxed_Number assign_difference(Boxed_Number t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::assign_difference, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number quotient(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) 
+      {
+        return Boxed_Number(oper(Operators::Opers::quotient, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number shift_left(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::shift_left, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number product(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::product, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number remainder(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::remainder, t_lhs.bv, t_rhs.bv));
+      }
+
+      static const Boxed_Number shift_right(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs)
+      {
+        return Boxed_Number(oper(Operators::Opers::shift_right, t_lhs.bv, t_rhs.bv));
+      }
+
+
+
+      static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs)
+      {
+        return oper(t_oper, t_lhs, t_rhs);
+      }
+
+      static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs)
+      {
+        return oper(t_oper, t_lhs);
+      }
+
+
+
+      Boxed_Value bv;
+  };
+
+  namespace detail
+  {
+    /// Cast_Helper for converting from Boxed_Value to Boxed_Number
+    template<>
+      struct Cast_Helper<Boxed_Number>
+      {
+        static Boxed_Number cast(const Boxed_Value &ob, const Type_Conversions_State *)
+        {
+          return Boxed_Number(ob);
+        }
+      };
+
+    /// Cast_Helper for converting from Boxed_Value to Boxed_Number
+    template<>
+      struct Cast_Helper<const Boxed_Number &> : Cast_Helper<Boxed_Number>
+      {
+      };
+
+    /// Cast_Helper for converting from Boxed_Value to Boxed_Number
+    template<>
+      struct Cast_Helper<const Boxed_Number> : Cast_Helper<Boxed_Number>
+      {
+      };
+  }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+
+}
+
+
+
+#endif
+

+ 485 - 0
chaiscript/dispatchkit/boxed_value.hpp

@@ -0,0 +1,485 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_BOXED_VALUE_HPP_
+#define CHAISCRIPT_BOXED_VALUE_HPP_
+
+#include <map>
+#include <memory>
+#include <type_traits>
+
+#include "../chaiscript_defines.hpp"
+#include "any.hpp"
+#include "type_info.hpp"
+
+namespace chaiscript 
+{
+
+  /// \brief A wrapper for holding any valid C++ type. All types in ChaiScript are Boxed_Value objects
+  /// \sa chaiscript::boxed_cast
+  class Boxed_Value
+  {
+    public:
+      /// used for explicitly creating a "void" object
+      struct Void_Type
+      {
+      };
+
+    private:
+      /// structure which holds the internal state of a Boxed_Value
+      /// \todo Get rid of Any and merge it with this, reducing an allocation in the process
+      struct Data
+      {
+        Data(const Type_Info &ti,
+            chaiscript::detail::Any to,
+            bool is_ref,
+            const void *t_void_ptr,
+            bool t_return_value)
+          : m_type_info(ti), m_obj(std::move(to)), m_data_ptr(ti.is_const()?nullptr:const_cast<void *>(t_void_ptr)), m_const_data_ptr(t_void_ptr),
+            m_is_ref(is_ref), m_return_value(t_return_value)
+        {
+        }
+
+        Data &operator=(const Data &rhs)
+        {
+          m_type_info = rhs.m_type_info;
+          m_obj = rhs.m_obj;
+          m_is_ref = rhs.m_is_ref;
+          m_data_ptr = rhs.m_data_ptr;
+          m_const_data_ptr = rhs.m_const_data_ptr;
+          m_return_value = rhs.m_return_value;
+
+          if (rhs.m_attrs)
+          {
+            m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>(*rhs.m_attrs);
+          }
+
+          return *this;
+        }
+
+        Data(const Data &) = delete;
+
+        Data(Data &&) = default;
+        Data &operator=(Data &&rhs) = default;
+
+
+        Type_Info m_type_info;
+        chaiscript::detail::Any m_obj;
+        void *m_data_ptr;
+        const void *m_const_data_ptr;
+        std::unique_ptr<std::map<std::string, std::shared_ptr<Data>>> m_attrs;
+        bool m_is_ref;
+        bool m_return_value;
+      };
+
+      struct Object_Data
+      {
+        static auto get(Boxed_Value::Void_Type, bool t_return_value)
+        {
+          return std::make_shared<Data>(
+                detail::Get_Type_Info<void>::get(),
+                chaiscript::detail::Any(), 
+                false,
+                nullptr,
+                t_return_value)
+              ;
+        }
+
+        template<typename T>
+          static auto get(const std::shared_ptr<T> *obj, bool t_return_value)
+          {
+            return get(*obj, t_return_value);
+          }
+
+        template<typename T>
+          static auto get(const std::shared_ptr<T> &obj, bool t_return_value)
+          {
+            return std::make_shared<Data>(
+                  detail::Get_Type_Info<T>::get(), 
+                  chaiscript::detail::Any(obj), 
+                  false,
+                  obj.get(),
+                  t_return_value
+                );
+          }
+
+        template<typename T>
+          static auto get(std::shared_ptr<T> &&obj, bool t_return_value)
+          {
+            auto ptr = obj.get();
+            return std::make_shared<Data>(
+                  detail::Get_Type_Info<T>::get(), 
+                  chaiscript::detail::Any(std::move(obj)), 
+                  false,
+                  ptr,
+                  t_return_value
+                );
+          }
+
+
+
+        template<typename T>
+          static auto get(T *t, bool t_return_value)
+          {
+            return get(std::ref(*t), t_return_value);
+          }
+
+        template<typename T>
+          static auto get(const T *t, bool t_return_value)
+          {
+            return get(std::cref(*t), t_return_value);
+          }
+
+
+        template<typename T>
+          static auto get(std::reference_wrapper<T> obj, bool t_return_value)
+          {
+            auto p = &obj.get();
+            return std::make_shared<Data>(
+                  detail::Get_Type_Info<T>::get(),
+                  chaiscript::detail::Any(std::move(obj)),
+                  true,
+                  p,
+                  t_return_value
+                );
+          }
+
+        template<typename T>
+          static auto get(std::unique_ptr<T> &&obj, bool t_return_value)
+          {
+            auto ptr = obj.get();
+            return std::make_shared<Data>(
+                  detail::Get_Type_Info<T>::get(), 
+                  chaiscript::detail::Any(std::make_shared<std::unique_ptr<T>>(std::move(obj))), 
+                  true,
+                  ptr,
+                  t_return_value
+                );
+          }
+
+        template<typename T>
+          static auto get(T t, bool t_return_value)
+          {
+            auto p = std::make_shared<T>(std::move(t));
+            auto ptr = p.get();
+            return std::make_shared<Data>(
+                  detail::Get_Type_Info<T>::get(), 
+                  chaiscript::detail::Any(std::move(p)),
+                  false,
+                  ptr,
+                  t_return_value
+                );
+          }
+
+        static std::shared_ptr<Data> get()
+        {
+          return std::make_shared<Data>(
+                Type_Info(),
+                chaiscript::detail::Any(),
+                false,
+                nullptr,
+                false
+              );
+        }
+
+      };
+
+    public:
+      /// Basic Boxed_Value constructor
+        template<typename T,
+          typename = typename std::enable_if<!std::is_same<Boxed_Value, typename std::decay<T>::type>::value>::type>
+        explicit Boxed_Value(T &&t, bool t_return_value = false)
+          : m_data(Object_Data::get(std::forward<T>(t), t_return_value))
+        {
+        }
+
+      /// Unknown-type constructor
+      Boxed_Value() = default;
+
+      Boxed_Value(Boxed_Value&&) = default;
+      Boxed_Value& operator=(Boxed_Value&&) = default;
+      Boxed_Value(const Boxed_Value&) = default;
+      Boxed_Value& operator=(const Boxed_Value&) = default;
+
+      void swap(Boxed_Value &rhs)
+      {
+        std::swap(m_data, rhs.m_data);
+      }
+
+      /// Copy the values stored in rhs.m_data to m_data.
+      /// m_data pointers are not shared in this case
+      Boxed_Value assign(const Boxed_Value &rhs)
+      {
+        (*m_data) = (*rhs.m_data);
+        return *this;
+      }
+
+      const Type_Info &get_type_info() const noexcept
+      {
+        return m_data->m_type_info;
+      }
+
+      /// return true if the object is uninitialized
+      bool is_undef() const noexcept
+      {
+        return m_data->m_type_info.is_undef();
+      }
+
+      bool is_const() const noexcept
+      {
+        return m_data->m_type_info.is_const();
+      }
+
+      bool is_type(const Type_Info &ti) const noexcept
+      {
+        return m_data->m_type_info.bare_equal(ti);
+      }
+
+
+      template<typename T>
+      auto pointer_sentinel(std::shared_ptr<T> &ptr) const
+      {
+        struct Sentinel {
+          Sentinel(std::shared_ptr<T> &t_ptr, Data &data)
+            : m_ptr(t_ptr), m_data(data)
+          {
+          }
+
+          ~Sentinel()
+          {
+            // save new pointer data
+            const auto ptr_ = m_ptr.get().get();
+            m_data.get().m_data_ptr = ptr_;
+            m_data.get().m_const_data_ptr = ptr_;
+          }
+
+          Sentinel& operator=(Sentinel&&s) = default;
+          Sentinel(Sentinel &&s) = default;
+
+          operator std::shared_ptr<T>&() const
+          {
+            return m_ptr.get();
+          }
+
+          Sentinel &operator=(const Sentinel &) = delete;
+          Sentinel(Sentinel&) = delete;
+
+          std::reference_wrapper<std::shared_ptr<T>> m_ptr;
+          std::reference_wrapper<Data> m_data;
+        };
+
+        return Sentinel(ptr, *(m_data.get()));
+      }
+
+      bool is_null() const noexcept
+      {
+        return (m_data->m_data_ptr == nullptr && m_data->m_const_data_ptr == nullptr);
+      }
+
+      const chaiscript::detail::Any & get() const noexcept
+      {
+        return m_data->m_obj;
+      }
+
+      bool is_ref() const noexcept
+      {
+        return m_data->m_is_ref;
+      }
+
+      bool is_return_value() const noexcept
+      {
+        return m_data->m_return_value;
+      }
+
+      void reset_return_value() const noexcept
+      {
+        m_data->m_return_value = false;
+      }
+
+      bool is_pointer() const noexcept
+      {
+        return !is_ref();
+      }
+
+      void *get_ptr() const noexcept
+      {
+        return m_data->m_data_ptr;
+      }
+
+      const void *get_const_ptr() const noexcept
+      {
+        return m_data->m_const_data_ptr;
+      }
+
+      Boxed_Value get_attr(const std::string &t_name)
+      {
+        if (!m_data->m_attrs)
+        {
+          m_data->m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>();
+        }
+
+        auto &attr = (*m_data->m_attrs)[t_name];
+        if (attr) {
+          return Boxed_Value(attr, Internal_Construction());
+        } else {
+          Boxed_Value bv; //default construct a new one
+          attr = bv.m_data;
+          return bv;
+        }
+      }
+
+      Boxed_Value &copy_attrs(const Boxed_Value &t_obj)
+      {
+        if (t_obj.m_data->m_attrs)
+        {
+          m_data->m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>(*t_obj.m_data->m_attrs);
+        }
+        return *this;
+      }
+
+      Boxed_Value &clone_attrs(const Boxed_Value &t_obj)
+      {
+        copy_attrs(t_obj);
+        reset_return_value();
+        return *this;
+      }
+
+
+      /// \returns true if the two Boxed_Values share the same internal type
+      static bool type_match(const Boxed_Value &l, const Boxed_Value &r) noexcept
+      {
+        return l.get_type_info() == r.get_type_info();
+      }
+
+    private:
+      // necessary to avoid hitting the templated && constructor of Boxed_Value
+      struct Internal_Construction{};
+
+      Boxed_Value(std::shared_ptr<Data> t_data, Internal_Construction)
+        : m_data(std::move(t_data)) {
+      }
+
+      std::shared_ptr<Data> m_data = Object_Data::get();
+  };
+
+  /// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or std::reference_type
+  ///        a copy is not made.
+  /// @param t The value to box
+  ///
+  /// Example:
+  ///
+  /// ~~~{.cpp}
+  /// int i;
+  /// chaiscript::ChaiScript chai;
+  /// chai.add(chaiscript::var(i), "i");
+  /// chai.add(chaiscript::var(&i), "ip");
+  /// ~~~
+  ///
+  /// @sa @ref adding_objects
+  template<typename T>
+    Boxed_Value var(T &&t)
+    {
+      return Boxed_Value(std::forward<T>(t));
+    }
+
+  namespace detail {
+    /// \brief Takes a value, copies it and returns a Boxed_Value object that is immutable
+    /// \param[in] t Value to copy and make const
+    /// \returns Immutable Boxed_Value 
+    /// \sa Boxed_Value::is_const
+    template<typename T>
+      Boxed_Value const_var_impl(const T &t)
+      {
+        return Boxed_Value(std::make_shared<typename std::add_const<T>::type >(t));
+      }
+
+    /// \brief Takes a pointer to a value, adds const to the pointed to type and returns an immutable Boxed_Value.
+    ///        Does not copy the pointed to value.
+    /// \param[in] t Pointer to make immutable
+    /// \returns Immutable Boxed_Value
+    /// \sa Boxed_Value::is_const
+    template<typename T>
+      Boxed_Value const_var_impl(T *t)
+      {
+        return Boxed_Value( const_cast<typename std::add_const<T>::type *>(t) );
+      }
+
+    /// \brief Takes a std::shared_ptr to a value, adds const to the pointed to type and returns an immutable Boxed_Value.
+    ///        Does not copy the pointed to value.
+    /// \param[in] t Pointer to make immutable
+    /// \returns Immutable Boxed_Value
+    /// \sa Boxed_Value::is_const
+    template<typename T>
+      Boxed_Value const_var_impl(const std::shared_ptr<T> &t)
+      {
+        return Boxed_Value( std::const_pointer_cast<typename std::add_const<T>::type>(t) );
+      }
+
+    /// \brief Takes a std::reference_wrapper value, adds const to the referenced type and returns an immutable Boxed_Value.
+    ///        Does not copy the referenced value.
+    /// \param[in] t Reference object to make immutable
+    /// \returns Immutable Boxed_Value
+    /// \sa Boxed_Value::is_const
+    template<typename T>
+      Boxed_Value const_var_impl(const std::reference_wrapper<T> &t)
+      {
+        return Boxed_Value( std::cref(t.get()) );
+      }
+  }
+
+  /// \brief Takes an object and returns an immutable Boxed_Value. If the object is a std::reference or pointer type
+  ///        the value is not copied. If it is an object type, it is copied.
+  /// \param[in] t Object to make immutable
+  /// \returns Immutable Boxed_Value
+  /// \sa chaiscript::Boxed_Value::is_const
+  /// \sa chaiscript::var
+  ///
+  /// Example:
+  /// \code
+  /// enum Colors
+  /// {
+  ///   Blue,
+  ///   Green,
+  ///   Red
+  /// };
+  /// chaiscript::ChaiScript chai
+  /// chai.add(chaiscript::const_var(Blue), "Blue"); // add immutable constant
+  /// chai.add(chaiscript::const_var(Red), "Red");
+  /// chai.add(chaiscript::const_var(Green), "Green");
+  /// \endcode
+  /// 
+  /// \todo support C++11 strongly typed enums
+  /// \sa \ref adding_objects
+  template<typename T>
+    Boxed_Value const_var(const T &t)
+    {
+      return detail::const_var_impl(t);
+    }
+
+  inline Boxed_Value void_var() {
+    static const auto v = Boxed_Value(Boxed_Value::Void_Type());
+    return v;
+  }
+
+  inline Boxed_Value const_var(bool b) {
+    static const auto t = detail::const_var_impl(true);
+    static const auto f = detail::const_var_impl(false);
+
+    if (b) {
+      return t;
+    } else {
+      return f;
+    }
+  }
+
+}
+
+#endif
+

+ 107 - 0
chaiscript/dispatchkit/callable_traits.hpp

@@ -0,0 +1,107 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_CALLABLE_TRAITS_HPP_
+#define CHAISCRIPT_CALLABLE_TRAITS_HPP_
+
+#include <memory>
+
+namespace chaiscript {
+  namespace dispatch {
+    namespace detail {
+
+      template<typename Class, typename ... Param>
+      struct Constructor
+      {
+        template<typename ... Inner>
+        std::shared_ptr<Class> operator()(Inner&& ... inner) const {
+          return std::make_shared<Class>(std::forward<Inner>(inner)...);
+        }
+      };
+
+      template<typename Ret, typename Class, typename ... Param>
+      struct Const_Caller
+      {
+        explicit Const_Caller(Ret (Class::*t_func)(Param...) const) : m_func(t_func) {}
+
+        template<typename ... Inner>
+        Ret operator()(const Class &o, Inner&& ... inner) const {
+          return (o.*m_func)(std::forward<Inner>(inner)...);
+        }
+
+        Ret (Class::*m_func)(Param...) const;
+      };
+
+      template<typename Ret, typename ... Param>
+      struct Fun_Caller
+      {
+        explicit Fun_Caller(Ret( * t_func)(Param...) ) : m_func(t_func) {}
+
+        template<typename ... Inner>
+        Ret operator()(Inner&& ... inner) const {
+          return (m_func)(std::forward<Inner>(inner)...);
+        }
+
+        Ret(*m_func)(Param...);
+      };
+
+      template<typename Ret, typename Class, typename ... Param>
+      struct Caller
+      {
+        explicit Caller(Ret (Class::*t_func)(Param...)) : m_func(t_func) {}
+
+        template<typename ... Inner>
+        Ret operator()(Class &o, Inner&& ... inner) const {
+          return (o.*m_func)(std::forward<Inner>(inner)...);
+        }
+
+        Ret (Class::*m_func)(Param...);
+      };
+
+      template<typename T>
+        struct Arity
+        {
+        };
+
+      template<typename Ret, typename ... Params>
+      struct Arity<Ret (Params...)>
+        {
+          static const size_t arity = sizeof...(Params);
+        };
+
+
+      template<typename T>
+        struct Function_Signature
+        {
+        };
+
+      template<typename Ret, typename ... Params>
+      struct Function_Signature<Ret (Params...)>
+        {
+          typedef Ret Return_Type;
+          typedef Ret (Signature)(Params...);
+        };
+
+      template<typename Ret, typename T, typename ... Params>
+      struct Function_Signature<Ret (T::*)(Params...) const>
+        {
+          typedef Ret Return_Type;
+          typedef Ret (Signature)(Params...);
+        };
+
+
+      template<typename T>
+        struct Callable_Traits
+        {
+          typedef typename Function_Signature<decltype(&T::operator())>::Signature Signature;
+          typedef typename Function_Signature<decltype(&T::operator())>::Return_Type Return_Type;
+        };
+    }
+  }
+}
+
+#endif
+

+ 1572 - 0
chaiscript/dispatchkit/dispatchkit.hpp

@@ -0,0 +1,1572 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_DISPATCHKIT_HPP_
+#define CHAISCRIPT_DISPATCHKIT_HPP_
+
+#include <algorithm>
+#include <iostream>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <stdexcept>
+#include <string>
+#include <typeinfo>
+#include <utility>
+#include <vector>
+
+#include "../chaiscript_defines.hpp"
+#include "../chaiscript_threading.hpp"
+#include "bad_boxed_cast.hpp"
+#include "boxed_cast.hpp"
+#include "boxed_cast_helper.hpp"
+#include "boxed_value.hpp"
+#include "type_conversions.hpp"
+#include "dynamic_object.hpp"
+#include "proxy_constructors.hpp"
+#include "proxy_functions.hpp"
+#include "type_info.hpp"
+#include "short_alloc.hpp"
+
+namespace chaiscript {
+class Boxed_Number;
+}  // namespace chaiscript
+
+namespace chaiscript {
+  namespace parser {
+    class ChaiScript_Parser_Base;
+  }
+namespace dispatch {
+class Dynamic_Proxy_Function;
+class Proxy_Function_Base;
+struct Placeholder_Object;
+}  // namespace dispatch
+}  // namespace chaiscript
+
+
+
+/// \namespace chaiscript::dispatch
+/// \brief Classes and functions specific to the runtime dispatch side of ChaiScript. Some items may be of use to the end user.
+
+namespace chaiscript
+{
+  namespace exception
+  {
+    /// Exception thrown in the case that an object name is invalid because it is a reserved word
+    class reserved_word_error : public std::runtime_error
+    {
+      public:
+        explicit reserved_word_error(const std::string &t_word) noexcept
+          : std::runtime_error("Reserved word not allowed in object name: " + t_word), m_word(t_word)
+        {
+        }
+
+        reserved_word_error(const reserved_word_error &) = default;
+
+        ~reserved_word_error() noexcept override = default;
+
+        std::string word() const
+        {
+          return m_word;
+        }
+
+      private:
+        std::string m_word;
+    };
+
+    /// Exception thrown in the case that an object name is invalid because it contains illegal characters
+    class illegal_name_error : public std::runtime_error
+    {
+      public:
+        explicit illegal_name_error(const std::string &t_name) noexcept
+          : std::runtime_error("Reserved name not allowed in object name: " + t_name), m_name(t_name)
+        {
+        }
+
+        illegal_name_error(const illegal_name_error &) = default;
+
+        ~illegal_name_error() noexcept override = default;
+
+        std::string name() const
+        {
+          return m_name;
+        }
+
+      private:
+        std::string m_name;
+    };
+
+
+    /// Exception thrown in the case that an object name is invalid because it already exists in current context
+    class name_conflict_error : public std::runtime_error
+    {
+      public:
+        explicit name_conflict_error(const std::string &t_name) noexcept
+          : std::runtime_error("Name already exists in current context " + t_name), m_name(t_name)
+        {
+        }
+
+        name_conflict_error(const name_conflict_error &) = default;
+
+        ~name_conflict_error() noexcept override = default;
+
+        std::string name() const
+        {
+          return m_name;
+        }
+
+      private:
+        std::string m_name;
+
+    };
+
+
+    /// Exception thrown in the case that a non-const object was added as a shared object
+    class global_non_const : public std::runtime_error
+    {
+      public:
+        global_non_const() noexcept
+          : std::runtime_error("a global object must be const")
+        {
+        }
+
+        global_non_const(const global_non_const &) = default;
+        ~global_non_const() noexcept override = default;
+    };
+  }
+
+
+  /// \brief Holds a collection of ChaiScript settings which can be applied to the ChaiScript runtime.
+  ///        Used to implement loadable module support.
+  class Module
+  {
+    public:
+      Module &add(Type_Info ti, std::string name)
+      {
+        m_typeinfos.emplace_back(ti, std::move(name));
+        return *this;
+      }
+
+      Module &add(Type_Conversion d)
+      {
+        m_conversions.push_back(std::move(d));
+        return *this;
+      }
+
+      Module &add(Proxy_Function f, std::string name)
+      {
+        m_funcs.emplace_back(std::move(f), std::move(name));
+        return *this;
+      }
+
+      Module &add_global_const(Boxed_Value t_bv, std::string t_name)
+      {
+        if (!t_bv.is_const())
+        {
+          throw chaiscript::exception::global_non_const();
+        }
+
+        m_globals.emplace_back(std::move(t_bv), std::move(t_name));
+        return *this;
+      }
+
+
+      //Add a bit of ChaiScript to eval during module implementation
+      Module &eval(const std::string &str)
+      {
+        m_evals.push_back(str);
+        return *this;
+      }
+
+      template<typename Eval, typename Engine>
+        void apply(Eval &t_eval, Engine &t_engine) const
+        {
+          apply(m_typeinfos.begin(), m_typeinfos.end(), t_engine);
+          apply(m_funcs.begin(), m_funcs.end(), t_engine);
+          apply_eval(m_evals.begin(), m_evals.end(), t_eval);
+          apply_single(m_conversions.begin(), m_conversions.end(), t_engine);
+          apply_globals(m_globals.begin(), m_globals.end(), t_engine);
+        }
+
+      bool has_function(const Proxy_Function &new_f, const std::string &name)
+      {
+        return std::any_of(m_funcs.begin(), m_funcs.end(), 
+            [&](const std::pair<Proxy_Function, std::string> &existing_f) {
+              return existing_f.second == name && *(existing_f.first) == *(new_f);
+            }
+          );
+      }
+      
+
+    private:
+      std::vector<std::pair<Type_Info, std::string>> m_typeinfos;
+      std::vector<std::pair<Proxy_Function, std::string>> m_funcs;
+      std::vector<std::pair<Boxed_Value, std::string>> m_globals;
+      std::vector<std::string> m_evals;
+      std::vector<Type_Conversion> m_conversions;
+
+      template<typename T, typename InItr>
+        static void apply(InItr begin, const InItr end, T &t) 
+        {
+          for_each(begin, end, 
+              [&t](const auto &obj) {
+                try {
+                  t.add(obj.first, obj.second);
+                } catch (const chaiscript::exception::name_conflict_error &) {
+                  /// \todo Should we throw an error if there's a name conflict 
+                  ///       while applying a module?
+                }
+              }
+            );
+        }
+
+      template<typename T, typename InItr>
+        static void apply_globals(InItr begin, InItr end, T &t)
+        {
+          while (begin != end)
+          {
+            t.add_global_const(begin->first, begin->second);
+            ++begin;
+          }
+        }
+
+      template<typename T, typename InItr>
+        static void apply_single(InItr begin, InItr end, T &t)
+        {
+          while (begin != end)
+          {
+            t.add(*begin);
+            ++begin;
+          }
+        }
+
+      template<typename T, typename InItr>
+        static void apply_eval(InItr begin, InItr end, T &t)
+        {
+          while (begin != end)
+          {
+            t.eval(*begin);
+            ++begin;
+          }
+        }
+  };
+
+  /// Convenience typedef for Module objects to be added to the ChaiScript runtime
+  typedef std::shared_ptr<Module> ModulePtr;
+
+  namespace detail
+  {
+    /// A Proxy_Function implementation that is able to take
+    /// a vector of Proxy_Functions and perform a dispatch on them. It is 
+    /// used specifically in the case of dealing with Function object variables
+    class Dispatch_Function final : public dispatch::Proxy_Function_Base
+    {
+      public:
+        explicit Dispatch_Function(std::vector<Proxy_Function> t_funcs)
+          : Proxy_Function_Base(build_type_infos(t_funcs), calculate_arity(t_funcs)),
+            m_funcs(std::move(t_funcs))
+        {
+        }
+
+        bool operator==(const dispatch::Proxy_Function_Base &rhs) const override
+        {
+          try {
+            const auto &dispatch_fun = dynamic_cast<const Dispatch_Function &>(rhs);
+            return m_funcs == dispatch_fun.m_funcs;
+          } catch (const std::bad_cast &) {
+            return false;
+          }
+        }
+
+        std::vector<Const_Proxy_Function> get_contained_functions() const override
+        {
+          return std::vector<Const_Proxy_Function>(m_funcs.begin(), m_funcs.end());
+        }
+
+
+        static int calculate_arity(const std::vector<Proxy_Function> &t_funcs)
+        {
+          if (t_funcs.empty()) {
+            return -1;
+          }
+
+          const auto arity = t_funcs.front()->get_arity();
+
+          for (const auto &func : t_funcs)
+          {
+            if (arity != func->get_arity())
+            {
+              // The arities in the list do not match, so it's unspecified
+              return -1;
+            }
+          }
+
+          return arity;
+        }
+
+        bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+        {
+          return std::any_of(std::begin(m_funcs), std::end(m_funcs),
+                             [&vals, &t_conversions](const Proxy_Function &f){ return f->call_match(vals, t_conversions); });
+        }
+
+      protected:
+        Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+        {
+          return dispatch::dispatch(m_funcs, params, t_conversions);
+        }
+
+      private:
+        std::vector<Proxy_Function> m_funcs;
+
+        static std::vector<Type_Info> build_type_infos(const std::vector<Proxy_Function> &t_funcs)
+        {
+          auto begin = t_funcs.cbegin();
+          const auto &end = t_funcs.cend();
+
+          if (begin != end)
+          {
+            std::vector<Type_Info> type_infos = (*begin)->get_param_types();
+
+            ++begin;
+
+            bool size_mismatch = false;
+
+            while (begin != end)
+            {
+              std::vector<Type_Info> param_types = (*begin)->get_param_types();
+
+              if (param_types.size() != type_infos.size())
+              {
+                size_mismatch = true;
+              }
+
+              for (size_t i = 0; i < type_infos.size() && i < param_types.size(); ++i)
+              {
+                if (!(type_infos[i] == param_types[i]))
+                {
+                  type_infos[i] = detail::Get_Type_Info<Boxed_Value>::get();
+                }
+              }
+
+              ++begin;
+            }
+
+            assert(!type_infos.empty() && " type_info vector size is < 0, this is only possible if something else is broken");
+
+            if (size_mismatch)
+            {
+              type_infos.resize(1);
+            }
+
+            return type_infos;
+          }
+
+          return std::vector<Type_Info>();
+        }
+    };
+  }
+
+
+  namespace detail
+  {
+    struct Stack_Holder
+    {
+      //template <class T, std::size_t BufSize = sizeof(T)*20000>
+      //  using SmallVector = std::vector<T, short_alloc<T, BufSize>>;
+
+      template <class T>
+        using SmallVector = std::vector<T>;
+      
+
+      typedef SmallVector<std::pair<std::string, Boxed_Value>> Scope;
+      typedef SmallVector<Scope> StackData;
+      typedef SmallVector<StackData> Stacks;
+      typedef SmallVector<Boxed_Value> Call_Param_List;
+      typedef SmallVector<Call_Param_List> Call_Params;
+
+      Stack_Holder()
+      {
+        push_stack();
+        push_call_params();
+      }
+
+      void push_stack_data()
+      {
+        stacks.back().emplace_back();
+//        stacks.back().emplace_back(Scope(scope_allocator));
+      }
+
+      void push_stack()
+      {
+        stacks.emplace_back(1);
+//        stacks.emplace_back(StackData(1, Scope(scope_allocator), stack_data_allocator));
+      }
+
+      void push_call_params()
+      {
+        call_params.emplace_back();
+//        call_params.emplace_back(Call_Param_List(call_param_list_allocator));
+      }
+
+      //Scope::allocator_type::arena_type scope_allocator;
+      //StackData::allocator_type::arena_type stack_data_allocator;
+      //Stacks::allocator_type::arena_type stacks_allocator;
+      //Call_Param_List::allocator_type::arena_type call_param_list_allocator;
+      //Call_Params::allocator_type::arena_type call_params_allocator;
+
+//      Stacks stacks = Stacks(stacks_allocator);
+//      Call_Params call_params = Call_Params(call_params_allocator);
+
+      Stacks stacks;
+      Call_Params call_params;
+
+      int call_depth = 0;
+    };
+
+    /// Main class for the dispatchkit. Handles management
+    /// of the object stack, functions and registered types.
+    class Dispatch_Engine
+    {
+
+      public:
+        typedef std::map<std::string, chaiscript::Type_Info> Type_Name_Map;
+        typedef std::vector<std::pair<std::string, Boxed_Value>> Scope;
+        typedef Stack_Holder::StackData StackData;
+
+        struct State
+        {
+          std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> m_functions;
+          std::vector<std::pair<std::string, Proxy_Function>> m_function_objects;
+          std::vector<std::pair<std::string, Boxed_Value>> m_boxed_functions;
+          std::map<std::string, Boxed_Value> m_global_objects;
+          Type_Name_Map m_types;
+        };
+
+        explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser)
+          : m_stack_holder(),
+            m_parser(parser)
+        {
+        }
+
+        /// \brief casts an object while applying any Dynamic_Conversion available
+        template<typename Type>
+          decltype(auto) boxed_cast(const Boxed_Value &bv) const
+          {
+            Type_Conversions_State state(m_conversions, m_conversions.conversion_saves());
+            return(chaiscript::boxed_cast<Type>(bv, &state));
+          }
+
+        /// Add a new conversion for upcasting to a base class
+        void add(const Type_Conversion &d)
+        {
+          m_conversions.add_conversion(d);
+        }
+
+        /// Add a new named Proxy_Function to the system
+        void add(const Proxy_Function &f, const std::string &name)
+        {
+          add_function(f, name);
+        }
+
+        /// Set the value of an object, by name. If the object
+        /// is not available in the current scope it is created
+        void add(Boxed_Value obj, const std::string &name)
+        {
+          auto &stack = get_stack_data();
+
+          for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem)
+          {
+            auto itr = std::find_if(stack_elem->begin(), stack_elem->end(),
+                [&](const std::pair<std::string, Boxed_Value> &o) {
+                  return o.first == name;
+                });
+
+            if (itr != stack_elem->end())
+            {
+              itr->second = std::move(obj);
+              return;
+            }
+          }
+
+          add_object(name, std::move(obj));
+        }
+
+        /// Adds a named object to the current scope
+        /// \warning This version does not check the validity of the name
+        /// it is meant for internal use only
+        Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder)
+        {
+          auto &stack_elem = get_stack_data(t_holder).back();
+
+          if (std::any_of(stack_elem.begin(), stack_elem.end(),
+              [&](const std::pair<std::string, Boxed_Value> &o) {
+                return o.first == t_name;
+              }))
+          {
+            throw chaiscript::exception::name_conflict_error(t_name);
+          }
+
+          stack_elem.emplace_back(t_name, std::move(obj));
+          return stack_elem.back().second;
+        }
+
+
+        /// Adds a named object to the current scope
+        /// \warning This version does not check the validity of the name
+        /// it is meant for internal use only
+        void add_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder)
+        {
+          auto &stack_elem = get_stack_data(t_holder).back();
+
+          if (std::any_of(stack_elem.begin(), stack_elem.end(),
+              [&](const std::pair<std::string, Boxed_Value> &o) {
+                return o.first == t_name;
+              }))
+          {
+            throw chaiscript::exception::name_conflict_error(t_name);
+          }
+
+          stack_elem.emplace_back(t_name, std::move(obj));
+        }
+
+
+        /// Adds a named object to the current scope
+        /// \warning This version does not check the validity of the name
+        /// it is meant for internal use only
+        void add_object(const std::string &name, Boxed_Value obj)
+        {
+          add_object(name, std::move(obj), get_stack_holder());
+        }
+
+        /// Adds a new global shared object, between all the threads
+        void add_global_const(const Boxed_Value &obj, const std::string &name)
+        {
+          if (!obj.is_const())
+          {
+            throw chaiscript::exception::global_non_const();
+          }
+
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
+          {
+            throw chaiscript::exception::name_conflict_error(name);
+          } else {
+            m_state.m_global_objects.insert(std::make_pair(name, obj));
+          }
+        }
+
+        /// Adds a new global (non-const) shared object, between all the threads
+        Boxed_Value add_global_no_throw(const Boxed_Value &obj, const std::string &name)
+        {
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          const auto itr = m_state.m_global_objects.find(name);
+          if (itr == m_state.m_global_objects.end())
+          {
+            m_state.m_global_objects.insert(std::make_pair(name, obj));
+            return obj;
+          } else {
+            return itr->second;
+          }
+        }
+
+
+        /// Adds a new global (non-const) shared object, between all the threads
+        void add_global(const Boxed_Value &obj, const std::string &name)
+        {
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
+          {
+            throw chaiscript::exception::name_conflict_error(name);
+          } else {
+            m_state.m_global_objects.insert(std::make_pair(name, obj));
+          }
+        }
+
+        /// Updates an existing global shared object or adds a new global shared object if not found
+        void set_global(const Boxed_Value &obj, const std::string &name)
+        {
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          const auto itr = m_state.m_global_objects.find(name);
+          if (itr != m_state.m_global_objects.end())
+          {
+            itr->second.assign(obj);
+          } else {
+            m_state.m_global_objects.insert(std::make_pair(name, obj));
+          }
+        }
+
+        /// Adds a new scope to the stack
+        void new_scope()
+        {
+          new_scope(*m_stack_holder);
+        }
+
+        /// Pops the current scope from the stack
+        void pop_scope()
+        {
+          pop_scope(*m_stack_holder);
+        }
+
+        /// Adds a new scope to the stack
+        static void new_scope(Stack_Holder &t_holder)
+        {
+          t_holder.push_stack_data();
+          t_holder.push_call_params();
+        }
+
+        /// Pops the current scope from the stack
+        static void pop_scope(Stack_Holder &t_holder)
+        {
+          t_holder.call_params.pop_back();
+          StackData &stack = get_stack_data(t_holder);
+
+          assert(!stack.empty());
+
+          stack.pop_back();
+        }
+
+
+        /// Pushes a new stack on to the list of stacks
+        static void new_stack(Stack_Holder &t_holder)
+        {
+          // add a new Stack with 1 element
+          t_holder.push_stack();
+        }
+
+        static void pop_stack(Stack_Holder &t_holder)
+        {
+          t_holder.stacks.pop_back();
+        }
+
+        /// Searches the current stack for an object of the given name
+        /// includes a special overload for the _ place holder object to
+        /// ensure that it is always in scope.
+        Boxed_Value get_object(const std::string &name, std::atomic_uint_fast32_t &t_loc, Stack_Holder &t_holder) const
+        {
+          enum class Loc : uint_fast32_t {
+            located    = 0x80000000,
+            is_local   = 0x40000000,
+            stack_mask = 0x0FFF0000,
+            loc_mask   = 0x0000FFFF
+          };
+
+          uint_fast32_t loc = t_loc;
+
+          if (loc == 0)
+          {
+            auto &stack = get_stack_data(t_holder);
+
+            // Is it in the stack?
+            for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem)
+            {
+              for (auto s = stack_elem->begin(); s != stack_elem->end(); ++s )
+              {
+                if (s->first == name) {
+                  t_loc = static_cast<uint_fast32_t>(std::distance(stack.rbegin(), stack_elem) << 16)
+                              | static_cast<uint_fast32_t>(std::distance(stack_elem->begin(), s))
+                              | static_cast<uint_fast32_t>(Loc::located)
+                              | static_cast<uint_fast32_t>(Loc::is_local);
+                  return s->second;
+                }
+              }
+            }
+
+            t_loc = static_cast<uint_fast32_t>(Loc::located);
+          } else if ((loc & static_cast<uint_fast32_t>(Loc::is_local)) != 0u) {
+            auto &stack = get_stack_data(t_holder);
+
+            return stack[stack.size() - 1 - ((loc & static_cast<uint_fast32_t>(Loc::stack_mask)) >> 16)][loc & static_cast<uint_fast32_t>(Loc::loc_mask)].second;
+          }
+
+          // Is the value we are looking for a global or function?
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          const auto itr = m_state.m_global_objects.find(name);
+          if (itr != m_state.m_global_objects.end())
+          {
+            return itr->second;
+          }
+
+          // no? is it a function object?
+          auto obj = get_function_object_int(name, loc);
+          if (obj.first != loc) { t_loc = uint_fast32_t(obj.first); }
+
+          return obj.second;
+
+        }
+
+        /// Registers a new named type
+        void add(const Type_Info &ti, const std::string &name)
+        {
+          add_global_const(const_var(ti), name + "_type");
+
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          m_state.m_types.insert(std::make_pair(name, ti));
+        }
+
+        /// Returns the type info for a named type
+        Type_Info get_type(const std::string &name, bool t_throw = true) const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          const auto itr = m_state.m_types.find(name);
+
+          if (itr != m_state.m_types.end())
+          {
+            return itr->second;
+          }
+
+          if (t_throw) {
+            throw std::range_error("Type Not Known");
+          } else {
+            return Type_Info();
+          }
+        }
+
+        /// Returns the registered name of a known type_info object
+        /// compares the "bare_type_info" for the broadest possible
+        /// match
+        std::string get_type_name(const Type_Info &ti) const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          for (const auto & elem : m_state.m_types)
+          {
+            if (elem.second.bare_equal(ti))
+            {
+              return elem.first;
+            }
+          }
+
+          return ti.bare_name();
+        }
+
+        /// Return all registered types
+        std::vector<std::pair<std::string, Type_Info> > get_types() const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          return std::vector<std::pair<std::string, Type_Info> >(m_state.m_types.begin(), m_state.m_types.end());
+        }
+
+        std::shared_ptr<std::vector<Proxy_Function>> get_method_missing_functions() const
+        {
+          uint_fast32_t method_missing_loc = m_method_missing_loc;
+          auto method_missing_funs = get_function("method_missing", method_missing_loc);
+          if (method_missing_funs.first != method_missing_loc) { 
+            m_method_missing_loc = uint_fast32_t(method_missing_funs.first); 
+          }
+
+          return std::move(method_missing_funs.second);
+        }
+
+
+        /// Return a function by name
+        std::pair<size_t, std::shared_ptr<std::vector< Proxy_Function>>> get_function(const std::string &t_name, const size_t t_hint) const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          const auto &funs = get_functions_int();
+
+          auto itr = find_keyed_value(funs, t_name, t_hint);
+
+          if (itr != funs.end())
+          {
+            return std::make_pair(std::distance(funs.begin(), itr), itr->second);
+          } else {
+            return std::make_pair(size_t(0), std::make_shared<std::vector<Proxy_Function>>());
+          }
+        }
+
+        /// \returns a function object (Boxed_Value wrapper) if it exists
+        /// \throws std::range_error if it does not
+        Boxed_Value get_function_object(const std::string &t_name) const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          return get_function_object_int(t_name, 0).second;
+        }
+
+        /// \returns a function object (Boxed_Value wrapper) if it exists
+        /// \throws std::range_error if it does not
+        /// \warn does not obtain a mutex lock. \sa get_function_object for public version
+        std::pair<size_t, Boxed_Value> get_function_object_int(const std::string &t_name, const size_t t_hint) const
+        {
+          const auto &funs = get_boxed_functions_int();
+
+          auto itr = find_keyed_value(funs, t_name, t_hint);
+
+          if (itr != funs.end())
+          {
+            return std::make_pair(std::distance(funs.begin(), itr), itr->second);
+          } else {
+            throw std::range_error("Object not found: " + t_name);
+          }
+        }
+
+
+        /// Return true if a function exists
+        bool function_exists(const std::string &name) const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          const auto &functions = get_functions_int();
+          return find_keyed_value(functions, name) != functions.end();
+        }
+
+        /// \returns All values in the local thread state in the parent scope, or if it doesn't exist,
+        ///          the current scope.
+        std::map<std::string, Boxed_Value> get_parent_locals() const
+        {
+          auto &stack = get_stack_data();
+          if (stack.size() > 1)
+          {
+            return std::map<std::string, Boxed_Value>(stack[1].begin(), stack[1].end());
+          } else {
+            return std::map<std::string, Boxed_Value>(stack[0].begin(), stack[0].end());
+          }
+        }
+
+        /// \returns All values in the local thread state, added through the add() function
+        std::map<std::string, Boxed_Value> get_locals() const
+        {
+          auto &stack = get_stack_data();
+          auto &scope = stack.front();
+          return std::map<std::string, Boxed_Value>(scope.begin(), scope.end());
+        }
+
+        /// \brief Sets all of the locals for the current thread state.
+        ///
+        /// \param[in] t_locals The map<name, value> set of variables to replace the current state with
+        ///
+        /// Any existing locals are removed and the given set of variables is added
+        void set_locals(const std::map<std::string, Boxed_Value> &t_locals)
+        {
+          auto &stack = get_stack_data();
+          auto &scope = stack.front();
+          scope.assign(t_locals.begin(), t_locals.end());
+        }
+
+
+
+        ///
+        /// Get a map of all objects that can be seen from the current scope in a scripting context
+        ///
+        std::map<std::string, Boxed_Value> get_scripting_objects() const
+        {
+          const Stack_Holder &s = *m_stack_holder;
+
+          // We don't want the current context, but one up if it exists
+          const StackData &stack = (s.stacks.size()==1)?(s.stacks.back()):(s.stacks[s.stacks.size()-2]);
+
+          std::map<std::string, Boxed_Value> retval;
+
+          // note: map insert doesn't overwrite existing values, which is why this works
+          for (auto itr = stack.rbegin(); itr != stack.rend(); ++itr)
+          {
+            retval.insert(itr->begin(), itr->end());
+          } 
+
+          // add the global values
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+          retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end());
+
+          return retval;
+        }
+
+
+        ///
+        /// Get a map of all functions that can be seen from a scripting context
+        ///
+        std::map<std::string, Boxed_Value> get_function_objects() const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          const auto &funs = get_function_objects_int();
+
+          std::map<std::string, Boxed_Value> objs;
+
+          for (const auto & fun : funs)
+          {
+            objs.insert(std::make_pair(fun.first, const_var(fun.second)));
+          }
+
+          return objs;
+        }
+
+
+        /// Get a vector of all registered functions
+        std::vector<std::pair<std::string, Proxy_Function > > get_functions() const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          std::vector<std::pair<std::string, Proxy_Function> > rets;
+
+          const auto &functions = get_functions_int();
+
+          for (const auto & function : functions)
+          {
+            for (const auto & internal_func : *function.second)
+            {
+              rets.emplace_back(function.first, internal_func);
+            }
+          }
+
+          return rets;
+        }
+
+
+        const Type_Conversions &conversions() const
+        {
+          return m_conversions;
+        }
+
+        static bool is_attribute_call(const std::vector<Proxy_Function> &t_funs, const std::vector<Boxed_Value> &t_params,
+            bool t_has_params, const Type_Conversions_State &t_conversions)
+        {
+          if (!t_has_params || t_params.empty()) {
+            return false;
+          }
+
+          return std::any_of(std::begin(t_funs), std::end(t_funs),
+              [&](const auto &fun) {
+                return fun->is_attribute_function() && fun->compare_first_type(t_params[0], t_conversions);
+              }
+            );
+
+        }
+
+#ifdef CHAISCRIPT_MSVC
+// MSVC is unable to recognize that "rethrow_exception" causes the function to return
+// so we must disable it here.
+#pragma warning(push)
+#pragma warning(disable : 4715)
+#endif
+        Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params, bool t_has_params,
+                                const Type_Conversions_State &t_conversions)
+        {
+          uint_fast32_t loc = t_loc;
+          const auto funs = get_function(t_name, loc);
+          if (funs.first != loc) { t_loc = uint_fast32_t(funs.first); }
+
+          const auto do_attribute_call = 
+            [this](int l_num_params, const std::vector<Boxed_Value> &l_params, const std::vector<Proxy_Function> &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value
+            {
+              std::vector<Boxed_Value> attr_params{l_params.begin(), l_params.begin() + l_num_params};
+              Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions);
+              if (l_num_params < int(l_params.size()) || bv.get_type_info().bare_equal(user_type<dispatch::Proxy_Function_Base>())) {
+                struct This_Foist {
+                  This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) : m_e(e) {
+                    m_e.get().new_scope();
+                    m_e.get().add_object("__this", t_bv);
+                  }
+
+                  ~This_Foist() {
+                    m_e.get().pop_scope();
+                  }
+
+                  std::reference_wrapper<Dispatch_Engine> m_e;
+                };
+
+                This_Foist fi(*this, l_params.front());
+
+                try {
+                  auto func = boxed_cast<const dispatch::Proxy_Function_Base *>(bv);
+                  try {
+                    return (*func)({l_params.begin() + l_num_params, l_params.end()}, l_conversions);
+                  } catch (const chaiscript::exception::bad_boxed_cast &) {
+                  } catch (const chaiscript::exception::arity_error &) {
+                  } catch (const chaiscript::exception::guard_error &) {
+                  }
+                  throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, 
+                      std::vector<Const_Proxy_Function>{boxed_cast<Const_Proxy_Function>(bv)});
+                } catch (const chaiscript::exception::bad_boxed_cast &) {
+                  // unable to convert bv into a Proxy_Function_Base
+                  throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, 
+                      std::vector<Const_Proxy_Function>(l_funs.begin(), l_funs.end()));
+                }
+              } else {
+                return bv;
+              }
+            };
+
+          if (is_attribute_call(*funs.second, params, t_has_params, t_conversions)) {
+            return do_attribute_call(1, params, *funs.second, t_conversions);
+          } else {
+            std::exception_ptr except;
+
+            if (!funs.second->empty()) {
+              try {
+                return dispatch::dispatch(*funs.second, params, t_conversions);
+              } catch(chaiscript::exception::dispatch_error&) {
+                except = std::current_exception();
+              }
+            }
+
+            // If we get here we know that either there was no method with that name,
+            // or there was no matching method
+
+            const auto functions = [&]()->std::vector<Proxy_Function> {
+              std::vector<Proxy_Function> fs;
+
+              const auto method_missing_funs = get_method_missing_functions();
+
+              for (const auto &f : *method_missing_funs)
+              {
+                if(f->compare_first_type(params[0], t_conversions)) {
+                  fs.push_back(f);
+                }
+              }
+
+              return fs;
+            }();
+
+
+
+            const bool is_no_param = [&]()->bool{
+              for (const auto &f : functions) {
+                if (f->get_arity() != 2) {
+                  return false;
+                }
+              }
+              return true;
+            }();
+
+            if (!functions.empty()) {
+              try {
+                if (is_no_param) {
+                  std::vector<Boxed_Value> tmp_params(params);
+                  tmp_params.insert(tmp_params.begin() + 1, var(t_name));
+                  return do_attribute_call(2, tmp_params, functions, t_conversions);
+                } else {
+                  return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector<Boxed_Value>(params.begin()+1, params.end()))}, t_conversions);
+                }
+              } catch (const dispatch::option_explicit_set &e) {
+                throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()), 
+                    e.what());
+              }
+            }
+
+            // If we get all the way down here we know there was no "method_missing"
+            // method at all.
+            if (except) {
+              std::rethrow_exception(except);
+            } else {
+              throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()));
+            }
+          }
+        }
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+
+
+
+        Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params,
+            const Type_Conversions_State &t_conversions) const
+        {
+          uint_fast32_t loc = t_loc;
+          const auto funs = get_function(t_name, loc);
+          if (funs.first != loc) { t_loc = uint_fast32_t(funs.first);
+}
+          return dispatch::dispatch(*funs.second, params, t_conversions);
+        }
+
+
+        /// Dump object info to stdout
+        void dump_object(const Boxed_Value &o) const
+        {
+          std::cout << (o.is_const()?"const ":"") << type_name(o) << '\n';
+        }
+
+        /// Dump type info to stdout
+        void dump_type(const Type_Info &type) const
+        {
+          std::cout << (type.is_const()?"const ":"") << get_type_name(type);
+        }
+
+        /// Dump function to stdout
+        void dump_function(const std::pair<const std::string, Proxy_Function > &f) const
+        {
+          std::vector<Type_Info> params = f.second->get_param_types();
+
+          dump_type(params.front());
+          std::cout << " " << f.first << "(";
+
+          for (std::vector<Type_Info>::const_iterator itr = params.begin() + 1;
+              itr != params.end();
+              )
+          {
+            dump_type(*itr);
+            ++itr;
+
+            if (itr != params.end())
+            {
+              std::cout << ", ";
+            }
+          }
+
+          std::cout << ") \n";
+        }
+
+        /// Returns true if a call can be made that consists of the first parameter
+        /// (the function) with the remaining parameters as its arguments.
+        Boxed_Value call_exists(const std::vector<Boxed_Value> &params) const
+        {
+          if (params.empty())
+          {
+            throw chaiscript::exception::arity_error(static_cast<int>(params.size()), 1);
+          }
+
+          const Const_Proxy_Function &f = this->boxed_cast<Const_Proxy_Function>(params[0]);
+          const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves());
+
+          return const_var(f->call_match(std::vector<Boxed_Value>(params.begin() + 1, params.end()), convs));
+        }
+
+        /// Dump all system info to stdout
+        void dump_system() const
+        {
+          std::cout << "Registered Types: \n";
+          std::vector<std::pair<std::string, Type_Info> > types = get_types();
+          for (std::vector<std::pair<std::string, Type_Info> >::const_iterator itr = types.begin();
+              itr != types.end();
+              ++itr)
+          {
+            std::cout << itr->first << ": ";
+            std::cout << itr->second.bare_name();
+            std::cout << '\n';
+          }
+
+          std::cout << '\n';  
+          std::vector<std::pair<std::string, Proxy_Function > > funcs = get_functions();
+
+          std::cout << "Functions: \n";
+          for (std::vector<std::pair<std::string, Proxy_Function > >::const_iterator itr = funcs.begin();
+              itr != funcs.end();
+              ++itr)
+          {
+            dump_function(*itr);
+          }
+          std::cout << '\n';
+        }
+
+        /// return true if the Boxed_Value matches the registered type by name
+        bool is_type(const Boxed_Value &r, const std::string &user_typename) const
+        {
+          try {
+            if (get_type(user_typename).bare_equal(r.get_type_info()))
+            {
+              return true;
+            }
+          } catch (const std::range_error &) {
+          }
+
+          try {
+            const dispatch::Dynamic_Object &d = boxed_cast<const dispatch::Dynamic_Object &>(r);
+            return d.get_type_name() == user_typename;
+          } catch (const std::bad_cast &) {
+          }
+
+          return false;
+        }
+
+        std::string type_name(const Boxed_Value &obj) const
+        {
+          return get_type_name(obj.get_type_info());
+        }
+
+        State get_state() const
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          return m_state;
+        }
+
+        void set_state(const State &t_state)
+        {
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          m_state = t_state;
+        }
+
+        static void save_function_params(Stack_Holder &t_s, std::initializer_list<Boxed_Value> t_params)
+        {
+          t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params);
+        }
+
+        static void save_function_params(Stack_Holder &t_s, std::vector<Boxed_Value> &&t_params)
+        {
+          for (auto &&param : t_params)
+          {
+            t_s.call_params.back().insert(t_s.call_params.back().begin(), std::move(param));
+          }
+        }
+
+        static void save_function_params(Stack_Holder &t_s, const std::vector<Boxed_Value> &t_params)
+        {
+          t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params.begin(), t_params.end());
+        }
+
+        void save_function_params(std::initializer_list<Boxed_Value> t_params)
+        {
+          save_function_params(*m_stack_holder, t_params);
+        }
+
+        void save_function_params(std::vector<Boxed_Value> &&t_params)
+        {
+          save_function_params(*m_stack_holder, std::move(t_params));
+        }
+
+        void save_function_params(const std::vector<Boxed_Value> &t_params)
+        {
+          save_function_params(*m_stack_holder, t_params);
+        }
+
+        void new_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves)
+        {
+          if (t_s.call_depth == 0)
+          {
+            m_conversions.enable_conversion_saves(t_saves, true);
+          }
+
+          ++t_s.call_depth;
+
+          save_function_params(m_conversions.take_saves(t_saves));
+        }
+
+        void pop_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves)
+        {
+          --t_s.call_depth;
+
+          assert(t_s.call_depth >= 0);
+
+          if (t_s.call_depth == 0)
+          {
+            t_s.call_params.back().clear();
+            m_conversions.enable_conversion_saves(t_saves, false);
+          }
+        }
+
+        void new_function_call()
+        {
+          new_function_call(*m_stack_holder, m_conversions.conversion_saves());
+        }
+
+        void pop_function_call()
+        {
+          pop_function_call(*m_stack_holder, m_conversions.conversion_saves());
+        }
+
+        Stack_Holder &get_stack_holder()
+        {
+          return *m_stack_holder;
+        }
+
+        /// Returns the current stack
+        /// make const/non const versions
+        const StackData &get_stack_data() const
+        {
+          return m_stack_holder->stacks.back();
+        }
+
+        static StackData &get_stack_data(Stack_Holder &t_holder)
+        {
+          return t_holder.stacks.back();
+        }
+
+        StackData &get_stack_data()
+        {
+          return m_stack_holder->stacks.back();
+        }
+
+        parser::ChaiScript_Parser_Base &get_parser()
+        {
+          return m_parser.get();
+        }
+
+      private:
+
+        const std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int() const
+        {
+          return m_state.m_boxed_functions;
+        }
+
+        std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int() 
+        {
+          return m_state.m_boxed_functions;
+        }
+
+        const std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int() const
+        {
+          return m_state.m_function_objects;
+        }
+
+        std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int() 
+        {
+          return m_state.m_function_objects;
+        }
+
+        const std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int() const
+        {
+          return m_state.m_functions;
+        }
+
+        std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int() 
+        {
+          return m_state.m_functions;
+        }
+
+        static bool function_less_than(const Proxy_Function &lhs, const Proxy_Function &rhs)
+        {
+
+          auto dynamic_lhs(std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(lhs));
+          auto dynamic_rhs(std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(rhs));
+
+          if (dynamic_lhs && dynamic_rhs)
+          {
+            if (dynamic_lhs->get_guard())
+            {
+              return dynamic_rhs->get_guard() ? false : true;
+            } else {
+              return false;
+            }
+          }
+
+          if (dynamic_lhs && !dynamic_rhs)
+          {
+            return false;
+          }
+
+          if (!dynamic_lhs && dynamic_rhs)
+          {
+            return true;
+          }
+
+          const auto &lhsparamtypes = lhs->get_param_types();
+          const auto &rhsparamtypes = rhs->get_param_types();
+
+          const auto lhssize = lhsparamtypes.size();
+          const auto rhssize = rhsparamtypes.size();
+
+          static const auto boxed_type = user_type<Boxed_Value>();
+          static const auto boxed_pod_type = user_type<Boxed_Number>();
+
+          for (size_t i = 1; i < lhssize && i < rhssize; ++i)
+          {
+            const Type_Info &lt = lhsparamtypes[i];
+            const Type_Info &rt = rhsparamtypes[i];
+
+            if (lt.bare_equal(rt) && lt.is_const() == rt.is_const())
+            {
+              continue; // The first two types are essentially the same, next iteration
+            }
+
+            // const is after non-const for the same type
+            if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const())
+            {
+              return false;
+            }
+
+            if (lt.bare_equal(rt) && !lt.is_const())
+            {
+              return true;
+            }
+
+            // boxed_values are sorted last
+            if (lt.bare_equal(boxed_type))
+            {
+              return false;
+            }
+
+            if (rt.bare_equal(boxed_type))
+            {
+              return true;
+            }
+
+            if (lt.bare_equal(boxed_pod_type))
+            {
+              return false;
+            }
+
+            if (rt.bare_equal(boxed_pod_type))
+            {
+              return true;
+            }
+
+            // otherwise, we want to sort by typeid
+            return lt < rt;
+          }
+
+          return false;
+        }
+
+
+
+        template<typename Container, typename Key, typename Value>
+          static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value)
+          {
+            auto itr = find_keyed_value(t_c, t_key);
+
+            if (itr == t_c.end()) {
+              t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here
+              t_c.emplace_back(t_key, std::forward<Value>(t_value));
+            } else {
+              typedef typename Container::value_type value_type;
+              *itr = value_type(t_key, std::forward<Value>(t_value));
+            }
+          }
+
+        template<typename Container, typename Key>
+        static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key)
+          {
+            return std::find_if(t_c.begin(), t_c.end(), 
+                [&t_key](const typename Container::value_type &o) {
+                  return o.first == t_key;
+                });
+          }
+
+        template<typename Container, typename Key>
+        static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key)
+          {
+            return std::find_if(t_c.begin(), t_c.end(), 
+                [&t_key](const typename Container::value_type &o) {
+                  return o.first == t_key;
+                });
+          }
+
+        template<typename Container, typename Key>
+        static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint)
+          {
+            if (t_c.size() > t_hint && t_c[t_hint].first == t_key) {
+              return std::next(t_c.begin(), static_cast<typename std::iterator_traits<typename Container::const_iterator>::difference_type>(t_hint));
+            } else {
+              return find_keyed_value(t_c, t_key);
+            }
+          }
+
+
+        /// Implementation detail for adding a function. 
+        /// \throws exception::name_conflict_error if there's a function matching the given one being added
+        void add_function(const Proxy_Function &t_f, const std::string &t_name)
+        {
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+          auto &funcs = get_functions_int();
+
+          auto itr = find_keyed_value(funcs, t_name);
+
+          Proxy_Function new_func =
+            [&]() -> Proxy_Function {
+              if (itr != funcs.end())
+              {
+                auto vec = *itr->second;
+                for (const auto &func : vec)
+                {
+                  if ((*t_f) == *(func))
+                  {
+                    throw chaiscript::exception::name_conflict_error(t_name);
+                  }
+                }
+
+                vec.reserve(vec.size() + 1); // tightly control vec growth
+                vec.push_back(t_f);
+                std::stable_sort(vec.begin(), vec.end(), &function_less_than);
+                itr->second = std::make_shared<std::vector<Proxy_Function>>(vec);
+                return std::make_shared<Dispatch_Function>(std::move(vec));
+              } else if (t_f->has_arithmetic_param()) {
+                // if the function is the only function but it also contains
+                // arithmetic operators, we must wrap it in a dispatch function
+                // to allow for automatic arithmetic type conversions
+                std::vector<Proxy_Function> vec({t_f});
+                funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(vec));
+                return std::make_shared<Dispatch_Function>(std::move(vec));
+              } else {
+                funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(std::initializer_list<Proxy_Function>({t_f})));
+                return t_f;
+              }
+            }();
+
+          add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func));
+          add_keyed_value(get_function_objects_int(), t_name, std::move(new_func));
+        }
+
+        mutable chaiscript::detail::threading::shared_mutex m_mutex;
+
+
+        Type_Conversions m_conversions;
+        chaiscript::detail::threading::Thread_Storage<Stack_Holder> m_stack_holder;
+        std::reference_wrapper<parser::ChaiScript_Parser_Base> m_parser;
+
+        mutable std::atomic_uint_fast32_t m_method_missing_loc = {0};
+
+        State m_state;
+    };
+
+    class Dispatch_State
+    {
+      public:
+        explicit Dispatch_State(Dispatch_Engine &t_engine)
+          : m_engine(t_engine),
+            m_stack_holder(t_engine.get_stack_holder()),
+            m_conversions(t_engine.conversions(), t_engine.conversions().conversion_saves())
+        {
+        }
+
+        Dispatch_Engine *operator->() const {
+          return &m_engine.get();
+        }
+
+        Dispatch_Engine &operator*() const {
+          return m_engine.get();
+        }
+
+        Stack_Holder &stack_holder() const {
+          return m_stack_holder.get();
+        }
+
+        const Type_Conversions_State &conversions() const {
+          return m_conversions;
+        }
+
+        Type_Conversions::Conversion_Saves &conversion_saves() const {
+          return m_conversions.saves();
+        }
+
+        Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj) const {
+          return m_engine.get().add_get_object(t_name, std::move(obj), m_stack_holder.get());
+        }
+
+        void add_object(const std::string &t_name, Boxed_Value obj) const {
+          return m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get());
+        }
+
+        Boxed_Value get_object(const std::string &t_name, std::atomic_uint_fast32_t &t_loc) const {
+          return m_engine.get().get_object(t_name, t_loc, m_stack_holder.get());
+        }
+
+      private:
+        std::reference_wrapper<Dispatch_Engine> m_engine;
+        std::reference_wrapper<Stack_Holder> m_stack_holder;
+        Type_Conversions_State m_conversions;
+    };
+  }
+}
+
+#endif
+
+

+ 131 - 0
chaiscript/dispatchkit/dynamic_object.hpp

@@ -0,0 +1,131 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_DYNAMIC_OBJECT_HPP_
+#define CHAISCRIPT_DYNAMIC_OBJECT_HPP_
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "boxed_value.hpp"
+
+namespace chaiscript {
+class Type_Conversions;
+namespace dispatch {
+class Proxy_Function_Base;
+}  // namespace dispatch
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  namespace dispatch
+  {
+    struct option_explicit_set : std::runtime_error {
+      explicit option_explicit_set(const std::string &t_param_name)
+        : std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist")
+      {
+
+      }
+
+      option_explicit_set(const option_explicit_set &) = default;
+
+      ~option_explicit_set() noexcept override = default;
+    };
+
+    class Dynamic_Object
+    {
+      public:
+        explicit Dynamic_Object(std::string t_type_name)
+          : m_type_name(std::move(t_type_name)), m_option_explicit(false)
+        {
+        }
+
+        Dynamic_Object() = default;
+
+        bool is_explicit() const
+        {
+          return m_option_explicit;
+        }
+
+        void set_explicit(const bool t_explicit)
+        {
+          m_option_explicit = t_explicit;
+        }
+
+        std::string get_type_name() const
+        {
+          return m_type_name;
+        }
+
+        const Boxed_Value &operator[](const std::string &t_attr_name) const
+        {
+          return get_attr(t_attr_name);
+        }
+
+        Boxed_Value &operator[](const std::string &t_attr_name)
+        {
+          return get_attr(t_attr_name);
+        }
+
+        const Boxed_Value &get_attr(const std::string &t_attr_name) const
+        {
+          auto a = m_attrs.find(t_attr_name);
+
+          if (a != m_attrs.end()) {
+            return a->second;
+          } else {
+            throw std::range_error("Attr not found '" + t_attr_name + "' and cannot be added to const obj");
+          }
+        }
+
+        bool has_attr(const std::string &t_attr_name) const {
+          return m_attrs.find(t_attr_name) != m_attrs.end();
+        }
+
+        Boxed_Value &get_attr(const std::string &t_attr_name)
+        {
+          return m_attrs[t_attr_name];
+        }
+
+        Boxed_Value &method_missing(const std::string &t_method_name)
+        {
+          if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) {
+            throw option_explicit_set(t_method_name);
+          }
+
+          return get_attr(t_method_name);
+        }
+
+        const Boxed_Value &method_missing(const std::string &t_method_name) const
+        {
+          if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) {
+            throw option_explicit_set(t_method_name);
+          }
+
+          return get_attr(t_method_name);
+        }
+
+        std::map<std::string, Boxed_Value> get_attrs() const
+        {
+          return m_attrs;
+        }
+
+      private:
+        const std::string m_type_name = "";
+        bool m_option_explicit = false;
+
+        std::map<std::string, Boxed_Value> m_attrs;
+    };
+
+  }
+}
+#endif
+

+ 237 - 0
chaiscript/dispatchkit/dynamic_object_detail.hpp

@@ -0,0 +1,237 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_
+#define CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_
+
+#include <cassert>
+#include <map>
+#include <memory>
+#include <string>
+#include <typeinfo>
+#include <utility>
+#include <vector>
+
+#include "../chaiscript_defines.hpp"
+#include "boxed_cast.hpp"
+#include "boxed_cast_helper.hpp"
+#include "boxed_value.hpp"
+#include "proxy_functions.hpp"
+#include "type_info.hpp"
+#include "dynamic_object.hpp"
+
+namespace chaiscript {
+class Type_Conversions;
+namespace dispatch {
+class Proxy_Function_Base;
+}  // namespace dispatch
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  namespace dispatch
+  {
+    namespace detail
+    {
+      /// A Proxy_Function implementation designed for calling a function
+      /// that is automatically guarded based on the first param based on the
+      /// param's type name
+      class Dynamic_Object_Function final : public Proxy_Function_Base
+      {
+        public:
+          Dynamic_Object_Function(
+              std::string t_type_name,
+              const Proxy_Function &t_func,
+              bool t_is_attribute = false)
+            : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()),
+              m_type_name(std::move(t_type_name)), m_func(t_func), m_doti(user_type<Dynamic_Object>()),
+              m_is_attribute(t_is_attribute)
+          {
+            assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0)
+                && "Programming error, Dynamic_Object_Function must have at least one parameter (this)");
+          }
+
+          Dynamic_Object_Function(
+              std::string t_type_name,
+              const Proxy_Function &t_func,
+              const Type_Info &t_ti,
+              bool t_is_attribute = false)
+            : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()),
+              m_type_name(std::move(t_type_name)), m_func(t_func), m_ti(t_ti.is_undef()?nullptr:new Type_Info(t_ti)), m_doti(user_type<Dynamic_Object>()),
+              m_is_attribute(t_is_attribute)
+          {
+            assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0)
+                && "Programming error, Dynamic_Object_Function must have at least one parameter (this)");
+          }
+
+
+          Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete;
+          Dynamic_Object_Function(Dynamic_Object_Function &) = delete;
+
+          bool operator==(const Proxy_Function_Base &f) const override
+          {
+            if (const auto *df = dynamic_cast<const Dynamic_Object_Function *>(&f))
+            {
+              return df->m_type_name == m_type_name && (*df->m_func) == (*m_func);
+            } else {
+              return false;
+            }
+          }
+
+          bool is_attribute_function() const override { return m_is_attribute; } 
+
+          bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+          {
+            if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions))
+            {
+              return m_func->call_match(vals, t_conversions);
+            } else {
+              return false;
+            }
+          }
+
+          std::vector<Const_Proxy_Function> get_contained_functions() const override
+          {
+            return {m_func};
+          }
+
+        protected:
+          Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+          {
+            if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions))
+            {
+              return (*m_func)(params, t_conversions);
+            } else {
+              throw exception::guard_error();
+            } 
+          }
+
+          bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const override
+          {
+            return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions);
+          }
+
+        private:
+          static std::vector<Type_Info> build_param_types(
+              const std::vector<Type_Info> &t_inner_types, const Type_Info& t_objectti)
+          {
+            std::vector<Type_Info> types(t_inner_types);
+
+            assert(types.size() > 1);
+            //assert(types[1].bare_equal(user_type<Boxed_Value>()));
+            types[1] = t_objectti;
+            return types;
+          }
+
+          bool dynamic_object_typename_match(const Boxed_Value &bv, const std::string &name,
+              const std::unique_ptr<Type_Info> &ti, const Type_Conversions_State &t_conversions) const
+          {
+            if (bv.get_type_info().bare_equal(m_doti))
+            {
+              try {
+                const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions);
+                return name == "Dynamic_Object" || d.get_type_name() == name;
+              } catch (const std::bad_cast &) {
+                return false;
+              } 
+            } else {
+              if (ti)
+              {
+                return bv.get_type_info().bare_equal(*ti);
+              } else {
+                return false;
+              }
+            }
+
+          }
+
+          bool dynamic_object_typename_match(const std::vector<Boxed_Value> &bvs, const std::string &name,
+              const std::unique_ptr<Type_Info> &ti, const Type_Conversions_State &t_conversions) const
+          {
+            if (!bvs.empty())
+            {
+              return dynamic_object_typename_match(bvs[0], name, ti, t_conversions);
+            } else {
+              return false;
+            }
+          }
+
+          std::string m_type_name;
+          Proxy_Function m_func;
+          std::unique_ptr<Type_Info> m_ti;
+          const Type_Info m_doti;
+          const bool m_is_attribute;
+      };
+
+
+      /**
+       * A Proxy_Function implementation designed for creating a new
+       * Dynamic_Object
+       * that is automatically guarded based on the first param based on the
+       * param's type name
+       */
+      class Dynamic_Object_Constructor final : public Proxy_Function_Base
+      {
+        public:
+          Dynamic_Object_Constructor(
+              std::string t_type_name,
+              const Proxy_Function &t_func)
+            : Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1),
+              m_type_name(std::move(t_type_name)), m_func(t_func)
+          {
+            assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0)
+                && "Programming error, Dynamic_Object_Function must have at least one parameter (this)");
+          }
+
+          static std::vector<Type_Info> build_type_list(const std::vector<Type_Info> &tl)
+          {
+            auto begin = tl.begin();
+            auto end = tl.end();
+
+            if (begin != end)
+            {
+              ++begin;
+            }
+
+            return std::vector<Type_Info>(begin, end);
+          }
+
+          bool operator==(const Proxy_Function_Base &f) const override
+          {
+            const Dynamic_Object_Constructor *dc = dynamic_cast<const Dynamic_Object_Constructor*>(&f);
+            return (dc != nullptr) && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func);
+          }
+
+          bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+          {
+            std::vector<Boxed_Value> new_vals{Boxed_Value(Dynamic_Object(m_type_name))};
+            new_vals.insert(new_vals.end(), vals.begin(), vals.end());
+
+            return m_func->call_match(new_vals, t_conversions);
+          }
+
+        protected:
+          Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+          {
+            auto bv = Boxed_Value(Dynamic_Object(m_type_name), true);
+            std::vector<Boxed_Value> new_params{bv};
+            new_params.insert(new_params.end(), params.begin(), params.end());
+
+            (*m_func)(new_params, t_conversions);
+
+            return bv;
+          }
+
+        private:
+          const std::string m_type_name;
+          const Proxy_Function m_func;
+
+      };
+    }
+  }
+}
+#endif
+

+ 117 - 0
chaiscript/dispatchkit/exception_specification.hpp

@@ -0,0 +1,117 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_
+#define CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_
+
+#include <memory>
+
+#include "../chaiscript_defines.hpp"
+#include "boxed_cast.hpp"
+
+namespace chaiscript {
+class Boxed_Value;
+namespace exception {
+class bad_boxed_cast;
+}  // namespace exception
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  namespace detail
+  {
+    struct Exception_Handler_Base
+    {
+      virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) = 0;
+
+      virtual ~Exception_Handler_Base() = default;
+
+      protected:
+        template<typename T>
+        static void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine)
+        {
+          try { T t = t_engine.boxed_cast<T>(bv); throw t; } catch (const chaiscript::exception::bad_boxed_cast &) {}
+        }
+    };
+
+    template<typename ... T>
+      struct Exception_Handler_Impl : Exception_Handler_Base
+      {
+        void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) override
+        {
+          (void)std::initializer_list<int>{(throw_type<T>(bv, t_engine), 0)...};
+        }
+      };
+  }
+
+  /// \brief Used in the automatic unboxing of exceptions thrown during script evaluation
+  ///
+  /// Exception specifications allow the user to tell ChaiScript what possible exceptions are expected from the script
+  /// being executed. Exception_Handler objects are created with the chaiscript::exception_specification() function.
+  ///
+  /// Example:
+  /// \code
+  /// chaiscript::ChaiScript chai;
+  ///
+  /// try {
+  ///   chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification<int, double, float, const std::string &, const std::exception &>());
+  /// } catch (const double e) {
+  /// } catch (int) {
+  /// } catch (float) {
+  /// } catch (const std::string &) {
+  /// } catch (const std::exception &e) {
+  ///   // This is the one what will be called in the specific throw() above
+  /// }
+  /// \endcode
+  ///
+  /// It is recommended that if catching the generic \c std::exception& type that you specifically catch
+  /// the chaiscript::exception::eval_error type, so that there is no confusion.
+  ///
+  /// \code
+  /// try {
+  ///   chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification<const std::exception &>());
+  /// } catch (const chaiscript::exception::eval_error &) {
+  ///   // Error in script parsing / execution
+  /// } catch (const std::exception &e) {
+  ///   // Error explicitly thrown from script
+  /// }
+  /// \endcode
+  ///
+  /// Similarly, if you are using the ChaiScript::eval form that unboxes the return value, then chaiscript::exception::bad_boxed_cast
+  /// should be handled as well.
+  /// 
+  /// \code
+  /// try {
+  ///   chai.eval<int>("1.0", chaiscript::exception_specification<const std::exception &>());
+  /// } catch (const chaiscript::exception::eval_error &) {
+  ///   // Error in script parsing / execution
+  /// } catch (const chaiscript::exception::bad_boxed_cast &) {
+  ///   // Error unboxing return value
+  /// } catch (const std::exception &e) {
+  ///   // Error explicitly thrown from script
+  /// }
+  /// \endcode
+  ///
+  /// \sa chaiscript::exception_specification for creation of chaiscript::Exception_Handler objects
+  /// \sa \ref exceptions
+  typedef std::shared_ptr<detail::Exception_Handler_Base> Exception_Handler;
+
+  /// \brief creates a chaiscript::Exception_Handler which handles one type of exception unboxing
+  /// \sa \ref exceptions
+  template<typename ... T>
+  Exception_Handler exception_specification()
+  {
+    return std::make_shared<detail::Exception_Handler_Impl<T...>>();
+  }
+}
+
+
+#endif
+

+ 132 - 0
chaiscript/dispatchkit/function_call.hpp

@@ -0,0 +1,132 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_FUNCTION_CALL_HPP_
+#define CHAISCRIPT_FUNCTION_CALL_HPP_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "boxed_cast.hpp"
+#include "function_call_detail.hpp"
+#include "proxy_functions.hpp"
+#include "callable_traits.hpp"
+
+namespace chaiscript {
+class Boxed_Value;
+class Type_Conversions_State;
+namespace detail {
+template <typename T> struct Cast_Helper;
+}  // namespace detail
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  namespace dispatch
+  {
+    /// Build a function caller that knows how to dispatch on a set of functions
+    /// example:
+    /// std::function<void (int)> f =
+    ///      build_function_caller(dispatchkit.get_function("print"));
+    /// \returns A std::function object for dispatching
+    /// \param[in] funcs the set of functions to dispatch on.
+    template<typename FunctionType>
+      std::function<FunctionType> functor(const std::vector<Const_Proxy_Function> &funcs, const Type_Conversions_State *t_conversions)
+      {
+        const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(),
+            [](const Const_Proxy_Function &f) {
+              return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity<FunctionType>::arity;
+            });
+
+        if (!has_arity_match) {
+          throw exception::bad_boxed_cast(user_type<Const_Proxy_Function>(), typeid(std::function<FunctionType>));
+        }
+
+        FunctionType *p=nullptr;
+        return detail::build_function_caller_helper(p, funcs, t_conversions);
+      }
+
+    /// Build a function caller for a particular Proxy_Function object
+    /// useful in the case that a function is being pass out from scripting back
+    /// into code
+    /// example:
+    /// void my_function(Proxy_Function f)
+    /// {
+    ///   std::function<void (int)> local_f =
+    ///      build_function_caller(f);
+    /// }
+    /// \returns A std::function object for dispatching
+    /// \param[in] func A function to execute.
+    template<typename FunctionType>
+      std::function<FunctionType> functor(Const_Proxy_Function func, const Type_Conversions_State *t_conversions)
+      {
+        return functor<FunctionType>(std::vector<Const_Proxy_Function>({std::move(func)}), t_conversions);
+      }
+
+    /// Helper for automatically unboxing a Boxed_Value that contains a function object
+    /// and creating a typesafe C++ function caller from it.
+    template<typename FunctionType>
+      std::function<FunctionType> functor(const Boxed_Value &bv, const Type_Conversions_State *t_conversions)
+      {
+        return functor<FunctionType>(boxed_cast<Const_Proxy_Function >(bv, t_conversions), t_conversions);
+      }
+  }
+
+  namespace detail{
+    /// Cast helper to handle automatic casting to const std::function &
+    template<typename Signature>
+      struct Cast_Helper<const std::function<Signature> &>
+      {
+        static std::function<Signature> cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions)
+        {
+          if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>()))
+          {
+            return dispatch::functor<Signature>(ob, t_conversions);
+          } else {
+            return Cast_Helper_Inner<const std::function<Signature> &>::cast(ob, t_conversions);
+          }
+        }
+      };
+
+    /// Cast helper to handle automatic casting to std::function
+    template<typename Signature>
+      struct Cast_Helper<std::function<Signature> >
+      {
+        static std::function<Signature> cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions)
+        {
+          if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>()))
+          {
+            return dispatch::functor<Signature>(ob, t_conversions);
+          } else {
+            return Cast_Helper_Inner<std::function<Signature> >::cast(ob, t_conversions);
+          }
+        }
+      };
+
+    /// Cast helper to handle automatic casting to const std::function
+    template<typename Signature>
+      struct Cast_Helper<const std::function<Signature> >
+      {
+        static std::function<Signature> cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions)
+        {
+          if (ob.get_type_info().bare_equal(user_type<Const_Proxy_Function>()))
+          {
+            return dispatch::functor<Signature>(ob, t_conversions);
+          } else {
+            return Cast_Helper_Inner<const std::function<Signature> >::cast(ob, t_conversions);
+          }
+        }
+      };
+  }
+}
+
+#endif
+

+ 171 - 0
chaiscript/dispatchkit/function_call_detail.hpp

@@ -0,0 +1,171 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_
+#define CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "boxed_cast.hpp"
+#include "boxed_number.hpp"
+#include "boxed_value.hpp"
+#include "type_conversions.hpp"
+#include "proxy_functions.hpp"
+
+namespace chaiscript
+{
+  namespace dispatch
+  {
+    namespace detail
+    {
+      /// Internal helper class for handling the return
+      /// value of a build_function_caller
+      template<typename Ret, bool is_arithmetic>
+        struct Function_Caller_Ret
+        {
+          static Ret call(const std::vector<Const_Proxy_Function> &t_funcs, 
+              const std::vector<Boxed_Value> &params, const Type_Conversions_State *t_conversions)
+          {
+            if (t_conversions != nullptr) {
+              return boxed_cast<Ret>(dispatch::dispatch(t_funcs, params, *t_conversions), t_conversions);
+            } else {
+              Type_Conversions conv;
+              Type_Conversions_State state(conv, conv.conversion_saves());
+              return boxed_cast<Ret>(dispatch::dispatch(t_funcs, params, state), t_conversions);
+            }
+          }
+        };
+
+      /**
+       * Specialization for arithmetic return types
+       */
+      template<typename Ret>
+        struct Function_Caller_Ret<Ret, true>
+        {
+          static Ret call(const std::vector<Const_Proxy_Function> &t_funcs, 
+              const std::vector<Boxed_Value> &params, const Type_Conversions_State *t_conversions)
+          {
+            if (t_conversions != nullptr) {
+              return Boxed_Number(dispatch::dispatch(t_funcs, params, *t_conversions)).get_as<Ret>();
+            } else {
+              Type_Conversions conv;
+              Type_Conversions_State state(conv, conv.conversion_saves());
+              return Boxed_Number(dispatch::dispatch(t_funcs, params, state)).get_as<Ret>();
+            }
+          }
+        };
+
+
+      /**
+       * Specialization for void return types
+       */
+      template<>
+        struct Function_Caller_Ret<void, false>
+        {
+          static void call(const std::vector<Const_Proxy_Function> &t_funcs, 
+              const std::vector<Boxed_Value> &params, const Type_Conversions_State *t_conversions)
+          {
+            if (t_conversions != nullptr) {
+              dispatch::dispatch(t_funcs, params, *t_conversions);
+            } else {
+              Type_Conversions conv;
+              Type_Conversions_State state(conv, conv.conversion_saves());
+              dispatch::dispatch(t_funcs, params, state);
+            }
+          }
+        };
+
+      /**
+       * used internally for unwrapping a function call's types
+       */
+      template<typename Ret, typename ... Param>
+        struct Build_Function_Caller_Helper
+        {
+          Build_Function_Caller_Helper(std::vector<Const_Proxy_Function> t_funcs, const Type_Conversions *t_conversions)
+            : m_funcs(std::move(t_funcs)),
+              m_conversions(t_conversions)
+          {
+          }
+
+          template<typename ... P>
+          Ret operator()(P&&  ...  param)
+          {
+            if (m_conversions) {
+              Type_Conversions_State state(*m_conversions, m_conversions->conversion_saves());
+              return Function_Caller_Ret<Ret, std::is_arithmetic<Ret>::value && !std::is_same<Ret, bool>::value>::call(m_funcs, { 
+                  box<P>(std::forward<P>(param))...
+                  }, &state
+                  );
+            } else {
+              return Function_Caller_Ret<Ret, std::is_arithmetic<Ret>::value && !std::is_same<Ret, bool>::value>::call(m_funcs, { 
+                  box<P>(std::forward<P>(param))...
+                  }, nullptr
+                  );
+            }
+
+          }
+
+          template<typename P, typename Q>
+          static auto box(Q&& q) -> typename std::enable_if<std::is_reference<P>::value&&!std::is_same<chaiscript::Boxed_Value, typename std::remove_const<typename std::remove_reference<P>::type>::type>::value, Boxed_Value>::type
+          {
+            return Boxed_Value(std::ref(std::forward<Q>(q)));
+          }
+
+          template<typename P, typename Q>
+          static auto box(Q&& q) -> typename std::enable_if<!std::is_reference<P>::value&&!std::is_same<chaiscript::Boxed_Value, typename std::remove_const<typename std::remove_reference<P>::type>::type>::value, Boxed_Value>::type
+          {
+            return Boxed_Value(std::forward<Q>(q));
+          }
+
+          template<typename P>
+          static Boxed_Value box(Boxed_Value bv)
+          {
+            return bv;
+          }
+
+
+          std::vector<Const_Proxy_Function> m_funcs;
+          const Type_Conversions *m_conversions;
+        };
+
+
+
+      /// \todo what happens if t_conversions is deleted out from under us?!
+      template<typename Ret, typename ... Params>
+        std::function<Ret (Params...)> build_function_caller_helper(Ret (Params...), const std::vector<Const_Proxy_Function> &funcs, const Type_Conversions_State *t_conversions)
+        {
+          /*
+          if (funcs.size() == 1)
+          {
+            std::shared_ptr<const Proxy_Function_Impl<Ret (Params...)>> pfi = 
+              std::dynamic_pointer_cast<const Proxy_Function_Impl<Ret (Params...)> >
+                (funcs[0]);
+
+            if (pfi)
+            {
+              return pfi->internal_function();
+            } 
+            // looks like this either wasn't a Proxy_Function_Impl or the types didn't match
+            // we cannot make any other guesses or assumptions really, so continuing
+          }
+*/
+
+          return std::function<Ret (Params...)>(Build_Function_Caller_Helper<Ret, Params...>(funcs, t_conversions?t_conversions->get():nullptr));
+        }
+    }
+  }
+}
+
+#endif
+

+ 256 - 0
chaiscript/dispatchkit/handle_return.hpp

@@ -0,0 +1,256 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_HANDLE_RETURN_HPP_
+#define CHAISCRIPT_HANDLE_RETURN_HPP_
+
+#include <functional>
+#include <memory>
+#include <type_traits>
+
+#include "boxed_number.hpp"
+#include "boxed_value.hpp"
+
+namespace chaiscript {
+class Boxed_Number;
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  namespace dispatch
+  {
+    template<class T, class U> class Proxy_Function_Callable_Impl;
+    template<class T> class Assignable_Proxy_Function_Impl;
+
+    namespace detail
+    {
+      /// Used internally for handling a return value from a Proxy_Function call
+      template<typename Ret>
+        struct Handle_Return
+        {
+          template<typename T,
+                   typename = typename std::enable_if<std::is_pod<typename std::decay<T>::type>::value>::type>
+          static Boxed_Value handle(T r)
+          {
+            return Boxed_Value(std::move(r), true);
+          }
+
+          template<typename T,
+                   typename = typename std::enable_if<!std::is_pod<typename std::decay<T>::type>::value>::type>
+          static Boxed_Value handle(T &&r)
+          {
+            return Boxed_Value(std::make_shared<T>(std::forward<T>(r)), true);
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<const std::function<Ret> &>
+        {
+          static Boxed_Value handle(const std::function<Ret> &f) {
+            return Boxed_Value(
+                chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret, std::function<Ret>>>(f)
+              );
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<std::function<Ret>> : Handle_Return<const std::function<Ret> &>
+        {
+        };
+
+      template<typename Ret>
+        struct Handle_Return<const std::shared_ptr<std::function<Ret>>>
+        {
+          static Boxed_Value handle(const std::shared_ptr<std::function<Ret>> &f) {
+            return Boxed_Value(
+                chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>(std::ref(*f),f)
+                );
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<const std::shared_ptr<std::function<Ret>> &> : Handle_Return<const std::shared_ptr<std::function<Ret>>>
+        {
+        };
+
+      template<typename Ret>
+        struct Handle_Return<std::shared_ptr<std::function<Ret>>> : Handle_Return<const std::shared_ptr<std::function<Ret>>>
+        {
+        };
+
+      template<typename Ret>
+        struct Handle_Return<std::function<Ret> &>
+        {
+          static Boxed_Value handle(std::function<Ret> &f) {
+            return Boxed_Value(
+                chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Assignable_Proxy_Function_Impl<Ret>>(std::ref(f),
+                  std::shared_ptr<std::function<Ret>>())
+              );
+          }
+
+          static Boxed_Value handle(const std::function<Ret> &f) {
+            return Boxed_Value(
+                chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret, std::function<Ret>>>(f)
+              );
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<Ret *&>
+        {
+          static Boxed_Value handle(Ret *p)
+          {
+            return Boxed_Value(p, true);
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<const Ret *&>
+        {
+          static Boxed_Value handle(const Ret *p)
+          {
+            return Boxed_Value(p, true);
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<Ret *>
+        {
+          static Boxed_Value handle(Ret *p)
+          {
+            return Boxed_Value(p, true);
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<const Ret *>
+        {
+          static Boxed_Value handle(const Ret *p)
+          {
+            return Boxed_Value(p, true);
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<std::shared_ptr<Ret> &>
+        {
+          static Boxed_Value handle(const std::shared_ptr<Ret> &r)
+          {
+            return Boxed_Value(r, true);
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<std::shared_ptr<Ret>> : Handle_Return<std::shared_ptr<Ret> &>
+        {
+        };
+
+      template<typename Ret>
+        struct Handle_Return<const std::shared_ptr<Ret> &> : Handle_Return<std::shared_ptr<Ret> &>
+        {
+        };
+
+
+      template<typename Ret>
+        struct Handle_Return<std::unique_ptr<Ret>> : Handle_Return<std::unique_ptr<Ret> &>
+        {
+          static Boxed_Value handle(std::unique_ptr<Ret> &&r)
+          {
+            return Boxed_Value(std::move(r), true);
+          }
+        };
+
+
+
+      template<typename Ret>
+        struct Handle_Return<const Ret &>
+        {
+          static Boxed_Value handle(const Ret &r)
+          {
+            return Boxed_Value(std::cref(r), true);
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<const Ret>
+        {
+          static Boxed_Value handle(Ret r)
+          {
+            return Boxed_Value(std::move(r));
+          }
+        };
+
+      template<typename Ret>
+        struct Handle_Return<Ret &>
+        {
+          static Boxed_Value handle(Ret &r)
+          {
+            return Boxed_Value(std::ref(r));
+          }
+        };
+
+      template<>
+        struct Handle_Return<Boxed_Value>
+        {
+          static Boxed_Value handle(const Boxed_Value &r)
+          {
+            return r;
+          }
+        };
+
+      template<>
+        struct Handle_Return<const Boxed_Value> : Handle_Return<Boxed_Value>
+        {
+        };
+
+      template<>
+        struct Handle_Return<Boxed_Value &> : Handle_Return<Boxed_Value>
+        {
+        };
+
+      template<>
+        struct Handle_Return<const Boxed_Value &> : Handle_Return<Boxed_Value>
+        {
+        };
+
+      /**
+       * Used internally for handling a return value from a Proxy_Function call
+       */
+      template<>
+        struct Handle_Return<Boxed_Number>
+        {
+          static Boxed_Value handle(const Boxed_Number &r)
+          {
+            return r.bv;
+          }
+        };
+
+      template<>
+        struct Handle_Return<const Boxed_Number> : Handle_Return<Boxed_Number>
+        {
+        };
+
+
+      /**
+       * Used internally for handling a return value from a Proxy_Function call
+       */
+      template<>
+        struct Handle_Return<void>
+        {
+          static Boxed_Value handle()
+          {
+            return void_var();
+          }
+        };
+    }
+  }
+}
+
+#endif

+ 224 - 0
chaiscript/dispatchkit/operators.hpp

@@ -0,0 +1,224 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_OPERATORS_HPP_
+#define CHAISCRIPT_OPERATORS_HPP_
+
+#include "../chaiscript_defines.hpp"
+#include "register_function.hpp"
+
+namespace chaiscript 
+{
+  namespace bootstrap
+  {
+    namespace operators
+    {
+      template<typename T>
+        void assign(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs = rhs;}), "=");
+        }
+
+      template<typename T>
+        void assign_bitwise_and(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs &= rhs;}), "&=");
+        }
+
+      template<typename T>
+        void assign_xor(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs ^= rhs;}), "^=");
+        }
+
+      template<typename T>
+        void assign_bitwise_or(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs |= rhs;}), "|=");
+        }
+
+      template<typename T>
+        void assign_difference(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs -= rhs;}), "-=");
+        }
+
+      template<typename T>
+        void assign_left_shift(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs <<= rhs;}), "<<=");
+        }
+
+      template<typename T>
+        void assign_product(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs <<= rhs;}), "*=");
+        }
+
+      template<typename T>
+        void assign_quotient(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs /= rhs;}), "/=");
+        }
+
+      template<typename T>
+        void assign_remainder(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs %= rhs;}), "%=");
+        }
+
+      template<typename T>
+        void assign_right_shift(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs >>= rhs;}), ">>=");
+        }
+
+      template<typename T>
+        void assign_sum(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs += rhs;}), "+=");
+        }
+
+      template<typename T>
+        void prefix_decrement(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs)->T&{return --lhs;}), "--");
+        }
+
+      template<typename T>
+        void prefix_increment(Module& m)
+        {
+          m.add(chaiscript::fun([](T &lhs)->T&{return ++lhs;}), "++");
+        }
+
+      template<typename T>
+        void equal(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs==rhs;}), "==");
+        }
+
+      template<typename T>
+        void greater_than(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>rhs;}), ">");
+        }
+
+      template<typename T>
+        void greater_than_equal(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>=rhs;}), ">=");
+        }
+
+      template<typename T>
+        void less_than(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs<rhs;}), "<");
+        }
+
+      template<typename T>
+        void less_than_equal(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs<=rhs;}), "<=");
+        }
+
+      template<typename T>
+        void logical_compliment(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs){return !lhs;}), "!");
+        }
+
+      template<typename T>
+        void not_equal(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs!=rhs;}), "!=");
+        }
+
+      template<typename T>
+        void addition(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs+rhs;}), "+");
+        }
+
+      template<typename T>
+        void unary_plus(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs){return +lhs;}), "+");
+        }
+
+      template<typename T>
+        void subtraction(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs-rhs;}), "-");
+        }
+
+      template<typename T>
+        void unary_minus(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs){return -lhs;}), "-");
+        }
+
+      template<typename T>
+        void bitwise_and(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs&rhs;}), "&");
+        }
+
+      template<typename T>
+        void bitwise_compliment(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs){return ~lhs;}), "~");
+        }
+
+      template<typename T>
+        void bitwise_xor(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs^rhs;}), "^");
+        }
+
+      template<typename T>
+        void bitwise_or(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs|rhs;}), "|");
+        }
+
+      template<typename T>
+        void division(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs/rhs;}), "/");
+        }
+
+      template<typename T>
+        void left_shift(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs<<rhs;}), "<<");
+        }
+
+      template<typename T>
+        void multiplication(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs*rhs;}), "*");
+        }
+
+      template<typename T>
+        void remainder(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs%rhs;}), "%");
+        }
+
+      template<typename T>
+        void right_shift(Module& m)
+        {
+          m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>>rhs;}), ">>");
+        }
+    }
+  }
+}
+
+#endif

+ 56 - 0
chaiscript/dispatchkit/proxy_constructors.hpp

@@ -0,0 +1,56 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_
+#define CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_
+
+#include "proxy_functions.hpp"
+
+namespace chaiscript
+{
+  namespace dispatch
+  {
+    namespace detail
+    {
+
+      template<typename Class, typename ... Params  >
+        Proxy_Function build_constructor_(Class (*)(Params...))
+        {
+          auto call = dispatch::detail::Constructor<Class, Params...>();
+
+          return Proxy_Function(
+            chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<std::shared_ptr<Class> (Params...), decltype(call)>>(call));
+        }
+    }
+  }
+
+
+  /// \brief Generates a constructor function for use with ChaiScript
+  /// 
+  /// \tparam T The signature of the constructor to generate. In the form of: ClassType (ParamType1, ParamType2, ...)
+  /// 
+  /// Example:
+  /// \code
+  ///    chaiscript::ChaiScript chai;
+  ///    // Create a new function that creates a MyClass object using the (int, float) constructor
+  ///    // and call that function "MyClass" so that it appears as a normal constructor to the user.
+  ///    chai.add(constructor<MyClass (int, float)>(), "MyClass");
+  /// \endcode
+  template<typename T>
+    Proxy_Function constructor()
+    {
+      T *f = nullptr;
+      return (dispatch::detail::build_constructor_(f));
+    }
+
+}
+
+#endif
+

+ 989 - 0
chaiscript/dispatchkit/proxy_functions.hpp

@@ -0,0 +1,989 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_PROXY_FUNCTIONS_HPP_
+#define CHAISCRIPT_PROXY_FUNCTIONS_HPP_
+
+
+#include <cassert>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include <iterator>
+
+#include "../chaiscript_defines.hpp"
+#include "boxed_cast.hpp"
+#include "boxed_value.hpp"
+#include "proxy_functions_detail.hpp"
+#include "type_info.hpp"
+#include "dynamic_object.hpp"
+
+namespace chaiscript {
+class Type_Conversions;
+namespace exception {
+class bad_boxed_cast;
+struct arity_error;
+}  // namespace exception
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  class Boxed_Number;
+  struct AST_Node;
+
+  typedef std::unique_ptr<AST_Node> AST_NodePtr;
+
+  namespace dispatch
+  {
+    template<typename FunctionType>
+      std::function<FunctionType> functor(std::shared_ptr<const Proxy_Function_Base> func, const Type_Conversions_State *t_conversions);
+
+    class Param_Types
+    {
+      public:
+        Param_Types()
+          : m_has_types(false),
+            m_doti(user_type<Dynamic_Object>())
+        {}
+
+        explicit Param_Types(std::vector<std::pair<std::string, Type_Info>> t_types)
+          : m_types(std::move(t_types)),
+            m_has_types(false),
+            m_doti(user_type<Dynamic_Object>())
+        {
+          update_has_types();
+        }
+
+        void push_front(std::string t_name, Type_Info t_ti)
+        {
+          m_types.emplace(m_types.begin(), std::move(t_name), t_ti);
+          update_has_types();
+        }
+
+        bool operator==(const Param_Types &t_rhs) const
+        {
+          return m_types == t_rhs.m_types;
+        }
+
+        std::vector<Boxed_Value> convert(std::vector<Boxed_Value> vals, const Type_Conversions_State &t_conversions) const
+        {
+          for (size_t i = 0; i < vals.size(); ++i)
+          {
+            const auto &name = m_types[i].first;
+            if (!name.empty()) {
+              const auto &bv = vals[i];
+
+              if (!bv.get_type_info().bare_equal(m_doti))
+              {
+                const auto &ti = m_types[i].second;
+                if (!ti.is_undef())
+                {
+                  if (!bv.get_type_info().bare_equal(ti)) {
+                    if (t_conversions->converts(ti, bv.get_type_info())) {
+                      try {
+                        // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it
+                        // either way, we are not responsible if it doesn't work
+                        vals[i] = t_conversions->boxed_type_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
+                      } catch (...) {
+                        try {
+                          // try going the other way
+                          vals[i] = t_conversions->boxed_type_down_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
+                        } catch (const chaiscript::detail::exception::bad_any_cast &) {
+                          throw exception::bad_boxed_cast(bv.get_type_info(), *m_types[i].second.bare_type_info());
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+
+          return vals;
+        }
+
+        // first result: is a match
+        // second result: needs conversions
+        std::pair<bool, bool> match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const
+        {
+          bool needs_conversion = false;
+
+          if (!m_has_types) { return std::make_pair(true, needs_conversion); }
+          if (vals.size() != m_types.size()) { return std::make_pair(false, needs_conversion); }
+
+          for (size_t i = 0; i < vals.size(); ++i)
+          {
+            const auto &name = m_types[i].first;
+            if (!name.empty()) {
+              const auto &bv = vals[i];
+
+              if (bv.get_type_info().bare_equal(m_doti))
+              {
+                try {
+                  const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions);
+                  if (!(name == "Dynamic_Object" || d.get_type_name() == name)) {
+                    return std::make_pair(false, false);
+                  }
+                } catch (const std::bad_cast &) {
+                  return std::make_pair(false, false);
+                } 
+              } else {
+                const auto &ti = m_types[i].second;
+                if (!ti.is_undef())
+                {
+                  if (!bv.get_type_info().bare_equal(ti)) {
+                    if (!t_conversions->converts(ti, bv.get_type_info())) {
+                      return std::make_pair(false, false);
+                    } else {
+                      needs_conversion = true;
+                    }
+                  }
+                } else {
+                  return std::make_pair(false, false);
+                }
+              }
+            }
+          }
+
+          return std::make_pair(true, needs_conversion);
+        }
+
+        const std::vector<std::pair<std::string, Type_Info>> &types() const
+        {
+          return m_types;
+        }
+
+      private:
+        void update_has_types()
+        {
+          for (const auto &type : m_types)
+          {
+            if (!type.first.empty())
+            {
+              m_has_types = true;
+              return;
+            }
+          }
+
+          m_has_types = false;
+        }
+
+        std::vector<std::pair<std::string, Type_Info>> m_types;
+        bool m_has_types;
+        Type_Info m_doti;
+
+    };
+
+    /**
+     * Pure virtual base class for all Proxy_Function implementations
+     * Proxy_Functions are a type erasure of type safe C++
+     * function calls. At runtime parameter types are expected to be
+     * tested against passed in types.
+     * Dispatch_Engine only knows how to work with Proxy_Function, no other
+     * function classes.
+     */
+    class Proxy_Function_Base
+    {
+      public:
+        virtual ~Proxy_Function_Base() = default;
+
+        Boxed_Value operator()(const std::vector<Boxed_Value> &params, const chaiscript::Type_Conversions_State &t_conversions) const
+        {
+          if (m_arity < 0 || size_t(m_arity) == params.size()) {
+            return do_call(params, t_conversions);
+          } else {
+            throw exception::arity_error(static_cast<int>(params.size()), m_arity);
+          }
+        }
+
+        /// Returns a vector containing all of the types of the parameters the function returns/takes
+        /// if the function is variadic or takes no arguments (arity of 0 or -1), the returned
+        /// value contains exactly 1 Type_Info object: the return type
+        /// \returns the types of all parameters. 
+        const std::vector<Type_Info> &get_param_types() const { return m_types; }
+
+        virtual bool operator==(const Proxy_Function_Base &) const = 0;
+        virtual bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const = 0;
+
+        virtual bool is_attribute_function() const { return false; }
+
+        bool has_arithmetic_param() const 
+        {
+          return m_has_arithmetic_param;
+        }
+
+        virtual std::vector<std::shared_ptr<const Proxy_Function_Base> > get_contained_functions() const
+        {
+          return std::vector<std::shared_ptr<const Proxy_Function_Base> >();
+        }
+
+        //! Return true if the function is a possible match
+        //! to the passed in values
+        bool filter(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const
+        {
+          assert(m_arity == -1 || (m_arity > 0 && static_cast<int>(vals.size()) == m_arity));
+
+          if (m_arity < 0)
+          {
+            return true;
+          } else if (m_arity > 1) {
+            return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions);
+          } else {
+            return compare_type_to_param(m_types[1], vals[0], t_conversions);
+          }
+        }
+
+        /// \returns the number of arguments the function takes or -1 if it is variadic
+        int get_arity() const
+        {
+          return m_arity;
+        }
+
+        static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions)
+        {
+          if (ti.is_undef() 
+              || ti.bare_equal(user_type<Boxed_Value>())
+              || (!bv.get_type_info().is_undef()
+                && ( (ti.bare_equal(user_type<Boxed_Number>()) && bv.get_type_info().is_arithmetic())
+                  || ti.bare_equal(bv.get_type_info())
+                  || bv.get_type_info().bare_equal(user_type<std::shared_ptr<const Proxy_Function_Base> >())
+                  || t_conversions->converts(ti, bv.get_type_info()) 
+                  )
+                )
+             )
+          {
+            return true;
+          } else {
+            return false;
+          }
+        }
+
+        virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const
+        {
+          return compare_type_to_param(m_types[1], bv, t_conversions);
+        }
+
+      protected:
+        virtual Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const = 0;
+
+        Proxy_Function_Base(std::vector<Type_Info> t_types, int t_arity)
+          : m_types(std::move(t_types)), m_arity(t_arity), m_has_arithmetic_param(false)
+        {
+          for (size_t i = 1; i < m_types.size(); ++i)
+          {
+            if (m_types[i].is_arithmetic())
+            {
+              m_has_arithmetic_param = true;
+              return;
+            }
+          }
+
+        }
+
+
+        static bool compare_types(const std::vector<Type_Info> &tis, const std::vector<Boxed_Value> &bvs, 
+                                  const Type_Conversions_State &t_conversions)
+        {
+          if (tis.size() - 1 != bvs.size())
+          {
+            return false;
+          } else {
+            const size_t size = bvs.size();
+            for (size_t i = 0; i < size; ++i)
+            {
+              if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) { return false;  }
+            }
+          }
+          return true;
+        }
+
+        std::vector<Type_Info> m_types;
+        int m_arity;
+        bool m_has_arithmetic_param;
+    };
+  }
+
+  /// \brief Common typedef used for passing of any registered function in ChaiScript
+  typedef std::shared_ptr<dispatch::Proxy_Function_Base> Proxy_Function;
+
+  /// \brief Const version of Proxy_Function. Points to a const Proxy_Function. This is how most registered functions
+  ///        are handled internally.
+  typedef std::shared_ptr<const dispatch::Proxy_Function_Base> Const_Proxy_Function;
+
+  namespace exception
+  {
+    /// \brief  Exception thrown if a function's guard fails
+    class guard_error : public std::runtime_error
+    {
+      public:
+        guard_error() noexcept
+          : std::runtime_error("Guard evaluation failed")
+        { }
+
+        guard_error(const guard_error &) = default;
+
+        ~guard_error() noexcept override = default;
+    };
+  }
+
+  namespace dispatch
+  {
+    /**
+     * A Proxy_Function implementation that is not type safe, the called function
+     * is expecting a vector<Boxed_Value> that it works with how it chooses.
+     */
+    class Dynamic_Proxy_Function : public Proxy_Function_Base
+    {
+      public:
+        Dynamic_Proxy_Function(
+            const int t_arity,
+            std::shared_ptr<AST_Node> t_parsenode,
+            Param_Types t_param_types = Param_Types(),
+            Proxy_Function t_guard = Proxy_Function())
+          : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity),
+            m_param_types(std::move(t_param_types)),
+            m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode))
+        {
+          // assert(t_parsenode);
+        }
+
+
+        bool operator==(const Proxy_Function_Base &rhs) const override
+        {
+          const Dynamic_Proxy_Function *prhs = dynamic_cast<const Dynamic_Proxy_Function *>(&rhs);
+
+          return this == &rhs
+            || ((prhs != nullptr)
+                && this->m_arity == prhs->m_arity
+                && !this->m_guard && !prhs->m_guard
+                && this->m_param_types == prhs->m_param_types);
+        }
+
+        bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+        {
+          return call_match_internal(vals, t_conversions).first;
+        }
+
+
+        Proxy_Function get_guard() const
+        {
+          return m_guard;
+        }
+
+        bool has_parse_tree() const {
+          return static_cast<bool>(m_parsenode);
+        }
+
+        const AST_Node &get_parse_tree() const
+        {
+          if (m_parsenode) {
+            return *m_parsenode;
+          } else {
+            throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree");
+          }
+        }
+
+
+      protected:
+        bool test_guard(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const
+        {
+          if (m_guard)
+          {
+            try {
+              return boxed_cast<bool>((*m_guard)(params, t_conversions));
+            } catch (const exception::arity_error &) {
+              return false;
+            } catch (const exception::bad_boxed_cast &) {
+              return false;
+            }
+          } else {
+            return true;
+          }
+        }
+
+        // first result: is a match
+        // second result: needs conversions
+        std::pair<bool, bool> call_match_internal(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const
+        {
+          const auto comparison_result = [&](){
+            if (m_arity < 0) {
+              return std::make_pair(true, false);
+            } else if (vals.size() == size_t(m_arity)) {
+              return m_param_types.match(vals, t_conversions);
+            } else {
+              return std::make_pair(false, false);
+            }
+          }();
+
+          return std::make_pair(
+              comparison_result.first && test_guard(vals, t_conversions), 
+              comparison_result.second
+              );
+        }
+
+      private:
+        static std::vector<Type_Info> build_param_type_list(const Param_Types &t_types)
+        {
+          // For the return type
+          std::vector<Type_Info> types{chaiscript::detail::Get_Type_Info<Boxed_Value>::get()};
+
+          for (const auto &t : t_types.types())
+          {
+            if (t.second.is_undef()) {
+              types.push_back(chaiscript::detail::Get_Type_Info<Boxed_Value>::get());
+            } else {
+              types.push_back(t.second);
+            }
+          }
+
+          return types;
+        }
+
+      protected:
+        Param_Types m_param_types;
+
+      private:
+        Proxy_Function m_guard;
+        std::shared_ptr<AST_Node> m_parsenode;
+    };
+
+
+
+    template<typename Callable>
+    class Dynamic_Proxy_Function_Impl final : public Dynamic_Proxy_Function
+    {
+      public:
+        Dynamic_Proxy_Function_Impl(
+            Callable t_f, 
+            int t_arity=-1,
+            std::shared_ptr<AST_Node> t_parsenode = AST_NodePtr(),
+            Param_Types t_param_types = Param_Types(),
+            Proxy_Function t_guard = Proxy_Function())
+          : Dynamic_Proxy_Function(
+                t_arity,
+                std::move(t_parsenode),
+                std::move(t_param_types),
+                std::move(t_guard)
+              ),
+            m_f(std::move(t_f))
+        {
+        }
+
+
+      protected:
+        Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+        {
+          const auto match_results = call_match_internal(params, t_conversions);
+          if (match_results.first)
+          {
+            if (match_results.second) {
+              return m_f(m_param_types.convert(params, t_conversions));
+            } else {
+              return m_f(params);
+            }
+          } else {
+            throw exception::guard_error();
+          }
+        }
+
+      private:
+        Callable m_f;
+    };
+
+    template<typename Callable, typename ... Arg>
+    Proxy_Function make_dynamic_proxy_function(Callable &&c, Arg&& ... a)
+    {
+      return chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Dynamic_Proxy_Function_Impl<Callable>>(
+          std::forward<Callable>(c), std::forward<Arg>(a)...);
+    }
+
+    /// An object used by Bound_Function to represent "_" parameters
+    /// of a binding. This allows for unbound parameters during bind.
+    struct Placeholder_Object
+    {
+    };
+
+    /// An implementation of Proxy_Function that takes a Proxy_Function
+    /// and substitutes bound parameters into the parameter list
+    /// at runtime, when call() is executed.
+    /// it is used for bind(function, param1, _, param2) style calls
+    class Bound_Function final : public Proxy_Function_Base
+    {
+      public:
+        Bound_Function(const Const_Proxy_Function &t_f, 
+            const std::vector<Boxed_Value> &t_args)
+          : Proxy_Function_Base(build_param_type_info(t_f, t_args), (t_f->get_arity()<0?-1:static_cast<int>(build_param_type_info(t_f, t_args).size())-1)),
+            m_f(t_f), m_args(t_args)
+        {
+          assert(m_f->get_arity() < 0 || m_f->get_arity() == static_cast<int>(m_args.size()));
+        }
+
+        bool operator==(const Proxy_Function_Base &t_f) const override
+        {
+          return &t_f == this;
+        }
+
+
+        bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+        {
+          return m_f->call_match(build_param_list(vals), t_conversions);
+        }
+
+        std::vector<Const_Proxy_Function> get_contained_functions() const override
+        {
+          return std::vector<Const_Proxy_Function>{m_f};
+        }
+
+
+        std::vector<Boxed_Value> build_param_list(const std::vector<Boxed_Value> &params) const
+        {
+          auto parg = params.begin();
+          auto barg = m_args.begin();
+
+          std::vector<Boxed_Value> args;
+
+          while (!(parg == params.end() && barg == m_args.end()))
+          {
+            while (barg != m_args.end() 
+                && !(barg->get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get()))
+            {
+              args.push_back(*barg);
+              ++barg;
+            }
+
+            if (parg != params.end())
+            {
+              args.push_back(*parg);
+              ++parg;
+            }
+
+            if (barg != m_args.end() 
+                && barg->get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get())
+            {
+              ++barg;
+            } 
+          }
+          return args;
+        }
+
+
+      protected:
+        static std::vector<Type_Info> build_param_type_info(const Const_Proxy_Function &t_f, 
+            const std::vector<Boxed_Value> &t_args)
+        {
+          assert(t_f->get_arity() < 0 || t_f->get_arity() == static_cast<int>(t_args.size()));
+
+          if (t_f->get_arity() < 0) { return std::vector<Type_Info>(); }
+
+          const auto types = t_f->get_param_types();
+          assert(types.size() == t_args.size() + 1);
+
+          // this analysis warning is invalid in MSVC12 and doesn't exist in MSVC14
+          std::vector<Type_Info> retval{types[0]};
+
+          for (size_t i = 0; i < types.size() - 1; ++i)
+          {
+            if (t_args[i].get_type_info() == chaiscript::detail::Get_Type_Info<Placeholder_Object>::get())
+            {
+              retval.push_back(types[i+1]);
+            }
+          }
+
+          return retval;
+        }
+
+        Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+        {
+          return (*m_f)(build_param_list(params), t_conversions);
+        }
+
+      private:
+        Const_Proxy_Function m_f;
+        std::vector<Boxed_Value> m_args;
+    };
+
+    class Proxy_Function_Impl_Base : public Proxy_Function_Base
+    {
+      public:
+        explicit Proxy_Function_Impl_Base(const std::vector<Type_Info> &t_types)
+          : Proxy_Function_Base(t_types, static_cast<int>(t_types.size()) - 1)
+        {
+        }
+
+        bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+        {
+          return static_cast<int>(vals.size()) == get_arity() 
+            && (compare_types(m_types, vals, t_conversions) && compare_types_with_cast(vals, t_conversions));
+        }
+
+        virtual bool compare_types_with_cast(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const = 0;
+    };
+
+
+
+    /// For any callable object
+    template<typename Func, typename Callable>
+      class Proxy_Function_Callable_Impl final : public Proxy_Function_Impl_Base
+    {
+      public:
+        explicit Proxy_Function_Callable_Impl(Callable f)
+          : Proxy_Function_Impl_Base(detail::build_param_type_list(static_cast<Func *>(nullptr))),
+            m_f(std::move(f))
+        {
+        }
+
+        bool compare_types_with_cast(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+        {
+          return detail::compare_types_cast(static_cast<Func *>(nullptr), vals, t_conversions);
+        }
+
+        bool operator==(const Proxy_Function_Base &t_func) const override
+        {
+          return dynamic_cast<const Proxy_Function_Callable_Impl<Func, Callable> *>(&t_func) != nullptr;
+        }
+
+
+      protected:
+        Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+        {
+          return detail::call_func(detail::Function_Signature<Func>(), m_f, params, t_conversions);
+        }
+
+      private:
+        Callable m_f;
+    };
+
+
+    class Assignable_Proxy_Function : public Proxy_Function_Impl_Base
+    {
+      public:
+        explicit Assignable_Proxy_Function(const std::vector<Type_Info> &t_types)
+          : Proxy_Function_Impl_Base(t_types)
+        {
+        }
+
+        virtual void assign(const std::shared_ptr<const Proxy_Function_Base> &t_rhs) = 0;
+    };
+
+    template<typename Func>
+      class Assignable_Proxy_Function_Impl final : public Assignable_Proxy_Function
+    {
+      public:
+        Assignable_Proxy_Function_Impl(std::reference_wrapper<std::function<Func>> t_f, std::shared_ptr<std::function<Func>> t_ptr)
+          : Assignable_Proxy_Function(detail::build_param_type_list(static_cast<Func *>(nullptr))),
+            m_f(std::move(t_f)), m_shared_ptr_holder(std::move(t_ptr))
+        {
+          assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get());
+        }
+
+        bool compare_types_with_cast(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
+        {
+          return detail::compare_types_cast(static_cast<Func *>(nullptr), vals, t_conversions);
+        }
+
+        bool operator==(const Proxy_Function_Base &t_func) const override
+        {
+          return dynamic_cast<const Assignable_Proxy_Function_Impl<Func> *>(&t_func) != nullptr;
+        }
+
+        std::function<Func> internal_function() const
+        {
+          return m_f.get();
+        }
+
+        void assign(const std::shared_ptr<const Proxy_Function_Base> &t_rhs) override {
+          m_f.get() = dispatch::functor<Func>(t_rhs, nullptr);
+        }
+
+      protected:
+        Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+        {
+          return detail::call_func(detail::Function_Signature<Func>(), m_f.get(), params, t_conversions);
+        }
+
+
+      private:
+        std::reference_wrapper<std::function<Func>> m_f;
+        std::shared_ptr<std::function<Func>> m_shared_ptr_holder;
+    };
+
+
+    /// Attribute getter Proxy_Function implementation
+    template<typename T, typename Class>
+      class Attribute_Access final : public Proxy_Function_Base
+    {
+      public:
+        explicit Attribute_Access(T Class::* t_attr)
+          : Proxy_Function_Base(param_types(), 1),
+            m_attr(t_attr)
+        {
+        }
+
+        bool is_attribute_function() const override { return true; } 
+
+        bool operator==(const Proxy_Function_Base &t_func) const override
+        {
+          const Attribute_Access<T, Class> * aa 
+            = dynamic_cast<const Attribute_Access<T, Class> *>(&t_func);
+
+          if (aa) {
+            return m_attr == aa->m_attr;
+          } else {
+            return false;
+          }
+        }
+
+        bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &) const override
+        {
+          if (vals.size() != 1)
+          {
+            return false;
+          }
+
+          return vals[0].get_type_info().bare_equal(user_type<Class>());
+        }
+
+      protected:
+        Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
+        {
+          const Boxed_Value &bv = params[0];
+          if (bv.is_const())
+          {
+            const Class *o = boxed_cast<const Class *>(bv, &t_conversions);
+            return do_call_impl<T>(o);
+          } else {
+            Class *o = boxed_cast<Class *>(bv, &t_conversions);
+            return do_call_impl<T>(o);
+          }
+        }
+
+      private:
+        template<typename Type>
+        auto do_call_impl(Class *o) const -> std::enable_if_t<std::is_pointer<Type>::value, Boxed_Value>
+        {
+          return detail::Handle_Return<Type>::handle(o->*m_attr);
+        }
+
+        template<typename Type>
+        auto do_call_impl(const Class *o) const -> std::enable_if_t<std::is_pointer<Type>::value, Boxed_Value>
+        {
+          return detail::Handle_Return<const Type>::handle(o->*m_attr);
+        }
+
+        template<typename Type>
+        auto do_call_impl(Class *o) const -> std::enable_if_t<!std::is_pointer<Type>::value, Boxed_Value>
+        {
+          return detail::Handle_Return<typename std::add_lvalue_reference<Type>::type>::handle(o->*m_attr);
+        }
+
+        template<typename Type>
+        auto do_call_impl(const Class *o) const -> std::enable_if_t<!std::is_pointer<Type>::value, Boxed_Value>
+        {
+          return detail::Handle_Return<typename std::add_lvalue_reference<typename std::add_const<Type>::type>::type>::handle(o->*m_attr);
+        }
+
+
+
+        static std::vector<Type_Info> param_types()
+        {
+          return {user_type<T>(), user_type<Class>()};
+        }
+
+        T Class::* m_attr;
+    };
+  }
+
+  namespace exception
+  {
+     /// \brief Exception thrown in the case that a method dispatch fails
+     ///        because no matching function was found
+     /// 
+     /// May be thrown due to an arity_error, a guard_error or a bad_boxed_cast
+     /// exception
+    class dispatch_error : public std::runtime_error
+    {
+      public:
+        dispatch_error(std::vector<Boxed_Value> t_parameters, 
+            std::vector<Const_Proxy_Function> t_functions)
+          : std::runtime_error("Error with function dispatch"), parameters(std::move(t_parameters)), functions(std::move(t_functions))
+        {
+        }
+
+        dispatch_error(std::vector<Boxed_Value> t_parameters, 
+            std::vector<Const_Proxy_Function> t_functions,
+            const std::string &t_desc)
+          : std::runtime_error(t_desc), parameters(std::move(t_parameters)), functions(std::move(t_functions))
+        {
+        }
+
+
+        dispatch_error(const dispatch_error &) = default;
+        ~dispatch_error() noexcept override = default;
+
+        std::vector<Boxed_Value> parameters;
+        std::vector<Const_Proxy_Function> functions;
+    };
+  } 
+
+  namespace dispatch
+  {
+    namespace detail 
+    {
+      template<typename FuncType>
+        bool types_match_except_for_arithmetic(const FuncType &t_func, const std::vector<Boxed_Value> &plist,
+            const Type_Conversions_State &t_conversions)
+        {
+          const std::vector<Type_Info> &types = t_func->get_param_types();
+
+          if (t_func->get_arity() == -1) { return false; }
+
+          assert(plist.size() == types.size() - 1);
+
+          return std::mismatch(plist.begin(), plist.end(),
+                               types.begin()+1,
+                               [&](const Boxed_Value &bv, const Type_Info &ti) {
+                                 return Proxy_Function_Base::compare_type_to_param(ti, bv, t_conversions)
+                                       || (bv.get_type_info().is_arithmetic() && ti.is_arithmetic());
+                               }
+              ) == std::make_pair(plist.end(), types.end());
+        }
+
+      template<typename InItr, typename Funcs>
+        Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const std::vector<Boxed_Value> &plist, 
+            const Type_Conversions_State &t_conversions, const Funcs &t_funcs)
+        {
+          InItr matching_func(end);
+
+          while (begin != end)
+          {
+            if (types_match_except_for_arithmetic(begin->second, plist, t_conversions))
+            {
+              if (matching_func == end)
+              {
+                matching_func = begin;
+              } else {
+                // handle const members vs non-const member, which is not really ambiguous
+                const auto &mat_fun_param_types = matching_func->second->get_param_types();
+                const auto &next_fun_param_types = begin->second->get_param_types();
+
+                if (plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) {
+                  matching_func = begin; // keep the new one, the const/non-const matchup is correct
+                } else if (!plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) {
+                  // keep the old one, it has a better const/non-const matchup
+                } else {
+                  // ambiguous function call
+                  throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
+                }
+              }
+            }
+
+            ++begin;
+          }
+
+          if (matching_func == end)
+          {
+            // no appropriate function to attempt arithmetic type conversion on
+            throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
+          }
+
+
+          std::vector<Boxed_Value> newplist;
+          newplist.reserve(plist.size());
+
+          const std::vector<Type_Info> &tis = matching_func->second->get_param_types();
+          std::transform(tis.begin() + 1, tis.end(),
+                         plist.begin(),
+                         std::back_inserter(newplist),
+                         [](const Type_Info &ti, const Boxed_Value &param) -> Boxed_Value {
+                           if (ti.is_arithmetic() && param.get_type_info().is_arithmetic()
+                               && param.get_type_info() != ti) {
+                             return Boxed_Number(param).get_as(ti).bv;
+                           } else {
+                             return param;
+                           }
+                         }
+                       );
+
+          try {
+            return (*(matching_func->second))(newplist, t_conversions);
+          } catch (const exception::bad_boxed_cast &) {
+            //parameter failed to cast
+          } catch (const exception::arity_error &) {
+            //invalid num params
+          } catch (const exception::guard_error &) {
+            //guard failed to allow the function to execute
+          }
+
+          throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(t_funcs.begin(), t_funcs.end()));
+
+        }
+    }
+
+    /// Take a vector of functions and a vector of parameters. Attempt to execute
+    /// each function against the set of parameters, in order, until a matching
+    /// function is found or throw dispatch_error if no matching function is found
+    template<typename Funcs>
+      Boxed_Value dispatch(const Funcs &funcs,
+          const std::vector<Boxed_Value> &plist, const Type_Conversions_State &t_conversions)
+      {
+        std::vector<std::pair<size_t, const Proxy_Function_Base *>> ordered_funcs;
+        ordered_funcs.reserve(funcs.size());
+
+        for (const auto &func : funcs)
+        {
+          const auto arity = func->get_arity();
+
+          if (arity == -1)
+          {
+            ordered_funcs.emplace_back(plist.size(), func.get());
+          } else if (arity == static_cast<int>(plist.size())) {
+            size_t numdiffs = 0;
+            for (size_t i = 0; i < plist.size(); ++i)
+            {
+              if (!func->get_param_types()[i+1].bare_equal(plist[i].get_type_info()))
+              {
+                ++numdiffs;
+              }
+            }
+            ordered_funcs.emplace_back(numdiffs, func.get());
+          }
+        }
+
+
+        for (size_t i = 0; i <= plist.size(); ++i)
+        {
+          for (const auto &func : ordered_funcs )
+          {
+            try {
+              if (func.first == i && (i == 0 || func.second->filter(plist, t_conversions)))
+              {
+                return (*(func.second))(plist, t_conversions);
+              }
+            } catch (const exception::bad_boxed_cast &) {
+              //parameter failed to cast, try again
+            } catch (const exception::arity_error &) {
+              //invalid num params, try again
+            } catch (const exception::guard_error &) {
+              //guard failed to allow the function to execute,
+              //try again
+            }
+          }
+        }
+
+        return detail::dispatch_with_conversions(ordered_funcs.cbegin(), ordered_funcs.cend(), plist, t_conversions, funcs);
+      }
+  }
+}
+
+
+#endif

+ 139 - 0
chaiscript/dispatchkit/proxy_functions_detail.hpp

@@ -0,0 +1,139 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_
+#define CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_
+
+#include <functional>
+#include <stdexcept>
+#include <vector>
+#include <array>
+
+#include "../chaiscript_defines.hpp"
+#include "boxed_cast.hpp"
+#include "boxed_value.hpp"
+#include "handle_return.hpp"
+#include "type_info.hpp"
+#include "callable_traits.hpp"
+
+namespace chaiscript {
+class Type_Conversions_State;
+namespace exception {
+class bad_boxed_cast;
+}  // namespace exception
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  namespace exception
+  {
+    /**
+     * Exception thrown when there is a mismatch in number of
+     * parameters during Proxy_Function execution
+     */
+    struct arity_error : std::range_error
+    {
+      arity_error(int t_got, int t_expected)
+        : std::range_error("Function dispatch arity mismatch"),
+        got(t_got), expected(t_expected)
+      {
+      }
+
+      arity_error(const arity_error &) = default;
+
+      ~arity_error() noexcept override = default;
+
+      int got;
+      int expected;
+    };
+  }
+
+  namespace dispatch
+  {
+    namespace detail
+    {
+      /**
+       * Used by Proxy_Function_Impl to return a list of all param types
+       * it contains.
+       */
+      template<typename Ret, typename ... Params>
+        std::vector<Type_Info> build_param_type_list(Ret (*)(Params...))
+        {
+          /// \note somehow this is responsible for a large part of the code generation
+          return { user_type<Ret>(), user_type<Params>()... };
+        }
+
+
+      /**
+       * Used by Proxy_Function_Impl to determine if it is equivalent to another
+       * Proxy_Function_Impl object. This function is primarily used to prevent
+       * registration of two functions with the exact same signatures
+       */
+      template<typename Ret, typename ... Params>
+        bool compare_types_cast(Ret (*)(Params...),
+             const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions)
+        {
+          try {
+            std::vector<Boxed_Value>::size_type i = 0;
+            (void)i;
+            (void)params; (void)t_conversions;
+            // this is ok because the order of evaluation of initializer lists is well defined
+            (void)std::initializer_list<int>{(boxed_cast<Params>(params[i++], &t_conversions), 0)...};
+            return true;
+          } catch (const exception::bad_boxed_cast &) {
+            return false;
+          }
+        }
+
+
+      template<typename Callable, typename Ret, typename ... Params, size_t ... I>
+        Ret call_func(const chaiscript::dispatch::detail::Function_Signature<Ret (Params...)> &, 
+                      std::index_sequence<I...>, const Callable &f,
+                      const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions)
+        {
+          (void)params; (void)t_conversions;
+          return f(boxed_cast<Params>(params[I], &t_conversions)...);
+        }
+
+
+      /// Used by Proxy_Function_Impl to perform typesafe execution of a function.
+      /// The function attempts to unbox each parameter to the expected type.
+      /// if any unboxing fails the execution of the function fails and
+      /// the bad_boxed_cast is passed up to the caller.
+      template<typename Callable, typename Ret, typename ... Params>
+        Boxed_Value call_func(const chaiscript::dispatch::detail::Function_Signature<Ret (Params...)> &sig, const Callable &f,
+            const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions)
+        {
+          return Handle_Return<Ret>::handle(call_func(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions));
+        }
+
+      template<typename Callable, typename ... Params>
+        Boxed_Value call_func(const chaiscript::dispatch::detail::Function_Signature<void (Params...)> &sig, const Callable &f,
+            const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions)
+        {
+          call_func(sig, std::index_sequence_for<Params...>{}, f, params, t_conversions);
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4702)
+#endif
+          // MSVC is reporting that this is unreachable code - and it's wrong.
+          return Handle_Return<void>::handle();
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+        }
+
+    }
+  }
+
+}
+
+
+#endif

+ 152 - 0
chaiscript/dispatchkit/register_function.hpp

@@ -0,0 +1,152 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_REGISTER_FUNCTION_HPP_
+#define CHAISCRIPT_REGISTER_FUNCTION_HPP_
+
+#include <type_traits>
+
+#include "bind_first.hpp"
+#include "proxy_functions.hpp"
+
+namespace chaiscript
+{
+
+  /// \brief Creates a new Proxy_Function object from a free function, member function or data member
+  /// \param[in] t Function / member to expose
+  ///
+  /// \b Example:
+  /// \code
+  /// int myfunction(const std::string &);
+  /// class MyClass
+  /// {
+  ///   public:
+  ///     void memberfunction();
+  ///     int memberdata;
+  /// };
+  /// 
+  /// chaiscript::ChaiScript chai;
+  /// chai.add(fun(&myfunction), "myfunction");
+  /// chai.add(fun(&MyClass::memberfunction), "memberfunction");
+  /// chai.add(fun(&MyClass::memberdata), "memberdata");
+  /// \endcode
+  /// 
+  /// \sa \ref adding_functions
+  template<typename T>
+    Proxy_Function fun(const T &t)
+    {
+      typedef typename dispatch::detail::Callable_Traits<T>::Signature Signature;
+
+      return Proxy_Function(
+          chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Signature, T>>(t));
+    }
+
+
+  template<typename Ret, typename ... Param>
+    Proxy_Function fun(Ret (*func)(Param...))
+    {
+      auto fun_call = dispatch::detail::Fun_Caller<Ret, Param...>(func);
+
+      return Proxy_Function(
+          chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Param...), decltype(fun_call)>>(fun_call));
+
+    }
+
+  template<typename Ret, typename Class, typename ... Param>
+    Proxy_Function fun(Ret (Class::*t_func)(Param...) const)
+    {
+      auto call = dispatch::detail::Const_Caller<Ret, Class, Param...>(t_func);
+
+      return Proxy_Function(
+          chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (const Class &, Param...), decltype(call)>>(call));
+    }
+
+  template<typename Ret, typename Class, typename ... Param>
+    Proxy_Function fun(Ret (Class::*t_func)(Param...))
+    {
+      auto call = dispatch::detail::Caller<Ret, Class, Param...>(t_func);
+
+      return Proxy_Function(
+          chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Class &, Param...), decltype(call)>>(call));
+
+    }
+
+  template<typename T, typename Class /*, typename = typename std::enable_if<std::is_member_object_pointer<T>::value>::type*/>
+    Proxy_Function fun(T Class::* m /*, typename std::enable_if<std::is_member_object_pointer<T>::value>::type* = 0*/ )
+    {
+      return Proxy_Function(chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Attribute_Access<T, Class>>(m));
+    }
+
+// only compile this bit if noexcept is part of the type system
+//
+#if __cpp_noexcept_function_type >= 201510
+  template<typename Ret, typename ... Param>
+    Proxy_Function fun(Ret (*func)(Param...) noexcept)
+    {
+      auto fun_call = dispatch::detail::Fun_Caller<Ret, Param...>(func);
+
+      return Proxy_Function(
+          chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Param...), decltype(fun_call)>>(fun_call));
+
+    }
+
+  template<typename Ret, typename Class, typename ... Param>
+    Proxy_Function fun(Ret (Class::*t_func)(Param...) const noexcept)
+    {
+      auto call = dispatch::detail::Const_Caller<Ret, Class, Param...>(t_func);
+
+      return Proxy_Function(
+          chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (const Class &, Param...), decltype(call)>>(call));
+    }
+
+  template<typename Ret, typename Class, typename ... Param>
+    Proxy_Function fun(Ret (Class::*t_func)(Param...) noexcept)
+    {
+      auto call = dispatch::detail::Caller<Ret, Class, Param...>(t_func);
+
+      return Proxy_Function(
+          chaiscript::make_shared<dispatch::Proxy_Function_Base, dispatch::Proxy_Function_Callable_Impl<Ret (Class &, Param...), decltype(call)>>(call));
+
+    }
+#endif
+
+
+
+  
+  /// \brief Creates a new Proxy_Function object from a free function, member function or data member and binds the first parameter of it
+  /// \param[in] t Function / member to expose
+  /// \param[in] q Value to bind to first parameter
+  ///
+  /// \b Example:
+  /// \code
+  /// struct MyClass
+  /// {
+  ///   void memberfunction(int);
+  /// };
+  /// 
+  /// MyClass obj;
+  /// chaiscript::ChaiScript chai;
+  /// // Add function taking only one argument, an int, and permanently bound to "obj"
+  /// chai.add(fun(&MyClass::memberfunction, std::ref(obj)), "memberfunction"); 
+  /// \endcode
+  /// 
+  /// \sa \ref adding_functions
+  template<typename T, typename Q>
+    Proxy_Function fun(T &&t, const Q &q)
+    {
+      return fun(detail::bind_first(std::forward<T>(t), q));
+    }
+
+
+}
+
+
+#endif
+

+ 159 - 0
chaiscript/dispatchkit/short_alloc.hpp

@@ -0,0 +1,159 @@
+#ifndef SHORT_ALLOC_H
+#define SHORT_ALLOC_H
+
+// The MIT License (MIT)
+// 
+// Copyright (c) 2015 Howard Hinnant
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#include <cstddef>
+#include <cassert>
+
+template <std::size_t N, std::size_t alignment = alignof(std::max_align_t)>
+class arena
+{
+    alignas(alignment) char buf_[N];
+    char* ptr_;
+
+public:
+    ~arena() {ptr_ = nullptr;}
+    arena() noexcept : ptr_(buf_) {}
+    arena(const arena&) = delete;
+    arena& operator=(const arena&) = delete;
+
+    template <std::size_t ReqAlign> char* allocate(std::size_t n);
+    void deallocate(char* p, std::size_t n) noexcept;
+
+    static constexpr std::size_t size() noexcept {return N;}
+    std::size_t used() const noexcept {return static_cast<std::size_t>(ptr_ - buf_);}
+    void reset() noexcept {ptr_ = buf_;}
+
+private:
+    static
+    std::size_t 
+    align_up(std::size_t n) noexcept
+        {return (n + (alignment-1)) & ~(alignment-1);}
+
+    bool
+    pointer_in_buffer(char* p) noexcept
+        {return buf_ <= p && p <= buf_ + N;}
+};
+
+template <std::size_t N, std::size_t alignment>
+template <std::size_t ReqAlign>
+char*
+arena<N, alignment>::allocate(std::size_t n)
+{
+    static_assert(ReqAlign <= alignment, "alignment is too small for this arena");
+    assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
+    auto const aligned_n = align_up(n);
+    if (static_cast<decltype(aligned_n)>(buf_ + N - ptr_) >= aligned_n)
+    {
+        char* r = ptr_;
+        ptr_ += aligned_n;
+        return r;
+    }
+
+    static_assert(alignment <= alignof(std::max_align_t), "you've chosen an "
+                  "alignment that is larger than alignof(std::max_align_t), and "
+                  "cannot be guaranteed by normal operator new");
+    return static_cast<char*>(::operator new(n));
+}
+
+template <std::size_t N, std::size_t alignment>
+void
+arena<N, alignment>::deallocate(char* p, std::size_t n) noexcept
+{
+    assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
+    if (pointer_in_buffer(p))
+    {
+      n = align_up(n);
+      if (p + n == ptr_) {
+        ptr_ = p;
+      }
+    }
+    else {
+      ::operator delete(p);
+    }
+}
+
+template <class T, std::size_t N, std::size_t Align = alignof(std::max_align_t)>
+class short_alloc
+{
+public:
+    using value_type = T;
+    static auto constexpr alignment = Align;
+    static auto constexpr size = N;
+    using arena_type = arena<size, alignment>;
+
+private:
+    arena_type& a_;
+
+public:
+    short_alloc(const short_alloc&) = default;
+    short_alloc& operator=(const short_alloc&) = delete;
+
+    explicit short_alloc(arena_type& a) noexcept : a_(a)
+    {
+        static_assert(size % alignment == 0,
+                      "size N needs to be a multiple of alignment Align");
+    }
+    template <class U>
+        explicit short_alloc(const short_alloc<U, N, alignment>& a) noexcept
+            : a_(a.a_) {}
+
+    template <class _Up> struct rebind {using other = short_alloc<_Up, N, alignment>;};
+
+    T* allocate(std::size_t n)
+    {
+        return reinterpret_cast<T*>(a_.template allocate<alignof(T)>(n*sizeof(T)));
+    }
+    void deallocate(T* p, std::size_t n) noexcept
+    {
+        a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T));
+    }
+
+    template <class T1, std::size_t N1, std::size_t A1, 
+              class U, std::size_t M, std::size_t A2>
+    friend
+    bool
+    operator==(const short_alloc<T1, N1, A1>& x, const short_alloc<U, M, A2>& y) noexcept;
+
+    template <class U, std::size_t M, std::size_t A> friend class short_alloc;
+};
+
+template <class T, std::size_t N, std::size_t A1, class U, std::size_t M, std::size_t A2>
+inline
+bool
+operator==(const short_alloc<T, N, A1>& x, const short_alloc<U, M, A2>& y) noexcept
+{
+    return N == M && A1 == A2 && &x.a_ == &y.a_;
+}
+
+template <class T, std::size_t N, std::size_t A1, class U, std::size_t M, std::size_t A2>
+inline
+bool
+operator!=(const short_alloc<T, N, A1>& x, const short_alloc<U, M, A2>& y) noexcept
+{
+    return !(x == y);
+}
+
+#endif  // SHORT_ALLOC_HPP
+

+ 649 - 0
chaiscript/dispatchkit/type_conversions.hpp

@@ -0,0 +1,649 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_
+#define CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_
+
+#include <atomic>
+#include <memory>
+#include <set>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <typeinfo>
+
+#include "../chaiscript_threading.hpp"
+#include "bad_boxed_cast.hpp"
+#include "boxed_cast_helper.hpp"
+#include "boxed_value.hpp"
+#include "type_info.hpp"
+
+namespace chaiscript
+{
+  namespace exception
+  {
+    class bad_boxed_dynamic_cast : public bad_boxed_cast
+    {
+      public:
+        bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to,
+            const std::string &t_what) noexcept
+          : bad_boxed_cast(t_from, t_to, t_what)
+        {
+        }
+
+        bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept
+          : bad_boxed_cast(t_from, t_to)
+        {
+        }
+
+        explicit bad_boxed_dynamic_cast(const std::string &w) noexcept
+          : bad_boxed_cast(w)
+        {
+        }
+
+        bad_boxed_dynamic_cast(const bad_boxed_dynamic_cast &) = default;
+
+        ~bad_boxed_dynamic_cast() noexcept override = default;
+    };
+
+    class bad_boxed_type_cast : public bad_boxed_cast
+    {
+      public:
+        bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to,
+            const std::string &t_what) noexcept
+          : bad_boxed_cast(t_from, t_to, t_what)
+        {
+        }
+
+        bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept
+          : bad_boxed_cast(t_from, t_to)
+        {
+        }
+
+        explicit bad_boxed_type_cast(const std::string &w) noexcept
+          : bad_boxed_cast(w)
+        {
+        }
+
+        bad_boxed_type_cast(const bad_boxed_type_cast &) = default;
+
+        ~bad_boxed_type_cast() noexcept override = default;
+    };
+  }
+
+
+  namespace detail
+  {
+    class Type_Conversion_Base
+    {
+      public:
+        virtual Boxed_Value convert(const Boxed_Value &from) const = 0;
+        virtual Boxed_Value convert_down(const Boxed_Value &to) const = 0;
+
+        const Type_Info &to() const
+        {
+          return m_to;
+        }
+        const Type_Info &from() const
+        {
+          return m_from;
+        }
+
+        virtual bool bidir() const
+        {
+          return true;
+        }
+
+        virtual ~Type_Conversion_Base() = default;
+
+      protected:
+        Type_Conversion_Base(Type_Info t_to, Type_Info t_from)
+          : m_to(std::move(t_to)), m_from(std::move(t_from))
+        {
+        }
+
+
+      private:
+        const Type_Info m_to;
+        const Type_Info m_from;
+
+    };
+
+    template<typename From, typename To> 
+      class Static_Caster
+      {
+        public: 
+          static Boxed_Value cast(const Boxed_Value &t_from)
+          {
+            if (t_from.get_type_info().bare_equal(chaiscript::user_type<From>()))
+            {
+              if (t_from.is_pointer())
+              {
+                // Dynamic cast out the contained boxed value, which we know is the type we want
+                if (t_from.is_const())
+                {
+                  return Boxed_Value(
+                      [&](){
+                        if (auto data = std::static_pointer_cast<const To>(detail::Cast_Helper<std::shared_ptr<const From> >::cast(t_from, nullptr)))
+                        {
+                          return data;
+                        } else {
+                          throw std::bad_cast();
+                        }
+                      }()
+                      );
+                } else {
+                  return Boxed_Value(
+                      [&](){
+                        if (auto data = std::static_pointer_cast<To>(detail::Cast_Helper<std::shared_ptr<From> >::cast(t_from, nullptr)))
+                        {
+                          return data;
+                        } else {
+                          throw std::bad_cast();
+                        }
+                      }()
+                      );
+                }
+              } else {
+                // Pull the reference out of the contained boxed value, which we know is the type we want
+                if (t_from.is_const())
+                {
+                  const From &d = detail::Cast_Helper<const From &>::cast(t_from, nullptr);
+                  const To &data = static_cast<const To &>(d);
+                  return Boxed_Value(std::cref(data));
+                } else {
+                  From &d = detail::Cast_Helper<From &>::cast(t_from, nullptr);
+                  To &data = static_cast<To &>(d);
+                  return Boxed_Value(std::ref(data));
+                }
+              }
+            } else {
+              throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion");
+            }
+          }
+ 
+      };
+
+
+    template<typename From, typename To> 
+      class Dynamic_Caster
+      {
+        public: 
+          static Boxed_Value cast(const Boxed_Value &t_from)
+          {
+            if (t_from.get_type_info().bare_equal(chaiscript::user_type<From>()))
+            {
+              if (t_from.is_pointer())
+              {
+                // Dynamic cast out the contained boxed value, which we know is the type we want
+                if (t_from.is_const())
+                {
+                  return Boxed_Value(
+                      [&](){
+                        if (auto data = std::dynamic_pointer_cast<const To>(detail::Cast_Helper<std::shared_ptr<const From> >::cast(t_from, nullptr)))
+                        {
+                          return data;
+                        } else {
+                          throw std::bad_cast();
+                        }
+                      }()
+                      );
+                } else {
+                  return Boxed_Value(
+                      [&](){
+                        if (auto data = std::dynamic_pointer_cast<To>(detail::Cast_Helper<std::shared_ptr<From> >::cast(t_from, nullptr)))
+                        {
+                          return data;
+                        } else {
+#ifdef CHAISCRIPT_LIBCPP
+                          /// \todo fix this someday after libc++ is fixed.
+                          if (std::string(typeid(To).name()).find("Assignable_Proxy_Function") != std::string::npos) {
+                            auto from = detail::Cast_Helper<std::shared_ptr<From> >::cast(t_from, nullptr);
+                            if (std::string(typeid(*from).name()).find("Assignable_Proxy_Function_Impl") != std::string::npos) {
+                              return std::static_pointer_cast<To>(from);
+                            }
+                          }
+#endif
+                          throw std::bad_cast();
+                        }
+                      }()
+                      );
+                }
+              } else {
+                // Pull the reference out of the contained boxed value, which we know is the type we want
+                if (t_from.is_const())
+                {
+                  const From &d = detail::Cast_Helper<const From &>::cast(t_from, nullptr);
+                  const To &data = dynamic_cast<const To &>(d);
+                  return Boxed_Value(std::cref(data));
+                } else {
+                  From &d = detail::Cast_Helper<From &>::cast(t_from, nullptr);
+                  To &data = dynamic_cast<To &>(d);
+                  return Boxed_Value(std::ref(data));
+                }
+              }
+            } else {
+              throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion");
+            }
+          }
+ 
+      };
+
+
+    template<typename Base, typename Derived>
+      class Dynamic_Conversion_Impl : public Type_Conversion_Base
+    {
+      public:
+        Dynamic_Conversion_Impl()
+          : Type_Conversion_Base(chaiscript::user_type<Base>(), chaiscript::user_type<Derived>())
+        {
+        }
+
+        Boxed_Value convert_down(const Boxed_Value &t_base) const override
+        {
+          return Dynamic_Caster<Base, Derived>::cast(t_base);
+        }
+
+        Boxed_Value convert(const Boxed_Value &t_derived) const override
+        {
+          return Static_Caster<Derived, Base>::cast(t_derived);
+        }
+    };
+
+    template<typename Base, typename Derived>
+      class Static_Conversion_Impl : public Type_Conversion_Base
+    {
+      public:
+        Static_Conversion_Impl()
+          : Type_Conversion_Base(chaiscript::user_type<Base>(), chaiscript::user_type<Derived>())
+        {
+        }
+
+        Boxed_Value convert_down(const Boxed_Value &t_base) const override
+        {
+          throw chaiscript::exception::bad_boxed_dynamic_cast(t_base.get_type_info(), typeid(Derived), 
+              "Unable to cast down inheritance hierarchy with non-polymorphic types");
+        }
+
+        bool bidir() const override
+        {
+          return false;
+        }
+
+        Boxed_Value convert(const Boxed_Value &t_derived) const override
+        {
+          return Static_Caster<Derived, Base>::cast(t_derived);
+        }
+    };
+
+
+
+    template<typename Callable>
+    class Type_Conversion_Impl : public Type_Conversion_Base
+    {
+      public:
+        Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, Callable t_func)
+          : Type_Conversion_Base(t_to, t_from),
+            m_func(std::move(t_func))
+        {
+        }
+
+        Boxed_Value convert_down(const Boxed_Value &) const override
+        {
+          throw chaiscript::exception::bad_boxed_type_cast("No conversion exists");
+        }
+
+        Boxed_Value convert(const Boxed_Value &t_from) const override
+        {
+          /// \todo better handling of errors from the conversion function
+          return m_func(t_from);
+        }
+
+        bool bidir() const override
+        {
+          return false;
+        }
+
+
+      private:
+        Callable m_func;
+    };
+  }
+
+  class Type_Conversions
+  {
+    public:
+      struct Conversion_Saves
+      {
+        bool enabled = false;
+        std::vector<Boxed_Value> saves;
+      };
+
+      struct Less_Than
+      {
+        bool operator()(const std::type_info *t_lhs, const std::type_info *t_rhs) const
+        {
+          return *t_lhs != *t_rhs && t_lhs->before(*t_rhs);
+        }
+      };
+
+      Type_Conversions()
+        : m_mutex(),
+          m_conversions(),
+          m_convertableTypes(),
+          m_num_types(0)
+      {
+      }
+
+      Type_Conversions(const Type_Conversions &t_other) = delete;
+      Type_Conversions(Type_Conversions &&) = default;
+
+      Type_Conversions &operator=(const Type_Conversions &) = delete;
+      Type_Conversions &operator=(Type_Conversions &&) = default;
+
+      const std::set<const std::type_info *, Less_Than> &thread_cache() const
+      {
+        auto &cache = *m_thread_cache;
+        if (cache.size() != m_num_types)
+        {
+          chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+          cache = m_convertableTypes;
+        }
+
+        return cache;
+      }
+
+      void add_conversion(const std::shared_ptr<detail::Type_Conversion_Base> &conversion)
+      {
+        chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+        /// \todo error if a conversion already exists
+        m_conversions.insert(conversion);
+        m_convertableTypes.insert({conversion->to().bare_type_info(), conversion->from().bare_type_info()});
+        m_num_types = m_convertableTypes.size();
+      }
+
+      template<typename T>
+        bool convertable_type() const
+        {
+          return thread_cache().count(user_type<T>().bare_type_info()) != 0;
+        }
+
+      template<typename To, typename From>
+        bool converts() const
+        {
+          return converts(user_type<To>(), user_type<From>());
+        }
+
+      bool converts(const Type_Info &to, const Type_Info &from) const
+      {
+        const auto &types = thread_cache();
+        if (types.count(to.bare_type_info()) != 0 && types.count(from.bare_type_info()) != 0)
+        {
+          return has_conversion(to, from);
+        } else {
+          return false;
+        }
+      }
+
+      template<typename To>
+        Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const
+        {
+          return boxed_type_conversion(user_type<To>(), t_saves, from);
+        }
+
+      template<typename From>
+        Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const
+        {
+          return boxed_type_down_conversion(user_type<From>(), t_saves, to);
+        }
+
+
+        Boxed_Value boxed_type_conversion(const Type_Info &to, Conversion_Saves &t_saves, const Boxed_Value &from) const
+        {
+          try {
+            Boxed_Value ret = get_conversion(to, from.get_type_info())->convert(from);
+            if (t_saves.enabled) { t_saves.saves.push_back(ret); }
+            return ret;
+          } catch (const std::out_of_range &) {
+            throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "No known conversion");
+          } catch (const std::bad_cast &) {
+            throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "Unable to perform dynamic_cast operation");
+          }
+        }
+
+        Boxed_Value boxed_type_down_conversion(const Type_Info &from, Conversion_Saves &t_saves, const Boxed_Value &to) const
+        {
+          try {
+            Boxed_Value ret = get_conversion(to.get_type_info(), from)->convert_down(to);
+            if (t_saves.enabled) { t_saves.saves.push_back(ret); }
+            return ret;
+          } catch (const std::out_of_range &) {
+            throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "No known conversion");
+          } catch (const std::bad_cast &) {
+            throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "Unable to perform dynamic_cast operation");
+          }
+        }
+
+      static void enable_conversion_saves(Conversion_Saves &t_saves, bool t_val) 
+      {
+        t_saves.enabled = t_val;
+      }
+
+      std::vector<Boxed_Value> take_saves(Conversion_Saves &t_saves)
+      {
+        std::vector<Boxed_Value> ret;
+        std::swap(ret, t_saves.saves);
+        return ret;
+      }
+
+      bool has_conversion(const Type_Info &to, const Type_Info &from) const
+      {
+        chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+        return find_bidir(to, from) != m_conversions.end();
+      }
+
+      std::shared_ptr<detail::Type_Conversion_Base> get_conversion(const Type_Info &to, const Type_Info &from) const
+      {
+        chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+        const auto itr = find(to, from);
+
+        if (itr != m_conversions.end())
+        {
+          return *itr;
+        } else {
+          throw std::out_of_range("No such conversion exists from " + from.bare_name() + " to " + to.bare_name());
+        }
+      }
+
+      Conversion_Saves &conversion_saves() const {
+        return *m_conversion_saves;
+      }
+
+    private:
+      std::set<std::shared_ptr<detail::Type_Conversion_Base> >::const_iterator find_bidir(
+          const Type_Info &to, const Type_Info &from) const
+      {
+        return std::find_if(m_conversions.begin(), m_conversions.end(),
+              [&to, &from](const std::shared_ptr<detail::Type_Conversion_Base> &conversion) -> bool
+              {
+                return  (conversion->to().bare_equal(to) && conversion->from().bare_equal(from))
+                     || (conversion->bidir() && conversion->from().bare_equal(to) && conversion->to().bare_equal(from));
+              }
+        );
+      }
+
+      std::set<std::shared_ptr<detail::Type_Conversion_Base> >::const_iterator find(
+          const Type_Info &to, const Type_Info &from) const
+      {
+        return std::find_if(m_conversions.begin(), m_conversions.end(),
+              [&to, &from](const std::shared_ptr<detail::Type_Conversion_Base> &conversion)
+              {
+                return conversion->to().bare_equal(to) && conversion->from().bare_equal(from);
+              }
+        );
+      }
+
+      std::set<std::shared_ptr<detail::Type_Conversion_Base>> get_conversions() const
+      {
+        chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
+
+        return m_conversions;
+      }
+
+
+
+      mutable chaiscript::detail::threading::shared_mutex m_mutex;
+      std::set<std::shared_ptr<detail::Type_Conversion_Base>> m_conversions;
+      std::set<const std::type_info *, Less_Than> m_convertableTypes;
+      std::atomic_size_t m_num_types;
+      mutable chaiscript::detail::threading::Thread_Storage<std::set<const std::type_info *, Less_Than>> m_thread_cache;
+      mutable chaiscript::detail::threading::Thread_Storage<Conversion_Saves> m_conversion_saves;
+  };
+
+  class Type_Conversions_State
+  {
+    public:
+      Type_Conversions_State(const Type_Conversions &t_conversions,
+          Type_Conversions::Conversion_Saves &t_saves)
+        : m_conversions(t_conversions),
+          m_saves(t_saves)
+      {
+      }
+
+      const Type_Conversions *operator->() const {
+        return &m_conversions.get();
+      }
+
+      const Type_Conversions *get() const {
+        return &m_conversions.get();
+      }
+
+      Type_Conversions::Conversion_Saves &saves() const {
+        return m_saves;
+      }
+
+    private:
+      std::reference_wrapper<const Type_Conversions> m_conversions;
+      std::reference_wrapper<Type_Conversions::Conversion_Saves> m_saves;
+  };
+
+  typedef std::shared_ptr<chaiscript::detail::Type_Conversion_Base> Type_Conversion;
+
+  /// \brief Used to register a to / parent class relationship with ChaiScript. Necessary if you
+  ///        want automatic conversions up your inheritance hierarchy.
+  ///
+  /// Create a new to class registration for applying to a module or to the ChaiScript engine
+  /// Currently, due to limitations in module loading on Windows, and for the sake of portability,
+  /// if you have a type that is introduced in a loadable module and is used by multiple modules
+  /// (through a tertiary dll that is shared between the modules, static linking the new type
+  /// into both loadable modules would not be portable), you need to register the type
+  /// relationship in all modules that use the newly added type in a polymorphic way.
+  ///
+  /// Example:
+  /// \code
+  /// class Base
+  /// {};
+  /// class Derived : public Base
+  /// {};
+  ///
+  /// chaiscript::ChaiScript chai;
+  /// chai.add(chaiscript::to_class<Base, Derived>());
+  /// \endcode
+  /// 
+  template<typename Base, typename Derived>
+  Type_Conversion base_class(typename std::enable_if<std::is_polymorphic<Base>::value && std::is_polymorphic<Derived>::value>::type* = nullptr)
+  {
+    //Can only be used with related polymorphic types
+    //may be expanded some day to support conversions other than child -> parent
+    static_assert(std::is_base_of<Base,Derived>::value, "Classes are not related by inheritance");
+
+    return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Dynamic_Conversion_Impl<Base, Derived>>();
+  }
+
+  template<typename Base, typename Derived>
+  Type_Conversion base_class(typename std::enable_if<!std::is_polymorphic<Base>::value || !std::is_polymorphic<Derived>::value>::type* = nullptr)
+  {
+    //Can only be used with related polymorphic types
+    //may be expanded some day to support conversions other than child -> parent
+    static_assert(std::is_base_of<Base,Derived>::value, "Classes are not related by inheritance");
+
+    return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Static_Conversion_Impl<Base, Derived>>();
+  }
+
+
+  template<typename Callable>
+    Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to, 
+        const Callable &t_func)
+    {
+      return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<Callable>>(t_from, t_to, t_func);
+    }
+
+  template<typename From, typename To, typename Callable>
+    Type_Conversion type_conversion(const Callable &t_function)
+    {
+      auto func = [t_function](const Boxed_Value &t_bv) -> Boxed_Value {
+            // not even attempting to call boxed_cast so that we don't get caught in some call recursion
+            return chaiscript::Boxed_Value(t_function(detail::Cast_Helper<const From &>::cast(t_bv, nullptr)));
+          };
+
+      return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<From>(), user_type<To>(), func);
+    }
+
+  template<typename From, typename To>
+    Type_Conversion type_conversion()
+    {
+      static_assert(std::is_convertible<From, To>::value, "Types are not automatically convertible");
+      auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
+            // not even attempting to call boxed_cast so that we don't get caught in some call recursion
+            return chaiscript::Boxed_Value(To(detail::Cast_Helper<From>::cast(t_bv, nullptr)));
+          };
+
+      return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<From>(), user_type<To>(), func);
+    }
+
+  template<typename To>
+    Type_Conversion vector_conversion()
+    {
+      auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
+        const std::vector<Boxed_Value> &from_vec = detail::Cast_Helper<const std::vector<Boxed_Value> &>::cast(t_bv, nullptr);
+
+        To vec;
+        vec.reserve(from_vec.size());
+        for (const Boxed_Value &bv : from_vec) {
+          vec.push_back(detail::Cast_Helper<typename To::value_type>::cast(bv, nullptr));
+        }
+
+        return Boxed_Value(std::move(vec));
+      };
+
+      return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<std::vector<Boxed_Value>>(), user_type<To>(), func);
+    }
+
+  template<typename To>
+    Type_Conversion map_conversion()
+    {
+      auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
+        const std::map<std::string, Boxed_Value> &from_map = detail::Cast_Helper<const std::map<std::string, Boxed_Value> &>::cast(t_bv, nullptr);
+
+        To map;
+        for (const std::pair<std::string, Boxed_Value> &p : from_map) {
+          map.insert(std::make_pair(p.first, detail::Cast_Helper<typename To::mapped_type>::cast(p.second, nullptr)));
+        }
+
+        return Boxed_Value(std::move(map));
+      };
+
+      return chaiscript::make_shared<detail::Type_Conversion_Base, detail::Type_Conversion_Impl<decltype(func)>>(user_type<std::map<std::string, Boxed_Value>>(), user_type<To>(), func);
+    }
+}
+
+
+#endif

+ 244 - 0
chaiscript/dispatchkit/type_info.hpp

@@ -0,0 +1,244 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_TYPE_INFO_HPP_
+#define CHAISCRIPT_TYPE_INFO_HPP_
+
+#include <memory>
+#include <type_traits>
+#include <typeinfo>
+#include <string>
+
+namespace chaiscript
+{
+
+  namespace detail
+  {
+    template<typename T>
+      struct Bare_Type
+      {
+        typedef typename std::remove_cv<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::type type;
+      };
+  }
+
+
+  /// \brief Compile time deduced information about a type
+  class Type_Info
+  {
+    public:
+      constexpr Type_Info(const bool t_is_const, const bool t_is_reference, const bool t_is_pointer, const bool t_is_void, 
+          const bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti)
+        : m_type_info(t_ti), m_bare_type_info(t_bare_ti),
+          m_flags((static_cast<unsigned int>(t_is_const) << is_const_flag)
+                + (static_cast<unsigned int>(t_is_reference) << is_reference_flag)
+                + (static_cast<unsigned int>(t_is_pointer) << is_pointer_flag)
+                + (static_cast<unsigned int>(t_is_void) << is_void_flag)
+                + (static_cast<unsigned int>(t_is_arithmetic) << is_arithmetic_flag))
+      {
+      }
+
+      constexpr Type_Info() = default;
+
+      constexpr bool operator<(const Type_Info &ti) const noexcept
+      {
+        return m_type_info < ti.m_type_info;
+      }
+
+      constexpr bool operator!=(const Type_Info &ti) const noexcept
+      {
+        return !(operator==(ti));
+      }
+
+      constexpr bool operator!=(const std::type_info &ti) const noexcept
+      {
+        return !(operator==(ti));
+      }
+
+      constexpr bool operator==(const Type_Info &ti) const noexcept
+      {
+        return ti.m_type_info == m_type_info
+           || *ti.m_type_info == *m_type_info;
+      }
+
+      constexpr bool operator==(const std::type_info &ti) const noexcept
+      {
+        return !is_undef() && (*m_type_info) == ti;
+      }
+
+      constexpr bool bare_equal(const Type_Info &ti) const noexcept
+      {
+        return ti.m_bare_type_info == m_bare_type_info
+           || *ti.m_bare_type_info == *m_bare_type_info;
+      }
+
+      constexpr bool bare_equal_type_info(const std::type_info &ti) const noexcept
+      {
+        return !is_undef() && (*m_bare_type_info) == ti;
+      }
+
+      constexpr bool is_const() const noexcept { return (m_flags & (1 << is_const_flag)) != 0; }
+      constexpr bool is_reference() const noexcept { return (m_flags & (1 << is_reference_flag)) != 0; }
+      constexpr bool is_void() const noexcept { return (m_flags & (1 << is_void_flag)) != 0; }
+      constexpr bool is_arithmetic() const noexcept { return (m_flags & (1 << is_arithmetic_flag)) != 0; }
+      constexpr bool is_undef() const noexcept { return (m_flags & (1 << is_undef_flag)) != 0; }
+      constexpr bool is_pointer() const noexcept { return (m_flags & (1 << is_pointer_flag)) != 0; }
+
+      std::string name() const
+      {
+        if (!is_undef())
+        {
+          return m_type_info->name();
+        } else {
+          return "";
+        }
+      }
+
+      std::string bare_name() const
+      {
+        if (!is_undef())
+        {
+          return m_bare_type_info->name();
+        } else {
+          return "";
+        }
+      }
+
+      constexpr const std::type_info *bare_type_info() const
+      {
+        return m_bare_type_info;
+      }
+
+    private:
+      struct Unknown_Type {};
+
+      const std::type_info *m_type_info = &typeid(Unknown_Type);
+      const std::type_info *m_bare_type_info = &typeid(Unknown_Type);
+      static const int is_const_flag = 0;
+      static const int is_reference_flag = 1;
+      static const int is_pointer_flag = 2;
+      static const int is_void_flag = 3;
+      static const int is_arithmetic_flag = 4;
+      static const int is_undef_flag = 5;
+      unsigned int m_flags = (1 << is_undef_flag);
+  };
+
+  namespace detail
+  {
+    /// Helper used to create a Type_Info object
+    template<typename T>
+      struct Get_Type_Info
+      {
+        static constexpr Type_Info get()
+        {
+          return Type_Info(std::is_const<typename std::remove_pointer<typename std::remove_reference<T>::type>::type>::value, 
+              std::is_reference<T>::value, std::is_pointer<T>::value, 
+              std::is_void<T>::value,
+              (std::is_arithmetic<T>::value || std::is_arithmetic<typename std::remove_reference<T>::type>::value)
+                && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
+              &typeid(T),
+              &typeid(typename Bare_Type<T>::type));
+        }
+      };
+
+    template<typename T>
+      struct Get_Type_Info<std::shared_ptr<T> >
+      {
+//        typedef T type;
+
+        static constexpr Type_Info get()
+        {
+          return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value, 
+              std::is_void<T>::value,
+              std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
+              &typeid(std::shared_ptr<T> ), 
+              &typeid(typename Bare_Type<T>::type));
+        }
+      };
+
+    template<typename T>
+      struct Get_Type_Info<std::shared_ptr<T> &> : Get_Type_Info<std::shared_ptr<T>>
+      {
+      };
+
+    template<typename T>
+      struct Get_Type_Info<const std::shared_ptr<T> &>
+      {
+        static constexpr Type_Info get()
+        {
+          return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value, 
+              std::is_void<T>::value,
+              std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
+              &typeid(const std::shared_ptr<T> &), 
+              &typeid(typename Bare_Type<T>::type));
+        }
+      };
+
+    template<typename T>
+      struct Get_Type_Info<std::reference_wrapper<T> >
+      {
+        static constexpr Type_Info get()
+        {
+          return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value, 
+              std::is_void<T>::value,
+              std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
+              &typeid(std::reference_wrapper<T> ), 
+              &typeid(typename Bare_Type<T>::type));
+        }
+      };
+
+    template<typename T>
+      struct Get_Type_Info<const std::reference_wrapper<T> &>
+      {
+        static constexpr Type_Info get()
+        {
+          return Type_Info(std::is_const<T>::value, std::is_reference<T>::value, std::is_pointer<T>::value, 
+              std::is_void<T>::value,
+              std::is_arithmetic<T>::value && !std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, bool>::value,
+              &typeid(const std::reference_wrapper<T> &), 
+              &typeid(typename Bare_Type<T>::type));
+        }
+      };
+
+  }
+
+  /// \brief Creates a Type_Info object representing the type passed in
+  /// \tparam T Type of object to get a Type_Info for, derived from the passed in parameter
+  /// \return Type_Info for T
+  /// 
+  /// \b Example:
+  /// \code
+  /// int i;
+  /// chaiscript::Type_Info ti = chaiscript::user_type(i);
+  /// \endcode
+  template<typename T>
+  constexpr Type_Info user_type(const T &/*t*/)
+  {
+    return detail::Get_Type_Info<T>::get();
+  }
+
+
+  /// \brief Creates a Type_Info object representing the templated type
+  /// \tparam T Type of object to get a Type_Info for
+  /// \return Type_Info for T
+  /// 
+  /// \b Example:
+  /// \code
+  /// chaiscript::Type_Info ti = chaiscript::user_type<int>();
+  /// \endcode
+  template<typename T>
+  constexpr Type_Info user_type()
+  {
+    return detail::Get_Type_Info<T>::get();
+  }
+
+}
+
+#endif
+

+ 108 - 0
chaiscript/language/chaiscript_algebraic.hpp

@@ -0,0 +1,108 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_ALGEBRAIC_HPP_
+#define CHAISCRIPT_ALGEBRAIC_HPP_
+
+#include "../utility/fnv1a.hpp"
+
+#include <string>
+
+namespace chaiscript
+{
+
+  struct Operators {
+    enum class Opers
+    {
+      boolean_flag,
+      equals, less_than, greater_than, less_than_equal, greater_than_equal, not_equal, 
+      non_const_flag, 
+      assign, pre_increment, pre_decrement, assign_product, assign_sum,
+      assign_quotient, assign_difference,
+      non_const_int_flag,
+      assign_bitwise_and, assign_bitwise_or, assign_shift_left, assign_shift_right,
+      assign_remainder, assign_bitwise_xor,
+      const_int_flag,
+      shift_left, shift_right, remainder, bitwise_and, bitwise_or, bitwise_xor, bitwise_complement,
+      const_flag,
+      sum, quotient, product, difference, unary_plus, unary_minus, 
+      invalid
+    };
+
+    static const char *to_string(Opers t_oper) {
+      static const char *opers[] = { 
+        "",
+        "==", "<", ">", "<=", ">=", "!=",
+        "",
+        "=", "++", "--", "*=", "+=",
+        "/=", "-=",
+        "",
+        "&=", "|=", "<<=", ">>=",
+        "%=", "^=",
+        "", 
+        "<<", ">>", "%", "&", "|", "^", "~",
+        "",
+        "+", "/", "*", "-", "+", "-",
+        ""
+      };
+      return opers[static_cast<int>(t_oper)];
+    }
+
+    static Opers to_operator(const std::string &t_str, bool t_is_unary = false)
+    {
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4307)
+#endif
+
+      const auto op_hash = utility::fnv1a_32(t_str.c_str());
+      switch (op_hash) {
+        case utility::fnv1a_32("=="): { return Opers::equals; }
+        case utility::fnv1a_32("<"): { return Opers::less_than; }
+        case utility::fnv1a_32(">"): { return Opers::greater_than; }
+        case utility::fnv1a_32("<="): { return Opers::less_than_equal; }
+        case utility::fnv1a_32(">="): { return Opers::greater_than_equal; }
+        case utility::fnv1a_32("!="): { return Opers::not_equal; }
+        case utility::fnv1a_32("="): { return Opers::assign; }
+        case utility::fnv1a_32("++"): { return Opers::pre_increment; }
+        case utility::fnv1a_32("--"): { return Opers::pre_decrement; }
+        case utility::fnv1a_32("*="): { return Opers::assign_product; }
+        case utility::fnv1a_32("+="): { return Opers::assign_sum; }
+        case utility::fnv1a_32("-="): { return Opers::assign_difference; }
+        case utility::fnv1a_32("&="): { return Opers::assign_bitwise_and; }
+        case utility::fnv1a_32("|="): { return Opers::assign_bitwise_or; }
+        case utility::fnv1a_32("<<="): { return Opers::assign_shift_left; }
+        case utility::fnv1a_32(">>="): { return Opers::assign_shift_right; }
+        case utility::fnv1a_32("%="): { return Opers::assign_remainder; }
+        case utility::fnv1a_32("^="): { return Opers::assign_bitwise_xor; }
+        case utility::fnv1a_32("<<"): { return Opers::shift_left; }
+        case utility::fnv1a_32(">>"): { return Opers::shift_right; }
+        case utility::fnv1a_32("%"): { return Opers::remainder; }
+        case utility::fnv1a_32("&"): { return Opers::bitwise_and; }
+        case utility::fnv1a_32("|"): { return Opers::bitwise_or; }
+        case utility::fnv1a_32("^"): { return Opers::bitwise_xor; }
+        case utility::fnv1a_32("~"): { return Opers::bitwise_complement; }
+        case utility::fnv1a_32("+"): { return t_is_unary ? Opers::unary_plus : Opers::sum; }
+        case utility::fnv1a_32("-"): { return t_is_unary ? Opers::unary_minus : Opers::difference; }
+        case utility::fnv1a_32("/"): { return Opers::quotient; }
+        case utility::fnv1a_32("*"): { return Opers::product; }
+        default: { return Opers::invalid; }
+      }
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+
+    }
+
+  };
+}
+
+#endif /* _CHAISCRIPT_ALGEBRAIC_HPP */
+

+ 753 - 0
chaiscript/language/chaiscript_common.hpp

@@ -0,0 +1,753 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_COMMON_HPP_
+#define CHAISCRIPT_COMMON_HPP_
+
+#include <algorithm>
+#include <memory>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "../chaiscript_defines.hpp"
+#include "../dispatchkit/boxed_value.hpp"
+#include "../dispatchkit/dispatchkit.hpp"
+#include "../dispatchkit/proxy_functions.hpp"
+#include "../dispatchkit/type_info.hpp"
+
+namespace chaiscript {
+struct AST_Node;
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  struct Name_Validator {
+    static bool is_reserved_word(const std::string &name)
+    {
+      static const std::set<std::string> m_reserved_words 
+        = {"def", "fun", "while", "for", "if", "else", "&&", "||", ",", "auto", 
+          "return", "break", "true", "false", "class", "attr", "var", "global", "GLOBAL", "_",
+          "__LINE__", "__FILE__", "__FUNC__", "__CLASS__"};
+      return m_reserved_words.count(name) > 0;
+    }
+
+    static bool valid_object_name(const std::string &name)
+    {
+      return name.find("::") == std::string::npos && !is_reserved_word(name);
+    }
+
+    static void validate_object_name(const std::string &name)
+    {
+      if (is_reserved_word(name)) {
+        throw exception::reserved_word_error(name);
+      }
+
+      if (name.find("::") != std::string::npos) {
+        throw exception::illegal_name_error(name);
+      }
+    }
+  };
+
+  /// Signature of module entry point that all binary loadable modules must implement.
+  typedef ModulePtr (*Create_Module_Func)();
+
+
+  /// Types of AST nodes available to the parser and eval
+  enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl,
+    Array_Call, Dot_Access,
+    Lambda, Block, Scopeless_Block, Def, While, If, For, Ranged_For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range,
+    Inline_Range, Try, Catch, Finally, Method, Attr_Decl,  
+    Logical_And, Logical_Or, Reference, Switch, Case, Default, Noop, Class, Binary, Arg, Global_Decl, Constant, Compiled
+  };
+
+  enum class Operator_Precidence { Ternary_Cond, Logical_Or, 
+    Logical_And, Bitwise_Or, Bitwise_Xor, Bitwise_And, 
+    Equality, Comparison, Shift, Addition, Multiplication, Prefix };
+
+  namespace
+  {
+    /// Helper lookup to get the name of each node type
+    inline const char *ast_node_type_to_string(AST_Node_Type ast_node_type) {
+      static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl",
+                                    "Array_Call", "Dot_Access", 
+                                    "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range",
+                                    "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl",
+                                    "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled"};
+
+      return ast_node_types[static_cast<int>(ast_node_type)];
+    }
+  }
+
+  /// \brief Convenience type for file positions
+  struct File_Position {
+    int line;
+    int column;
+
+    File_Position(int t_file_line, int t_file_column)
+      : line(t_file_line), column(t_file_column) { }
+
+    File_Position() : line(0), column(0) { }
+  };
+
+  struct Parse_Location {
+    Parse_Location(std::string t_fname="", const int t_start_line=0, const int t_start_col=0,
+        const int t_end_line=0, const int t_end_col=0)
+      : start(t_start_line, t_start_col), 
+        end(t_end_line, t_end_col),
+        filename(std::make_shared<std::string>(std::move(t_fname)))
+    {
+    }
+
+    Parse_Location(std::shared_ptr<std::string> t_fname, const int t_start_line=0, const int t_start_col=0,
+        const int t_end_line=0, const int t_end_col=0)
+      : start(t_start_line, t_start_col), 
+        end(t_end_line, t_end_col),
+        filename(std::move(t_fname))
+    {
+    }
+
+
+
+    File_Position start;
+    File_Position end;
+    std::shared_ptr<std::string> filename;
+  };
+
+
+  /// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree
+  typedef std::unique_ptr<AST_Node> AST_NodePtr;
+  typedef std::unique_ptr<const AST_Node> AST_NodePtr_Const;
+
+  struct AST_Node_Trace;
+
+
+  /// \brief Classes which may be thrown during error cases when ChaiScript is executing.
+  namespace exception
+  {
+    /// \brief Thrown if an error occurs while attempting to load a binary module
+    struct load_module_error : std::runtime_error
+    {
+      explicit load_module_error(const std::string &t_reason) noexcept
+        : std::runtime_error(t_reason)
+      {
+      }
+
+      load_module_error(const std::string &t_name, const std::vector<load_module_error> &t_errors)
+        : std::runtime_error(format_error(t_name, t_errors))
+      {
+      }
+
+      load_module_error(const load_module_error &) = default;
+      ~load_module_error() noexcept override = default;
+
+      static std::string format_error(const std::string &t_name, const std::vector<load_module_error> &t_errors)
+      {
+        std::stringstream ss;
+        ss << "Error loading module '" << t_name << "'\n"
+           << "  The following locations were searched:\n";
+
+        for (const auto &err : t_errors) {
+          ss << "    " << err.what() << "\n";
+        }
+
+        return ss.str();
+      }
+    };
+
+
+    /// Errors generated during parsing or evaluation
+    struct eval_error : std::runtime_error {
+      std::string reason;
+      File_Position start_position;
+      std::string filename;
+      std::string detail;
+      std::vector<AST_Node_Trace> call_stack;
+
+      eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname,
+          const std::vector<Boxed_Value> &t_parameters, const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
+          bool t_dot_notation,
+          const chaiscript::detail::Dispatch_Engine &t_ss) noexcept :
+        std::runtime_error(format(t_why, t_where, t_fname, t_parameters, t_dot_notation, t_ss)),
+        reason(t_why), start_position(t_where), filename(t_fname), detail(format_detail(t_functions, t_dot_notation, t_ss)) 
+      {}
+
+      eval_error(const std::string &t_why, 
+           const std::vector<Boxed_Value> &t_parameters, const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
+           bool t_dot_notation,
+           const chaiscript::detail::Dispatch_Engine &t_ss) noexcept :
+        std::runtime_error(format(t_why, t_parameters, t_dot_notation, t_ss)),
+        reason(t_why), detail(format_detail(t_functions, t_dot_notation, t_ss))
+      {}
+
+
+      eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) noexcept :
+        std::runtime_error(format(t_why, t_where, t_fname)),
+        reason(t_why), start_position(t_where), filename(t_fname)
+      {}
+
+      explicit eval_error(const std::string &t_why) noexcept
+        : std::runtime_error("Error: \"" + t_why + "\" "),
+        reason(t_why) 
+      {}
+
+      eval_error(const eval_error &) = default;
+
+      std::string pretty_print() const
+      {
+        std::ostringstream ss;
+
+        ss << what();
+        if (!call_stack.empty()) {
+          ss << "during evaluation at (" << fname(call_stack[0]) << " " << startpos(call_stack[0]) << ")\n";
+          ss << '\n' << detail << '\n';
+          ss << "  " << fname(call_stack[0]) << " (" << startpos(call_stack[0]) << ") '" << pretty(call_stack[0]) << "'";
+          for (size_t j = 1; j < call_stack.size(); ++j) {
+            if (id(call_stack[j]) != chaiscript::AST_Node_Type::Block
+                && id(call_stack[j]) != chaiscript::AST_Node_Type::File)
+            {
+              ss << '\n';
+              ss << "  from " << fname(call_stack[j]) << " (" << startpos(call_stack[j]) << ") '" << pretty(call_stack[j]) << "'";
+            }
+          }
+        }
+        ss << '\n';
+        return ss.str();
+      }
+
+      ~eval_error() noexcept override = default;
+
+    private:
+
+      template<typename T>
+        static AST_Node_Type id(const T& t)
+        {
+          return t.identifier;
+        }
+
+      template<typename T>
+        static std::string pretty(const T& t)
+        {
+          return t.pretty_print();
+        }
+
+      template<typename T>
+        static const std::string &fname(const T& t)
+        {
+          return t.filename();
+        }
+
+      template<typename T>
+        static std::string startpos(const T& t)
+        {
+          std::ostringstream oss;
+          oss << t.start().line << ", " << t.start().column;
+          return oss.str();
+        }
+
+      static std::string format_why(const std::string &t_why)
+      {
+        return "Error: \"" + t_why + "\"";
+      }
+
+      static std::string format_types(const Const_Proxy_Function &t_func,
+          bool t_dot_notation,
+          const chaiscript::detail::Dispatch_Engine &t_ss)
+      {
+        assert(t_func);
+        int arity = t_func->get_arity();
+        std::vector<Type_Info> types = t_func->get_param_types();
+
+        std::string retval;
+        if (arity == -1)
+        {
+          retval = "(...)";
+          if (t_dot_notation)
+          {
+            retval = "(Object)." + retval;
+          }
+        } else if (types.size() <= 1) {
+          retval = "()";
+        } else {
+          std::stringstream ss;
+          ss << "(";
+
+          std::string paramstr;
+
+          for (size_t index = 1;
+               index != types.size();
+               ++index)
+          {
+            paramstr += (types[index].is_const()?"const ":"");
+            paramstr += t_ss.get_type_name(types[index]);
+
+            if (index == 1 && t_dot_notation)
+            {
+              paramstr += ").(";
+              if (types.size() == 2)
+              {
+                paramstr += ", ";
+              }
+            } else {
+              paramstr += ", ";
+            }
+          }
+
+          ss << paramstr.substr(0, paramstr.size() - 2);
+
+          ss << ")";
+          retval = ss.str();
+        }
+
+
+        std::shared_ptr<const dispatch::Dynamic_Proxy_Function> dynfun 
+          = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(t_func);
+
+        if (dynfun && dynfun->has_parse_tree())
+        {
+          Proxy_Function f = dynfun->get_guard();
+
+          if (f)
+          {
+            auto dynfunguard = std::dynamic_pointer_cast<const dispatch::Dynamic_Proxy_Function>(f);
+            if (dynfunguard && dynfunguard->has_parse_tree())
+            {
+              retval += " : " + format_guard(dynfunguard->get_parse_tree());
+            }
+          }
+
+          retval += "\n          Defined at " + format_location(dynfun->get_parse_tree());
+        }
+
+        return retval;
+      }
+
+      template<typename T>
+        static std::string format_guard(const T &t)
+        {
+          return t.pretty_print();
+        }
+
+      template<typename T>
+        static std::string format_location(const T &t)
+        {
+          std::ostringstream oss;
+          oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")"; 
+          return oss.str();
+        }
+
+      static std::string format_detail(const std::vector<chaiscript::Const_Proxy_Function> &t_functions,
+          bool t_dot_notation,
+          const chaiscript::detail::Dispatch_Engine &t_ss)
+      {
+        std::stringstream ss;
+        if (t_functions.size() == 1)
+        {
+          assert(t_functions[0]);
+          ss << "  Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n';
+        } else {
+          ss << "  " << t_functions.size() << " overloads available:\n";
+
+          for (const auto & t_function : t_functions)
+          {
+            ss << "      " << format_types((t_function), t_dot_notation, t_ss) << '\n';
+          }
+
+        }
+
+        return ss.str();
+
+      }
+
+      static std::string format_parameters(const std::vector<Boxed_Value> &t_parameters,
+          bool t_dot_notation,
+          const chaiscript::detail::Dispatch_Engine &t_ss)
+      {
+        std::stringstream ss;
+        ss << "(";
+
+        if (!t_parameters.empty())
+        {
+          std::string paramstr;
+
+          for (auto itr = t_parameters.begin();
+               itr != t_parameters.end();
+               ++itr)
+          {
+            paramstr += (itr->is_const()?"const ":"");
+            paramstr += t_ss.type_name(*itr);
+
+            if (itr == t_parameters.begin() && t_dot_notation)
+            {
+              paramstr += ").(";
+              if (t_parameters.size() == 1)
+              {
+                paramstr += ", ";
+              }
+            } else {
+              paramstr += ", ";
+            }
+          }
+
+          ss << paramstr.substr(0, paramstr.size() - 2);
+        }
+        ss << ")";
+
+        return ss.str();
+      }
+
+      static std::string format_filename(const std::string &t_fname)
+      {
+        std::stringstream ss;
+
+        if (t_fname != "__EVAL__")
+        {
+          ss << "in '" << t_fname << "' ";
+        } else {
+          ss << "during evaluation ";
+        }
+
+        return ss.str();
+      }
+
+      static std::string format_location(const File_Position &t_where)
+      {
+        std::stringstream ss;
+        ss << "at (" << t_where.line << ", " << t_where.column << ")";
+        return ss.str();
+      }
+
+      static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname,
+          const std::vector<Boxed_Value> &t_parameters, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss)
+      {
+        std::stringstream ss;
+
+        ss << format_why(t_why);
+        ss << " ";
+
+        ss << "With parameters: " << format_parameters(t_parameters, t_dot_notation, t_ss);
+        ss << " ";
+
+        ss << format_filename(t_fname);
+        ss << " ";
+
+        ss << format_location(t_where);
+
+        return ss.str();
+      }
+
+      static std::string format(const std::string &t_why, 
+          const std::vector<Boxed_Value> &t_parameters, 
+          bool t_dot_notation,
+          const chaiscript::detail::Dispatch_Engine &t_ss)
+      {
+        std::stringstream ss;
+
+        ss << format_why(t_why);
+        ss << " ";
+
+        ss << "With parameters: " << format_parameters(t_parameters, t_dot_notation, t_ss);
+        ss << " ";
+
+        return ss.str();
+      }
+
+      static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname)
+      {
+        std::stringstream ss;
+
+        ss << format_why(t_why);
+        ss << " ";
+
+        ss << format_filename(t_fname);
+        ss << " ";
+
+        ss << format_location(t_where);
+
+        return ss.str();
+      }
+    };
+
+
+    /// Errors generated when loading a file
+    struct file_not_found_error : std::runtime_error {
+      explicit file_not_found_error(const std::string &t_filename) noexcept
+        : std::runtime_error("File Not Found: " + t_filename)
+      { }
+
+      file_not_found_error(const file_not_found_error &) = default;
+      ~file_not_found_error() noexcept override = default;
+    };
+
+  }
+
+ 
+  /// \brief Struct that doubles as both a parser ast_node and an AST node.
+  struct AST_Node {
+    public:
+      const AST_Node_Type identifier;
+      const std::string text;
+      Parse_Location location;
+
+      const std::string &filename() const {
+        return *location.filename;
+      }
+
+      const File_Position &start() const {
+        return location.start;
+      }
+
+      const File_Position &end() const {
+        return location.end;
+      }
+
+      std::string pretty_print() const
+      {
+        std::ostringstream oss;
+
+        oss << text;
+
+        for (auto & elem : get_children()) {
+          oss << elem.get().pretty_print() << ' ';
+        }
+
+        return oss.str();
+      }
+
+      virtual std::vector<std::reference_wrapper<AST_Node>> get_children() const = 0;
+      virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0;
+
+
+      /// Prints the contents of an AST node, including its children, recursively
+      std::string to_string(const std::string &t_prepend = "") const {
+        std::ostringstream oss;
+
+        oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") "
+            << this->text << " : " << this->location.start.line << ", " << this->location.start.column << '\n';
+
+        for (auto & elem : get_children()) {
+          oss << elem.get().to_string(t_prepend + "  ");
+        }
+        return oss.str();
+      }
+
+
+      static bool get_bool_condition(const Boxed_Value &t_bv, const chaiscript::detail::Dispatch_State &t_ss) {
+        try {
+          return t_ss->boxed_cast<bool>(t_bv);
+        }
+        catch (const exception::bad_boxed_cast &) {
+          throw exception::eval_error("Condition not boolean");
+        }
+      }
+
+
+      virtual ~AST_Node() = default;
+      AST_Node(AST_Node &&) = default;
+      AST_Node &operator=(AST_Node &&) = default;
+      AST_Node(const AST_Node &) = delete;
+      AST_Node& operator=(const AST_Node &) = delete;
+
+
+    protected:
+      AST_Node(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc)
+        : identifier(t_id), text(std::move(t_ast_node_text)),
+          location(std::move(t_loc))
+      {
+      }
+
+
+  };
+
+  struct AST_Node_Trace
+  {
+    const AST_Node_Type identifier;
+    const std::string text;
+    Parse_Location location;
+
+    const std::string &filename() const {
+      return *location.filename;
+    }
+
+    const File_Position &start() const {
+      return location.start;
+    }
+
+    const File_Position &end() const {
+      return location.end;
+    }
+
+    std::string pretty_print() const
+    {
+      std::ostringstream oss;
+
+      oss << text;
+
+      for (const auto & elem : children) {
+        oss << elem.pretty_print() << ' ';
+      }
+
+      return oss.str();
+    }
+
+    std::vector<AST_Node_Trace> get_children(const AST_Node &node)
+    {
+      const auto node_children = node.get_children();
+      return std::vector<AST_Node_Trace>(node_children.begin(), node_children.end());
+    }
+
+    AST_Node_Trace(const AST_Node &node)
+      : identifier(node.identifier), text(node.text),
+      location(node.location), children(get_children(node))
+    {
+    }
+
+
+    std::vector<AST_Node_Trace> children;
+
+  };
+
+  namespace parser {
+    class ChaiScript_Parser_Base
+    {
+      public:
+        virtual AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) = 0;
+        virtual void debug_print(const AST_Node &t, std::string prepend = "") const = 0;
+        virtual void *get_tracer_ptr() = 0;
+        virtual ~ChaiScript_Parser_Base() = default;
+        ChaiScript_Parser_Base() = default;
+        ChaiScript_Parser_Base(ChaiScript_Parser_Base &&) = default;
+        ChaiScript_Parser_Base &operator=(ChaiScript_Parser_Base &&) = delete;
+        ChaiScript_Parser_Base &operator=(const ChaiScript_Parser_Base &&) = delete;
+
+        template<typename T>
+        T &get_tracer()
+        {
+          // to do type check this somehow?
+          return *static_cast<T*>(get_tracer_ptr());
+        }
+
+      protected:
+        ChaiScript_Parser_Base(const ChaiScript_Parser_Base &) = default;
+    };
+  }
+
+  namespace eval
+  {
+    namespace detail
+    {
+      /// Special type for returned values
+      struct Return_Value {
+        Boxed_Value retval;
+
+        explicit Return_Value(Boxed_Value t_return_value) : retval(std::move(t_return_value)) { }
+      };
+
+
+      /// Special type indicating a call to 'break'
+      struct Break_Loop {
+        Break_Loop() = default;
+      };
+
+
+      /// Special type indicating a call to 'continue'
+      struct Continue_Loop {
+        Continue_Loop() = default;
+      };
+
+
+      /// Creates a new scope then pops it on destruction
+      struct Scope_Push_Pop
+      {
+        Scope_Push_Pop(Scope_Push_Pop &&) = default;
+        Scope_Push_Pop& operator=(Scope_Push_Pop &&) = default;
+        Scope_Push_Pop(const Scope_Push_Pop &) = delete;
+        Scope_Push_Pop& operator=(const Scope_Push_Pop &) = delete;
+
+        explicit Scope_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds)
+          : m_ds(t_ds)
+        {
+          m_ds->new_scope(m_ds.stack_holder());
+        }
+
+        ~Scope_Push_Pop()
+        {
+          m_ds->pop_scope(m_ds.stack_holder());
+        }
+
+
+        private:
+          const chaiscript::detail::Dispatch_State &m_ds;
+      };
+
+      /// Creates a new function call and pops it on destruction
+      struct Function_Push_Pop
+      {
+        Function_Push_Pop(Function_Push_Pop &&) = default;
+        Function_Push_Pop& operator=(Function_Push_Pop &&) = default;
+        Function_Push_Pop(const Function_Push_Pop &) = delete;
+        Function_Push_Pop& operator=(const Function_Push_Pop &) = delete;
+
+        explicit Function_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds)
+          : m_ds(t_ds)
+        {
+          m_ds->new_function_call(m_ds.stack_holder(), m_ds.conversion_saves());
+        }
+
+        ~Function_Push_Pop()
+        {
+          m_ds->pop_function_call(m_ds.stack_holder(), m_ds.conversion_saves());
+        }
+
+        void save_params(const std::vector<Boxed_Value> &t_params)
+        {
+          m_ds->save_function_params(t_params);
+        }
+
+        void save_params(std::initializer_list<Boxed_Value> t_params)
+        {
+          m_ds->save_function_params(t_params);
+        }
+
+
+        private:
+          const chaiscript::detail::Dispatch_State &m_ds;
+      };
+
+      /// Creates a new scope then pops it on destruction
+      struct Stack_Push_Pop
+      {
+        Stack_Push_Pop(Stack_Push_Pop &&) = default;
+        Stack_Push_Pop& operator=(Stack_Push_Pop &&) = default;
+        Stack_Push_Pop(const Stack_Push_Pop &) = delete;
+        Stack_Push_Pop& operator=(const Stack_Push_Pop &) = delete;
+
+        explicit Stack_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds)
+          : m_ds(t_ds)
+        {
+          m_ds->new_stack(m_ds.stack_holder());
+        }
+
+        ~Stack_Push_Pop()
+        {
+          m_ds->pop_stack(m_ds.stack_holder());
+        }
+
+
+        private:
+          const chaiscript::detail::Dispatch_State &m_ds;
+      };
+    }
+  }
+}
+
+#endif /* _CHAISCRIPT_COMMON_HPP */
+

+ 710 - 0
chaiscript/language/chaiscript_engine.hpp

@@ -0,0 +1,710 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_ENGINE_HPP_
+#define CHAISCRIPT_ENGINE_HPP_
+
+#include <cassert>
+#include <exception>
+#include <fstream>
+#include <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <stdexcept>
+#include <vector>
+#include <cstring>
+
+#include "../chaiscript_defines.hpp"
+#include "../chaiscript_threading.hpp"
+#include "../dispatchkit/boxed_cast_helper.hpp"
+#include "../dispatchkit/boxed_value.hpp"
+#include "../dispatchkit/dispatchkit.hpp"
+#include "../dispatchkit/type_conversions.hpp"
+#include "../dispatchkit/proxy_functions.hpp"
+#include "chaiscript_common.hpp"
+
+#if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
+#include <dlfcn.h>
+#endif
+
+#if defined(CHAISCRIPT_NO_DYNLOAD)
+#include "chaiscript_unknown.hpp"
+#elif defined(CHAISCRIPT_WINDOWS)
+#include "chaiscript_windows.hpp"
+#elif _POSIX_VERSION
+#include "chaiscript_posix.hpp"
+#else
+#include "chaiscript_unknown.hpp"
+#endif
+
+
+#include "../dispatchkit/exception_specification.hpp"
+
+namespace chaiscript
+{
+
+  namespace detail
+  {
+    typedef std::shared_ptr<Loadable_Module> Loadable_Module_Ptr;
+  }
+
+
+  /// \brief The main object that the ChaiScript user will use.
+  class ChaiScript_Basic {
+
+    mutable chaiscript::detail::threading::shared_mutex m_mutex;
+    mutable chaiscript::detail::threading::recursive_mutex m_use_mutex;
+
+    std::set<std::string> m_used_files;
+    std::map<std::string, detail::Loadable_Module_Ptr> m_loaded_modules;
+    std::set<std::string> m_active_loaded_modules;
+
+    std::vector<std::string> m_module_paths;
+    std::vector<std::string> m_use_paths;
+
+    std::unique_ptr<parser::ChaiScript_Parser_Base> m_parser;
+
+    chaiscript::detail::Dispatch_Engine m_engine;
+
+    /// Evaluates the given string in by parsing it and running the results through the evaluator
+    Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/  = false) 
+    {
+      try {
+        const auto p = m_parser->parse(t_input, t_filename);
+        return p->eval(chaiscript::detail::Dispatch_State(m_engine));
+      }
+      catch (chaiscript::eval::detail::Return_Value &rv) {
+        return rv.retval;
+      }
+    }
+
+
+
+    /// Evaluates the given file and looks in the 'use' paths
+    const Boxed_Value internal_eval_file(const std::string &t_filename) {
+      for (const auto &path : m_use_paths)
+      {
+        try {
+          const auto appendedpath = path + t_filename;
+          return do_eval(load_file(appendedpath), appendedpath, true);
+        } catch (const exception::file_not_found_error &) {
+          // failed to load, try the next path
+        } catch (const exception::eval_error &t_ee) {
+          throw Boxed_Value(t_ee);
+        }
+      }
+
+      // failed to load by any name
+      throw exception::file_not_found_error(t_filename);
+
+    }
+
+
+
+    /// Evaluates the given string, used during eval() inside of a script
+    const Boxed_Value internal_eval(const std::string &t_e) {
+      try {
+        return do_eval(t_e, "__EVAL__", true);
+      } catch (const exception::eval_error &t_ee) {
+        throw Boxed_Value(t_ee);
+      }
+    }
+
+    /// Returns the current evaluation m_engine
+    chaiscript::detail::Dispatch_Engine &get_eval_engine() {
+      return m_engine;
+    }
+
+    /// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude.
+    void build_eval_system(const ModulePtr &t_lib, const std::vector<Options> &t_opts) {
+      if (t_lib)
+      {
+        add(t_lib);
+      }
+
+      m_engine.add(fun([this](){ m_engine.dump_system(); }), "dump_system");
+      m_engine.add(fun([this](const Boxed_Value &t_bv){ m_engine.dump_object(t_bv); }), "dump_object");
+      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_type){ return m_engine.is_type(t_bv, t_type); }), "is_type");
+      m_engine.add(fun([this](const Boxed_Value &t_bv){ return m_engine.type_name(t_bv); }), "type_name");
+      m_engine.add(fun([this](const std::string &t_f){ return m_engine.function_exists(t_f); }), "function_exists");
+      m_engine.add(fun([this](){ return m_engine.get_function_objects(); }), "get_functions");
+      m_engine.add(fun([this](){ return m_engine.get_scripting_objects(); }), "get_objects");
+
+      m_engine.add(
+          dispatch::make_dynamic_proxy_function(
+              [this](const std::vector<Boxed_Value> &t_params) {
+                return m_engine.call_exists(t_params);
+              })
+          , "call_exists");
+
+
+      m_engine.add(fun(
+            [=](const dispatch::Proxy_Function_Base &t_fun, const std::vector<Boxed_Value> &t_params) -> Boxed_Value {
+              Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves());
+              return t_fun(t_params, s);
+            }), "call");
+
+
+      m_engine.add(fun([this](const Type_Info &t_ti){ return m_engine.get_type_name(t_ti); }), "name");
+
+      m_engine.add(fun([this](const std::string &t_type_name, bool t_throw){ return m_engine.get_type(t_type_name, t_throw); }), "type");
+      m_engine.add(fun([this](const std::string &t_type_name){ return m_engine.get_type(t_type_name, true); }), "type");
+
+      m_engine.add(fun(
+            [=](const Type_Info &t_from, const Type_Info &t_to, const std::function<Boxed_Value (const Boxed_Value &)> &t_func) {
+              m_engine.add(chaiscript::type_conversion(t_from, t_to, t_func));
+            }
+          ), "add_type_conversion");
+
+
+
+      if (std::find(t_opts.begin(), t_opts.end(), Options::No_Load_Modules) == t_opts.end()
+          && std::find(t_opts.begin(), t_opts.end(), Options::Load_Modules) != t_opts.end()) 
+      {
+        m_engine.add(fun([this](const std::string &t_module, const std::string &t_file){ return load_module(t_module, t_file); }), "load_module");
+        m_engine.add(fun([this](const std::string &t_module){ return load_module(t_module); }), "load_module");
+      }
+
+      if (std::find(t_opts.begin(), t_opts.end(), Options::No_External_Scripts) == t_opts.end()
+          && std::find(t_opts.begin(), t_opts.end(), Options::External_Scripts) != t_opts.end())
+      {
+        m_engine.add(fun([this](const std::string &t_file){ return use(t_file); }), "use");
+        m_engine.add(fun([this](const std::string &t_file){ return internal_eval_file(t_file); }), "eval_file");
+      }
+
+      m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval");
+      m_engine.add(fun([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval");
+
+      m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse");
+      m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse");
+
+
+      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const");
+      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global");
+      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global");
+    }
+
+
+    /// Helper function for loading a file
+    static std::string load_file(const std::string &t_filename) {
+      std::ifstream infile(t_filename.c_str(), std::ios::in | std::ios::ate | std::ios::binary );
+
+      if (!infile.is_open()) {
+        throw chaiscript::exception::file_not_found_error(t_filename);
+      }
+
+      const auto size = infile.tellg();
+      infile.seekg(0, std::ios::beg);
+
+      assert(size >= 0);
+
+      if (size == std::streampos(0))
+      {
+        return std::string();
+      } else {
+        std::vector<char> v(static_cast<size_t>(size));
+        infile.read(&v[0], size);
+        return std::string(v.begin(), v.end());
+      }
+    }
+
+    std::vector<std::string> ensure_minimum_path_vec(std::vector<std::string> paths)
+    {
+      if (paths.empty()) { return {""}; }
+      else { return paths; }
+    }
+
+  public:
+
+    /// \brief Constructor for ChaiScript
+    /// \param[in] t_lib Standard library to apply to this ChaiScript instance
+    /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module
+    /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file
+    ChaiScript_Basic(const ModulePtr &t_lib,
+                     std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
+                     std::vector<std::string> t_module_paths = {},
+                     std::vector<std::string> t_use_paths = {},
+                     const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options())
+      : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))),
+        m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))),
+        m_parser(std::move(parser)),
+        m_engine(*m_parser)
+    {
+#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
+      // If on Unix, add the path of the current executable to the module search path
+      // as windows would do
+
+      union cast_union
+      {
+        Boxed_Value (ChaiScript_Basic::*in_ptr)(const std::string&);
+        void *out_ptr;
+      };
+
+      Dl_info rInfo; 
+      memset( &rInfo, 0, sizeof(rInfo) ); 
+      cast_union u;
+      u.in_ptr = &ChaiScript_Basic::use;
+      if ( (dladdr(static_cast<void*>(u.out_ptr), &rInfo) != 0) && (rInfo.dli_fname != nullptr) ) { 
+        std::string dllpath(rInfo.dli_fname);
+        const size_t lastslash = dllpath.rfind('/');
+        if (lastslash != std::string::npos)
+        {
+          dllpath.erase(lastslash);
+        }
+
+        // Let's see if this is a link that we should expand
+        std::vector<char> buf(2048);
+        const auto pathlen = readlink(dllpath.c_str(), &buf.front(), buf.size());
+        if (pathlen > 0 && static_cast<size_t>(pathlen) < buf.size())
+        {
+          dllpath = std::string(&buf.front(), static_cast<size_t>(pathlen));
+        }
+
+        m_module_paths.insert(m_module_paths.begin(), dllpath+"/");
+      }
+#endif
+      build_eval_system(t_lib, t_opts);
+    }
+
+#ifndef CHAISCRIPT_NO_DYNLOAD
+    /// \brief Constructor for ChaiScript.
+    /// 
+    /// This version of the ChaiScript constructor attempts to find the stdlib module to load
+    /// at runtime generates an error if it cannot be found.
+    ///
+    /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module
+    /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file
+    explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
+                     std::vector<std::string> t_module_paths = {},
+                     std::vector<std::string> t_use_paths = {},
+                     const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options())
+      : ChaiScript_Basic({}, std::move(parser), t_module_paths, t_use_paths, t_opts)
+    {
+      try {
+        // attempt to load the stdlib
+        load_module("chaiscript_stdlib-" + Build_Info::version());
+      } catch (const exception::load_module_error &t_err) {
+        std::cout << "An error occured while trying to load the chaiscript standard library.\n"
+                  << "\n"
+                  << "You must either provide a standard library, or compile it in.\n"
+                  << "For an example of compiling the standard library in,\n"
+                  << "see: https://gist.github.com/lefticus/9456197\n"
+                  << "Compiling the stdlib in is the recommended and MOST SUPPORTED method.\n"
+                  << "\n"
+                  << "\n"
+                  << t_err.what();
+        throw;
+      }
+    }
+#else // CHAISCRIPT_NO_DYNLOAD
+explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
+                          std::vector<std::string> t_module_paths = {},
+                          std::vector<std::string> t_use_paths = {},
+                          const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options()) = delete;
+#endif
+
+    parser::ChaiScript_Parser_Base &get_parser()
+    {
+      return *m_parser;
+    }
+
+    const Boxed_Value eval(const AST_Node &t_ast)
+    {
+      try {
+        return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine));
+      } catch (const exception::eval_error &t_ee) {
+        throw Boxed_Value(t_ee);
+      }
+    }
+
+    AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false)
+    {
+      auto ast = m_parser->parse(t_input, "PARSE");
+      if (t_debug_print) {
+        m_parser->debug_print(*ast);
+      }
+      return ast;
+    }
+
+
+    std::string get_type_name(const Type_Info &ti) const
+    {
+      return m_engine.get_type_name(ti);
+    }
+
+    template<typename T>
+    std::string get_type_name() const
+    {
+      return get_type_name(user_type<T>());
+    }
+
+
+    /// \brief Loads and parses a file. If the file is already, it is not reloaded
+    /// The use paths specified at ChaiScript construction time are searched for the 
+    /// requested file.
+    ///
+    /// \param[in] t_filename Filename to load and evaluate
+    Boxed_Value use(const std::string &t_filename)
+    {
+      for (const auto &path : m_use_paths)
+      {
+        try {
+          const auto appendedpath = path + t_filename;
+
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
+          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
+
+          Boxed_Value retval;
+
+          if (m_used_files.count(appendedpath) == 0)
+          {
+            l2.unlock();
+            retval = eval_file(appendedpath);
+            l2.lock();
+            m_used_files.insert(appendedpath);
+          }
+
+          return retval; // return, we loaded it, or it was already loaded
+        } catch (const exception::file_not_found_error &) {
+          // failed to load, try the next path
+        }
+      }
+
+      // failed to load by any name
+      throw exception::file_not_found_error(t_filename);
+    }
+
+    /// \brief Adds a constant object that is available in all contexts and to all threads
+    /// \param[in] t_bv Boxed_Value to add as a global
+    /// \param[in] t_name Name of the value to add
+    /// \throw chaiscript::exception::global_non_const If t_bv is not a constant object
+    /// \sa Boxed_Value::is_const
+    ChaiScript_Basic &add_global_const(const Boxed_Value &t_bv, const std::string &t_name)
+    {
+      Name_Validator::validate_object_name(t_name);
+      m_engine.add_global_const(t_bv, t_name);
+      return *this;
+    }
+
+    /// \brief Adds a mutable object that is available in all contexts and to all threads
+    /// \param[in] t_bv Boxed_Value to add as a global
+    /// \param[in] t_name Name of the value to add
+    /// \warning The user is responsible for making sure the object is thread-safe if necessary
+    ///          ChaiScript is thread-safe but provides no threading locking mechanism to the script
+    ChaiScript_Basic &add_global(const Boxed_Value &t_bv, const std::string &t_name)
+    {
+      Name_Validator::validate_object_name(t_name);
+      m_engine.add_global(t_bv, t_name);
+      return *this;
+    }
+
+    ChaiScript_Basic &set_global(const Boxed_Value &t_bv, const std::string &t_name)
+    {
+      Name_Validator::validate_object_name(t_name);
+      m_engine.set_global(t_bv, t_name);
+      return *this;
+    }
+
+    /// \brief Represents the current state of the ChaiScript system. State and be saved and restored
+    /// \warning State object does not contain the user defined type conversions of the engine. They
+    ///          are left out due to performance considerations involved in tracking the state
+    /// \sa ChaiScript::get_state
+    /// \sa ChaiScript::set_state
+    struct State
+    {
+      std::set<std::string> used_files;
+      chaiscript::detail::Dispatch_Engine::State engine_state;
+      std::set<std::string> active_loaded_modules;
+    };
+
+    /// \brief Returns a state object that represents the current state of the global system
+    ///
+    /// The global system includes the reserved words, global const objects, functions and types.
+    /// local variables are thread specific and not included.
+    ///
+    /// \return Current state of the global system
+    ///
+    /// \b Example:
+    ///
+    /// \code
+    /// chaiscript::ChaiScript chai;
+    /// chaiscript::ChaiScript::State s = chai.get_state(); // represents bootstrapped initial state
+    /// \endcode
+    State get_state() const
+    {
+      chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
+      chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
+
+      State s;
+      s.used_files = m_used_files;
+      s.engine_state = m_engine.get_state();
+      s.active_loaded_modules = m_active_loaded_modules;
+      return s;
+    }
+
+    /// \brief Sets the state of the system
+    ///
+    /// The global system includes the reserved words, global objects, functions and types.
+    /// local variables are thread specific and not included.
+    ///
+    /// \param[in] t_state New state to set
+    ///
+    /// \b Example:
+    /// \code
+    /// chaiscript::ChaiScript chai;
+    /// chaiscript::ChaiScript::State s = chai.get_state(); // get initial state
+    /// chai.add(chaiscript::fun(&somefunction), "somefunction");
+    /// chai.set_state(s); // restore initial state, which does not have the recently added "somefunction"
+    /// \endcode
+    void set_state(const State &t_state)
+    {
+      chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
+      chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
+
+      m_used_files = t_state.used_files;
+      m_active_loaded_modules = t_state.active_loaded_modules;
+      m_engine.set_state(t_state.engine_state);
+    }
+
+    /// \returns All values in the local thread state, added through the add() function
+    std::map<std::string, Boxed_Value> get_locals() const
+    {
+      return m_engine.get_locals();
+    }
+
+    /// \brief Sets all of the locals for the current thread state.
+    ///
+    /// \param[in] t_locals The map<name, value> set of variables to replace the current state with
+    ///
+    /// Any existing locals are removed and the given set of variables is added
+    void set_locals(const std::map<std::string, Boxed_Value> &t_locals)
+    {
+      m_engine.set_locals(t_locals);
+    }
+
+    /// \brief Adds a type, function or object to ChaiScript. Objects are added to the local thread state.
+    /// \param[in] t_t Item to add
+    /// \param[in] t_name Name of item to add
+    /// \returns Reference to current ChaiScript object
+    /// 
+    /// \b Examples:
+    /// \code
+    /// chaiscript::ChaiScript chai;
+    /// chai.add(chaiscript::user_type<MyClass>(), "MyClass"); // Add explicit type info (not strictly necessary)
+    /// chai.add(chaiscript::fun(&MyClass::function), "function"); // Add a class method
+    /// MyClass obj;
+    /// chai.add(chaiscript::var(&obj), "obj"); // Add a pointer to a locally defined object
+    /// \endcode
+    ///
+    /// \sa \ref adding_items
+    template<typename T>
+    ChaiScript_Basic &add(const T &t_t, const std::string &t_name)
+    {
+      Name_Validator::validate_object_name(t_name);
+      m_engine.add(t_t, t_name);
+      return *this;
+    }
+
+    /// \brief Add a new conversion for upcasting to a base class
+    /// \sa chaiscript::base_class
+    /// \param[in] d Base class / parent class 
+    ///
+    /// \b Example:
+    /// \code
+    /// chaiscript::ChaiScript chai;
+    /// chai.add(chaiscript::base_class<std::runtime_error, chaiscript::dispatch_error>());
+    /// \endcode
+    ChaiScript_Basic &add(const Type_Conversion &d)
+    {
+      m_engine.add(d);
+      return *this;
+    }
+
+    /// \brief Adds all elements of a module to ChaiScript runtime
+    /// \param[in] t_p The module to add.
+    /// \sa chaiscript::Module
+    ChaiScript_Basic &add(const ModulePtr &t_p)
+    {
+      t_p->apply(*this, this->get_eval_engine());
+      return *this;
+    }
+
+    /// \brief Load a binary module from a dynamic library. Works on platforms that support
+    ///        dynamic libraries.
+    /// \param[in] t_module_name Name of the module to load
+    ///
+    /// The module is searched for in the registered module path folders (chaiscript::ChaiScript::ChaiScript)
+    /// and with standard prefixes and postfixes: ("lib"|"")\<t_module_name\>(".dll"|".so"|".bundle"|"").
+    ///
+    /// Once the file is located, the system looks for the symbol "create_chaiscript_module_\<t_module_name\>".
+    /// If no file can be found matching the search criteria and containing the appropriate entry point 
+    /// (the symbol mentioned above), an exception is thrown.
+    ///
+    /// \throw chaiscript::exception::load_module_error In the event that no matching module can be found.
+    std::string load_module(const std::string &t_module_name)
+    {
+#ifdef CHAISCRIPT_NO_DYNLOAD
+      (void)t_module_name; // -Wunused-parameter
+      throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
+#else
+      std::vector<exception::load_module_error> errors;
+      std::string version_stripped_name = t_module_name;
+      size_t version_pos = version_stripped_name.find("-" + Build_Info::version());
+      if (version_pos != std::string::npos)
+      {
+        version_stripped_name.erase(version_pos);
+      }
+
+      std::vector<std::string> prefixes{"lib", "cyg", ""};
+
+      std::vector<std::string> postfixes{".dll", ".so", ".bundle", ""};
+
+      for (auto & elem : m_module_paths)
+      {
+        for (auto & prefix : prefixes)
+        {
+          for (auto & postfix : postfixes)
+          {
+            try {
+              const auto name = elem + prefix + t_module_name + postfix;
+              // std::cerr << "trying location: " << name << '\n';
+              load_module(version_stripped_name, name);
+              return name;
+            } catch (const chaiscript::exception::load_module_error &e) {
+              // std::cerr << "error: " << e.what() << '\n';
+              errors.push_back(e);
+              // Try next set
+            }
+          }
+        }
+      }
+
+      throw chaiscript::exception::load_module_error(t_module_name, errors);
+#endif
+    }
+
+    /// \brief Load a binary module from a dynamic library. Works on platforms that support
+    ///        dynamic libraries.
+    ///
+    /// \param[in] t_module_name Module name to load
+    /// \param[in] t_filename Ignore normal filename search process and use specific filename
+    ///
+    /// \sa ChaiScript::load_module(const std::string &t_module_name)
+    void load_module(const std::string &t_module_name, const std::string &t_filename)
+    {
+      chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
+
+      if (m_loaded_modules.count(t_module_name) == 0)
+      {
+        detail::Loadable_Module_Ptr lm(new detail::Loadable_Module(t_module_name, t_filename));
+        m_loaded_modules[t_module_name] = lm;
+        m_active_loaded_modules.insert(t_module_name);
+        add(lm->m_moduleptr);
+      } else if (m_active_loaded_modules.count(t_module_name) == 0) {
+        m_active_loaded_modules.insert(t_module_name);
+        add(m_loaded_modules[t_module_name]->m_moduleptr);
+      } 
+    }
+
+
+    /// \brief Evaluates a string. Equivalent to ChaiScript::eval.
+    ///
+    /// \param[in] t_script Script to execute
+    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
+    ///
+    /// \return result of the script execution
+    /// 
+    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
+    Boxed_Value operator()(const std::string &t_script, const Exception_Handler &t_handler = Exception_Handler())
+    {
+      return eval(t_script, t_handler);
+    }
+
+    /// \brief Evaluates a string and returns a typesafe result.
+    ///
+    /// \tparam T Type to extract from the result value of the script execution
+    /// \param[in] t_input Script to execute
+    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
+    /// \param[in] t_filename Optional filename to report to the user for where the error occured. Useful
+    ///                       in special cases where you are loading a file internally instead of using eval_file
+    ///
+    /// \return result of the script execution
+    /// 
+    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
+    /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted
+    ///        to the requested type.
+    template<typename T>
+    T eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__")
+    {
+      return m_engine.boxed_cast<T>(eval(t_input, t_handler, t_filename));
+    }
+
+    /// \brief casts an object while applying any Dynamic_Conversion available
+    template<typename Type>
+      decltype(auto) boxed_cast(const Boxed_Value &bv) const
+      {
+        return(m_engine.boxed_cast<Type>(bv));
+      }
+ 
+
+    /// \brief Evaluates a string.
+    ///
+    /// \param[in] t_input Script to execute
+    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
+    /// \param[in] t_filename Optional filename to report to the user for where the error occurred. Useful
+    ///                       in special cases where you are loading a file internally instead of using eval_file
+    ///
+    /// \return result of the script execution
+    /// 
+    /// \throw exception::eval_error In the case that evaluation fails.
+    Boxed_Value eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__")
+    {
+      try {
+        return do_eval(t_input, t_filename);
+      } catch (Boxed_Value &bv) {
+        if (t_handler) {
+          t_handler->handle(bv, m_engine);
+        }
+        throw;
+      }
+    }
+
+    /// \brief Loads the file specified by filename, evaluates it, and returns the result.
+    /// \param[in] t_filename File to load and parse.
+    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
+    /// \return result of the script execution
+    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
+    Boxed_Value eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
+      return eval(load_file(t_filename), t_handler, t_filename);
+    }
+
+    /// \brief Loads the file specified by filename, evaluates it, and returns the type safe result.
+    /// \tparam T Type to extract from the result value of the script execution
+    /// \param[in] t_filename File to load and parse.
+    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
+    /// \return result of the script execution
+    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
+    /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted
+    ///        to the requested type.
+    template<typename T>
+    T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
+      return m_engine.boxed_cast<T>(eval_file(t_filename, t_handler));
+    }
+  };
+
+}
+#endif /* CHAISCRIPT_ENGINE_HPP_ */
+

+ 1522 - 0
chaiscript/language/chaiscript_eval.hpp

@@ -0,0 +1,1522 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_EVAL_HPP_
+#define CHAISCRIPT_EVAL_HPP_
+
+#include <exception>
+#include <functional>
+#include <limits>
+#include <map>
+#include <memory>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "../chaiscript_defines.hpp"
+#include "../dispatchkit/boxed_cast.hpp"
+#include "../dispatchkit/boxed_number.hpp"
+#include "../dispatchkit/boxed_value.hpp"
+#include "../dispatchkit/dispatchkit.hpp"
+#include "../dispatchkit/dynamic_object_detail.hpp"
+#include "../dispatchkit/proxy_functions.hpp"
+#include "../dispatchkit/proxy_functions_detail.hpp"
+#include "../dispatchkit/register_function.hpp"
+#include "../dispatchkit/type_info.hpp"
+#include "chaiscript_algebraic.hpp"
+#include "chaiscript_common.hpp"
+
+namespace chaiscript {
+namespace exception {
+class bad_boxed_cast;
+}  // namespace exception
+}  // namespace chaiscript
+
+namespace chaiscript
+{
+  /// \brief Classes and functions that are part of the runtime eval system
+  namespace eval
+  {
+    template<typename T> struct AST_Node_Impl;
+
+    template<typename T> using AST_Node_Impl_Ptr = typename std::unique_ptr<AST_Node_Impl<T>>;
+
+    namespace detail
+    {
+      /// Helper function that will set up the scope around a function call, including handling the named function parameters
+      template<typename T>
+      static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl<T> &t_node, const std::vector<std::string> &t_param_names, const std::vector<Boxed_Value> &t_vals, const std::map<std::string, Boxed_Value> *t_locals=nullptr, bool has_this_capture = false) {
+        chaiscript::detail::Dispatch_State state(t_ss);
+
+        const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{
+          auto &stack = t_ss.get_stack_data(state.stack_holder()).back();
+          if (!stack.empty() && stack.back().first == "__this") {
+            return &stack.back().second;
+          } else if (!t_vals.empty()) {
+            return &t_vals[0];
+          } else {
+            return nullptr;
+          }
+        }();
+
+        chaiscript::eval::detail::Stack_Push_Pop tpp(state);
+        if (thisobj && !has_this_capture) { state.add_object("this", *thisobj); }
+
+        if (t_locals) {
+          for (const auto &local : *t_locals) {
+            state.add_object(local.first, local.second);
+          }
+        }
+
+        for (size_t i = 0; i < t_param_names.size(); ++i) {
+          if (t_param_names[i] != "this") {
+            state.add_object(t_param_names[i], t_vals[i]);
+          }
+        }
+
+        try {
+          return t_node.eval(state);
+        } catch (detail::Return_Value &rv) {
+          return std::move(rv.retval);
+        } 
+      }
+    }
+
+    template<typename T>
+    struct AST_Node_Impl : AST_Node 
+    {
+      AST_Node_Impl(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc, 
+               std::vector<AST_Node_Impl_Ptr<T>> t_children = std::vector<AST_Node_Impl_Ptr<T>>())
+        : AST_Node(std::move(t_ast_node_text), t_id, std::move(t_loc)),
+          children(std::move(t_children))
+      {
+      }
+
+      static bool get_scoped_bool_condition(const AST_Node_Impl<T> &node, const chaiscript::detail::Dispatch_State &t_ss) {
+        chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+        return get_bool_condition(node.eval(t_ss), t_ss);
+      }
+
+
+      std::vector<std::reference_wrapper<AST_Node>> get_children() const final {
+        std::vector<std::reference_wrapper<AST_Node>> retval;
+        retval.reserve(children.size());
+        for (auto &&child : children) {
+          retval.emplace_back(*child);
+        }
+
+        return retval;
+      }
+
+      Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final
+      {
+        try {
+          T::trace(t_e, this);
+          return eval_internal(t_e);
+        } catch (exception::eval_error &ee) {
+          ee.call_stack.push_back(*this);
+          throw;
+        }
+      }
+
+      std::vector<AST_Node_Impl_Ptr<T>> children;
+
+      protected:
+        virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const
+        {
+          throw std::runtime_error("Undispatched ast_node (internal error)");
+        }
+    };
+
+
+    template<typename T>
+    struct Compiled_AST_Node : AST_Node_Impl<T> {
+        Compiled_AST_Node(AST_Node_Impl_Ptr<T> t_original_node, std::vector<AST_Node_Impl_Ptr<T>> t_children,
+            std::function<Boxed_Value (const std::vector<AST_Node_Impl_Ptr<T>> &, const chaiscript::detail::Dispatch_State &t_ss)> t_func) :
+          AST_Node_Impl<T>(t_original_node->text, AST_Node_Type::Compiled, t_original_node->location, std::move(t_children)),
+          m_func(std::move(t_func)),
+          m_original_node(std::move(t_original_node))
+        { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          return m_func(this->children, t_ss);
+        }
+
+        std::function<Boxed_Value (const std::vector<AST_Node_Impl_Ptr<T>> &, const chaiscript::detail::Dispatch_State &t_ss)> m_func;
+        AST_Node_Impl_Ptr<T> m_original_node;
+    };
+
+
+    template<typename T>
+    struct Fold_Right_Binary_Operator_AST_Node : AST_Node_Impl<T> {
+        Fold_Right_Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children, Boxed_Value t_rhs) :
+          AST_Node_Impl<T>(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)),
+          m_oper(Operators::to_operator(t_oper)),
+          m_rhs(std::move(t_rhs))
+        { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          return do_oper(t_ss, this->text, this->children[0]->eval(t_ss));
+        }
+
+      protected:
+        Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, 
+            const std::string &t_oper_string, const Boxed_Value &t_lhs) const
+        {
+          try {
+            if (t_lhs.get_type_info().is_arithmetic())
+            {
+              // If it's an arithmetic operation we want to short circuit dispatch
+              try{
+                return Boxed_Number::do_oper(m_oper, t_lhs, m_rhs);
+              } catch (const chaiscript::exception::arithmetic_error &) {
+                throw;
+              } catch (...) {
+                throw exception::eval_error("Error with numeric operator calling: " + t_oper_string);
+              }
+            } else {
+              chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
+              fpp.save_params({t_lhs, m_rhs});
+              return t_ss->call_function(t_oper_string, m_loc, {t_lhs, m_rhs}, t_ss.conversions());
+            }
+          }
+          catch(const exception::dispatch_error &e){
+            throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss);
+          }
+        }
+
+      private:
+        Operators::Opers m_oper;
+        Boxed_Value m_rhs;
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+
+    template<typename T>
+    struct Binary_Operator_AST_Node : AST_Node_Impl<T> {
+        Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)),
+          m_oper(Operators::to_operator(t_oper))
+        { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          auto lhs = this->children[0]->eval(t_ss);
+          auto rhs = this->children[1]->eval(t_ss);
+          return do_oper(t_ss, m_oper, this->text, lhs, rhs);
+        }
+
+      protected:
+        Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, 
+            Operators::Opers t_oper, const std::string &t_oper_string, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) const
+        {
+          try {
+            if (t_oper != Operators::Opers::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic())
+            {
+              // If it's an arithmetic operation we want to short circuit dispatch
+              try{
+                return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs);
+              } catch (const chaiscript::exception::arithmetic_error &) {
+                throw;
+              } catch (...) {
+                throw exception::eval_error("Error with numeric operator calling: " + t_oper_string);
+              }
+            } else {
+              chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
+              fpp.save_params({t_lhs, t_rhs});
+              return t_ss->call_function(t_oper_string, m_loc, {t_lhs, t_rhs}, t_ss.conversions());
+            }
+          }
+          catch(const exception::dispatch_error &e){
+            throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss);
+          }
+        }
+
+      private:
+        Operators::Opers m_oper;
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+
+    template<typename T>
+    struct Constant_AST_Node final : AST_Node_Impl<T> {
+      Constant_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_value)
+        : AST_Node_Impl<T>(t_ast_node_text, AST_Node_Type::Constant, std::move(t_loc)),
+          m_value(std::move(t_value))
+      {
+      }
+
+      explicit Constant_AST_Node(Boxed_Value t_value)
+        : AST_Node_Impl<T>("", AST_Node_Type::Constant, Parse_Location()),
+          m_value(std::move(t_value))
+      {
+      }
+
+      Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override {
+        return m_value;
+      }
+
+      Boxed_Value m_value;
+    };
+
+    template<typename T>
+    struct Id_AST_Node final : AST_Node_Impl<T> {
+        Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc) :
+          AST_Node_Impl<T>(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc))
+        { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          try {
+            return t_ss.get_object(this->text, m_loc);
+          }
+          catch (std::exception &) {
+            throw exception::eval_error("Can not find object: " + this->text);
+          }
+        }
+
+      private:
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+    template<typename T>
+    struct Fun_Call_AST_Node : AST_Node_Impl<T> {
+        Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { 
+            assert(!this->children.empty());
+          }
+
+        template<bool Save_Params>
+        Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const
+        {
+          chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
+
+          std::vector<Boxed_Value> params;
+
+          params.reserve(this->children[1]->children.size());
+          for (const auto &child : this->children[1]->children) {
+            params.push_back(child->eval(t_ss));
+          }
+
+          if (Save_Params) {
+            fpp.save_params(params);
+          }
+
+          Boxed_Value fn(this->children[0]->eval(t_ss));
+
+          try {
+            return (*t_ss->boxed_cast<const dispatch::Proxy_Function_Base *>(fn))(params, t_ss.conversions());
+          }
+          catch(const exception::dispatch_error &e){
+            throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, *t_ss);
+          }
+          catch(const exception::bad_boxed_cast &){
+            try {
+              Const_Proxy_Function f = t_ss->boxed_cast<const Const_Proxy_Function &>(fn);
+              // handle the case where there is only 1 function to try to call and dispatch fails on it
+              throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, {f}, false, *t_ss);
+            } catch (const exception::bad_boxed_cast &) {
+              throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function.");
+            }
+          }
+          catch(const exception::arity_error &e){
+            throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'");
+          }
+          catch(const exception::guard_error &e){
+            throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'");
+          }
+          catch(detail::Return_Value &rv) {
+            return rv.retval;
+          }
+        }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
+        {
+          return do_eval_internal<true>(t_ss);
+        }
+
+    };
+
+
+    template<typename T>
+    struct Unused_Return_Fun_Call_AST_Node final : Fun_Call_AST_Node<T> {
+        Unused_Return_Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          Fun_Call_AST_Node<T>(std::move(t_ast_node_text), std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
+        {
+          return this->template do_eval_internal<false>(t_ss);
+        }
+    };
+
+
+
+
+
+    template<typename T>
+    struct Arg_AST_Node final : AST_Node_Impl<T> {
+        Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { }
+
+    };
+
+    template<typename T>
+    struct Arg_List_AST_Node final : AST_Node_Impl<T> {
+        Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { }
+
+
+        static std::string get_arg_name(const AST_Node_Impl<T> &t_node) {
+          if (t_node.children.empty())
+          {
+            return t_node.text;
+          } else if (t_node.children.size() == 1) {
+            return t_node.children[0]->text;
+          } else {
+            return t_node.children[1]->text;
+          }
+        }
+
+        static std::vector<std::string> get_arg_names(const AST_Node_Impl<T> &t_node) {
+          std::vector<std::string> retval;
+
+          for (const auto &node : t_node.children)
+          {
+            retval.push_back(get_arg_name(*node));
+          }
+
+          return retval;
+        }
+
+        static std::pair<std::string, Type_Info> get_arg_type(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) 
+        {
+          if (t_node.children.size() < 2)
+          {
+            return {};
+          } else {
+            return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)};
+          }
+        }
+
+        static dispatch::Param_Types get_arg_types(const AST_Node_Impl<T> &t_node, const chaiscript::detail::Dispatch_State &t_ss) {
+          std::vector<std::pair<std::string, Type_Info>> retval;
+
+          for (const auto &child : t_node.children)
+          {
+            retval.push_back(get_arg_type(*child, t_ss));
+          }
+
+          return dispatch::Param_Types(std::move(retval));
+        }
+    };
+
+    template<typename T>
+    struct Equation_AST_Node final : AST_Node_Impl<T> {
+        Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children)), 
+          m_oper(Operators::to_operator(this->text))
+        { assert(this->children.size() == 2); }
+
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
+          Boxed_Value rhs = this->children[1]->eval(t_ss); 
+          Boxed_Value lhs = this->children[0]->eval(t_ss);
+
+          if (m_oper != Operators::Opers::invalid && lhs.get_type_info().is_arithmetic() &&
+              rhs.get_type_info().is_arithmetic())
+          {
+            try {
+              return Boxed_Number::do_oper(m_oper, lhs, rhs);
+            } catch (const std::exception &) {
+              throw exception::eval_error("Error with unsupported arithmetic assignment operation");
+            }
+          } else if (m_oper == Operators::Opers::assign) {
+            if (lhs.is_return_value()) {
+              throw exception::eval_error("Error, cannot assign to temporary value.");
+            }
+
+            try {
+
+              if (lhs.is_undef()) {
+                if (!this->children.empty()
+                     && ((this->children[0]->identifier == AST_Node_Type::Reference)
+                         || (!this->children[0]->children.empty()
+                              && this->children[0]->children[0]->identifier == AST_Node_Type::Reference)
+                       )
+                   )
+                  
+                {
+                  /// \todo This does not handle the case of an unassigned reference variable
+                  ///       being assigned outside of its declaration
+                  lhs.assign(rhs);
+                  lhs.reset_return_value();
+                  return rhs;
+                } else {
+                  if (!rhs.is_return_value())
+                  {
+                    rhs = t_ss->call_function("clone", m_clone_loc, {rhs}, t_ss.conversions());
+                  }
+                  rhs.reset_return_value();
+                }
+              }
+
+              try {
+                return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions());
+              }
+              catch(const exception::dispatch_error &e){
+                throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss);
+              }
+            }
+            catch(const exception::dispatch_error &e){
+              throw exception::eval_error("Missing clone or copy constructor for right hand side of equation", e.parameters, e.functions, false, *t_ss);
+            }
+          }
+          else if (this->text == ":=") {
+            if (lhs.is_undef() || Boxed_Value::type_match(lhs, rhs)) {
+              lhs.assign(rhs);
+              lhs.reset_return_value();
+            } else {
+              throw exception::eval_error("Mismatched types in equation");
+            }
+          }
+          else {
+            try {
+              return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions());
+            } catch(const exception::dispatch_error &e){
+              throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss);
+            }
+          }
+
+          return rhs;
+        }
+
+      private:
+        Operators::Opers m_oper;
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+        mutable std::atomic_uint_fast32_t m_clone_loc = {0};
+    };
+
+    template<typename T>
+    struct Global_Decl_AST_Node final : AST_Node_Impl<T> {
+        Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          const std::string &idname =
+            [&]()->const std::string & {
+              if (this->children[0]->identifier == AST_Node_Type::Reference) {
+                return this->children[0]->children[0]->text;
+              } else {
+                return this->children[0]->text;
+              }
+            }();
+
+          return t_ss->add_global_no_throw(Boxed_Value(), idname);
+
+        }
+    };
+
+
+    template<typename T>
+    struct Var_Decl_AST_Node final : AST_Node_Impl<T> {
+        Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          const std::string &idname = this->children[0]->text;
+
+          try {
+            Boxed_Value bv;
+            t_ss.add_object(idname, bv);
+            return bv;
+          } catch (const exception::name_conflict_error &e) {
+            throw exception::eval_error("Variable redefined '" + e.name() + "'");
+          }
+        }
+    };
+
+
+    template<typename T>
+    struct Array_Call_AST_Node final : AST_Node_Impl<T> {
+        Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
+
+          const std::vector<Boxed_Value> params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)};
+
+          try {
+            fpp.save_params(params);
+            return t_ss->call_function("[]", m_loc, params, t_ss.conversions());
+          }
+          catch(const exception::dispatch_error &e){
+            throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss );
+          }
+        }
+
+
+      private:
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+    template<typename T>
+    struct Dot_Access_AST_Node final : AST_Node_Impl<T> {
+        Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children)),
+          m_fun_name(
+              ((this->children[1]->identifier == AST_Node_Type::Fun_Call) || (this->children[1]->identifier == AST_Node_Type::Array_Call))?
+              this->children[1]->children[0]->text:this->children[1]->text) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
+
+
+          Boxed_Value retval = this->children[0]->eval(t_ss);
+          std::vector<Boxed_Value> params{retval};
+
+          bool has_function_params = false;
+          if (this->children[1]->children.size() > 1) {
+            has_function_params = true;
+            for (const auto &child : this->children[1]->children[1]->children) {
+              params.push_back(child->eval(t_ss));
+            }
+          }
+
+          fpp.save_params(params);
+
+          try {
+            retval = t_ss->call_member(m_fun_name, m_loc, std::move(params), has_function_params, t_ss.conversions());
+          }
+          catch(const exception::dispatch_error &e){
+            if (e.functions.empty())
+            {
+              throw exception::eval_error("'" + m_fun_name + "' is not a function.");
+            } else {
+              throw exception::eval_error(std::string(e.what()) + " for function '" + m_fun_name + "'", e.parameters, e.functions, true, *t_ss);
+            }
+          }
+          catch(detail::Return_Value &rv) {
+            retval = std::move(rv.retval);
+          }
+
+          if (this->children[1]->identifier == AST_Node_Type::Array_Call) {
+            try {
+              retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[1]->children[1]->eval(t_ss)}, t_ss.conversions());
+            }
+            catch(const exception::dispatch_error &e){
+              throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss);
+            }
+          }
+
+          return retval;
+        }
+
+      private:
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+        mutable std::atomic_uint_fast32_t m_array_loc = {0};
+        const std::string m_fun_name;
+    };
+
+
+    template<typename T>
+    struct Lambda_AST_Node final : AST_Node_Impl<T> {
+        Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(t_ast_node_text, 
+              AST_Node_Type::Lambda, 
+              std::move(t_loc), 
+              std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()), 
+                                                std::make_move_iterator(std::prev(t_children.end())))
+              ),
+          m_param_names(Arg_List_AST_Node<T>::get_arg_names(*this->children[1])),
+          m_this_capture(has_this_capture(this->children[0]->children)),
+          m_lambda_node(std::move(t_children.back()))
+        { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+
+          const auto captures = [&]()->std::map<std::string, Boxed_Value>{
+            std::map<std::string, Boxed_Value> named_captures;
+            for (const auto &capture : this->children[0]->children) {
+              named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss)));
+            }
+            return named_captures;
+          }();
+
+          const auto numparams = this->children[1]->children.size();
+          const auto param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
+
+          std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
+
+          return Boxed_Value(
+              dispatch::make_dynamic_proxy_function(
+                  [engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, 
+                   this_capture = this->m_this_capture] (const std::vector<Boxed_Value> &t_params)
+                  {
+                    return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture);
+                  },
+                  static_cast<int>(numparams), m_lambda_node, param_types
+                )
+              );
+        }
+
+        static bool has_this_capture(const std::vector<AST_Node_Impl_Ptr<T>> &children) {
+          return std::any_of(std::begin(children), std::end(children),
+                [](const auto &child){
+                  return child->children[0]->text == "this";
+                }
+              );
+        }
+
+      private:
+        const std::vector<std::string> m_param_names;
+        const bool m_this_capture = false;
+        const std::shared_ptr<AST_Node_Impl<T>> m_lambda_node;
+    };
+
+    template<typename T>
+    struct Scopeless_Block_AST_Node final : AST_Node_Impl<T> {
+        Scopeless_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Scopeless_Block, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          const auto num_children = this->children.size();
+          for (size_t i = 0; i < num_children-1; ++i) {
+            this->children[i]->eval(t_ss);
+          }
+          return this->children.back()->eval(t_ss);
+        }
+    };
+
+    template<typename T>
+    struct Block_AST_Node final : AST_Node_Impl<T> {
+        Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+          const auto num_children = this->children.size();
+          for (size_t i = 0; i < num_children-1; ++i) {
+            this->children[i]->eval(t_ss);
+          }
+          return this->children.back()->eval(t_ss);
+        }
+    };
+
+    template<typename T>
+    struct Def_AST_Node final : AST_Node_Impl<T> {
+
+        std::shared_ptr<AST_Node_Impl<T>> m_body_node;
+        std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
+
+        Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), 
+              std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()), 
+                                                std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children, 1)?2:1)))
+              ),
+              m_body_node(get_body_node(std::move(t_children))),
+              m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2))
+
+        { }
+
+        static std::shared_ptr<AST_Node_Impl<T>> get_guard_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec, bool has_guard)
+        {
+          if (has_guard) {
+            return std::move(*std::prev(vec.end(), 2));
+          } else {
+            return {};
+          }
+        }
+
+        static std::shared_ptr<AST_Node_Impl<T>> get_body_node(std::vector<AST_Node_Impl_Ptr<T>> &&vec)
+        {
+          return std::move(vec.back());
+        }
+
+        static bool has_guard(const std::vector<AST_Node_Impl_Ptr<T>> &t_children, const std::size_t offset)
+        {
+          if ((t_children.size() > 2 + offset) && (t_children[1+offset]->identifier == AST_Node_Type::Arg_List)) {
+            if (t_children.size() > 3 + offset) {
+              return true;
+            }
+          }
+          else {
+            if (t_children.size() > 2 + offset) {
+              return true;
+            }
+          }
+          return false;
+        }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+          std::vector<std::string> t_param_names;
+          size_t numparams = 0;
+
+          dispatch::Param_Types param_types;
+
+          if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) {
+            numparams = this->children[1]->children.size();
+            t_param_names = Arg_List_AST_Node<T>::get_arg_names(*this->children[1]);
+            param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[1], t_ss);
+          }
+
+          std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
+          std::shared_ptr<dispatch::Proxy_Function_Base> guard;
+          if (m_guard_node) {
+            guard = dispatch::make_dynamic_proxy_function(
+                [engine, guardnode = m_guard_node, t_param_names](const std::vector<Boxed_Value> &t_params)
+                {
+                  return detail::eval_function(engine, *guardnode, t_param_names, t_params);
+                },
+                static_cast<int>(numparams), m_guard_node);
+          }
+
+          try {
+            const std::string & l_function_name = this->children[0]->text;
+            t_ss->add(
+                dispatch::make_dynamic_proxy_function(
+                  [engine, func_node = m_body_node, t_param_names](const std::vector<Boxed_Value> &t_params)
+                  {
+                    return detail::eval_function(engine, *func_node, t_param_names, t_params);
+                  },
+                  static_cast<int>(numparams), m_body_node,
+                  param_types, guard), l_function_name);
+          } catch (const exception::name_conflict_error &e) {
+            throw exception::eval_error("Function redefined '" + e.name() + "'");
+          }
+          return void_var();
+        }
+
+    };
+
+    template<typename T>
+    struct While_AST_Node final : AST_Node_Impl<T> {
+        While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+          try {
+            while (this->get_scoped_bool_condition(*this->children[0], t_ss)) {
+              try {
+                this->children[1]->eval(t_ss);
+              } catch (detail::Continue_Loop &) {
+                // we got a continue exception, which means all of the remaining 
+                // loop implementation is skipped and we just need to continue to
+                // the next condition test
+              }
+            } 
+          } catch (detail::Break_Loop &) {
+            // loop was broken intentionally
+          }
+
+          return void_var();
+        }
+    };
+
+    template<typename T>
+    struct Class_AST_Node final : AST_Node_Impl<T> {
+        Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+          /// \todo do this better
+          // put class name in current scope so it can be looked up by the attrs and methods
+          t_ss.add_object("_current_class_name", const_var(this->children[0]->text));
+
+          this->children[1]->eval(t_ss);
+
+          return void_var();
+        }
+    };
+
+
+    template<typename T>
+    struct If_AST_Node final : AST_Node_Impl<T> {
+        If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) 
+        { 
+          assert(this->children.size() == 3);
+        }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) {
+            return this->children[1]->eval(t_ss);
+          } else {
+            return this->children[2]->eval(t_ss);
+          }
+        }
+    };
+
+    template<typename T>
+    struct Ranged_For_AST_Node final : AST_Node_Impl<T> {
+        Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Ranged_For, std::move(t_loc), std::move(t_children))
+          { assert(this->children.size() == 3); }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+          const auto get_function = [&t_ss](const std::string &t_name, auto &t_hint){
+            uint_fast32_t hint = t_hint;
+            auto funs = t_ss->get_function(t_name, hint);
+            if (funs.first != hint) { t_hint = uint_fast32_t(funs.first); }
+            return std::move(funs.second);
+          };
+
+          const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) {
+            return dispatch::dispatch(*t_funcs, {t_param}, t_ss.conversions());
+          };
+
+
+          const std::string &loop_var_name = this->children[0]->text;
+          Boxed_Value range_expression_result = this->children[1]->eval(t_ss);
+
+
+          const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing){
+            try {
+              chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+              Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var());
+              for (auto loop_var : ranged_thing) {
+                obj = Boxed_Value(std::move(loop_var));
+                try {
+                  this->children[2]->eval(t_ss);
+                } catch (detail::Continue_Loop &) {
+                }
+              }
+            } catch (detail::Break_Loop &) {
+              // loop broken
+            }
+            return void_var();
+          };
+
+          if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::vector<Boxed_Value>))) {
+            return do_loop(boxed_cast<const std::vector<Boxed_Value> &>(range_expression_result));
+          } else if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::map<std::string, Boxed_Value>))) {
+            return do_loop(boxed_cast<const std::map<std::string, Boxed_Value> &>(range_expression_result));
+          } else {
+            const auto range_funcs = get_function("range", m_range_loc);
+            const auto empty_funcs = get_function("empty", m_empty_loc);
+            const auto front_funcs = get_function("front", m_front_loc);
+            const auto pop_front_funcs = get_function("pop_front", m_pop_front_loc);
+
+            try {
+              const auto range_obj = call_function(range_funcs, range_expression_result);
+              chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+              Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var());
+              while (!boxed_cast<bool>(call_function(empty_funcs, range_obj))) {
+                obj = call_function(front_funcs, range_obj);
+                try {
+                  this->children[2]->eval(t_ss);
+                } catch (detail::Continue_Loop &) {
+                }
+                call_function(pop_front_funcs, range_obj);
+              }
+            } catch (detail::Break_Loop &) {
+              // loop broken
+            }
+            return void_var();
+          }
+
+        }
+
+      private:
+        mutable std::atomic_uint_fast32_t m_range_loc = {0};
+        mutable std::atomic_uint_fast32_t m_empty_loc = {0};
+        mutable std::atomic_uint_fast32_t m_front_loc = {0};
+        mutable std::atomic_uint_fast32_t m_pop_front_loc = {0};
+    };
+
+
+    template<typename T>
+    struct For_AST_Node final : AST_Node_Impl<T> {
+        For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) 
+          { assert(this->children.size() == 4); }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+          try {
+            for (
+                this->children[0]->eval(t_ss);
+                this->get_scoped_bool_condition(*this->children[1], t_ss);
+                this->children[2]->eval(t_ss)
+                ) {
+              try {
+                // Body of Loop
+                this->children[3]->eval(t_ss);
+              } catch (detail::Continue_Loop &) {
+                // we got a continue exception, which means all of the remaining 
+                // loop implementation is skipped and we just need to continue to
+                // the next iteration step
+              }
+            }
+          } catch (detail::Break_Loop &) {
+            // loop broken
+          }
+
+          return void_var();
+        }
+
+    };
+
+    template<typename T>
+    struct Switch_AST_Node final : AST_Node_Impl<T> {
+        Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          bool breaking = false;
+          size_t currentCase = 1;
+          bool hasMatched = false;
+
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+          Boxed_Value match_value(this->children[0]->eval(t_ss));
+
+          while (!breaking && (currentCase < this->children.size())) {
+            try {
+              if (this->children[currentCase]->identifier == AST_Node_Type::Case) {
+                //This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here.
+                try {
+                  if (hasMatched || boxed_cast<bool>(t_ss->call_function("==", m_loc, {match_value, this->children[currentCase]->children[0]->eval(t_ss)}, t_ss.conversions()))) {
+                    this->children[currentCase]->eval(t_ss);
+                    hasMatched = true;
+                  }
+                }
+                catch (const exception::bad_boxed_cast &) {
+                  throw exception::eval_error("Internal error: case guard evaluation not boolean");
+                }
+              }
+              else if (this->children[currentCase]->identifier == AST_Node_Type::Default) {
+                this->children[currentCase]->eval(t_ss);
+                hasMatched = true;
+              }
+            }
+            catch (detail::Break_Loop &) {
+              breaking = true;
+            }
+            ++currentCase;
+          }
+          return void_var();
+        }
+
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+    template<typename T>
+    struct Case_AST_Node final : AST_Node_Impl<T> {
+        Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) 
+        { assert(this->children.size() == 2); /* how many children does it have? */ }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+          this->children[1]->eval(t_ss);
+
+          return void_var();
+        }
+    };
+   
+    template<typename T>
+    struct Default_AST_Node final : AST_Node_Impl<T> {
+        Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children))
+        { assert(this->children.size() == 1); }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+          this->children[0]->eval(t_ss);
+
+          return void_var();
+        }
+    };
+
+
+    template<typename T>
+    struct Inline_Array_AST_Node final : AST_Node_Impl<T> {
+        Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          try {
+            std::vector<Boxed_Value> vec;
+            if (!this->children.empty()) {
+              vec.reserve(this->children[0]->children.size());
+              for (const auto &child : this->children[0]->children) {
+                auto obj = child->eval(t_ss);
+                if (!obj.is_return_value()) {
+                  vec.push_back(t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions()));
+                } else {
+                  vec.push_back(std::move(obj));
+                }
+              }
+            }
+            return const_var(std::move(vec));
+          }
+          catch (const exception::dispatch_error &) {
+            throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for vector elements");
+          }
+        }
+
+      private:
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+    template<typename T>
+    struct Inline_Map_AST_Node final : AST_Node_Impl<T> {
+        Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
+        {
+          try {
+            std::map<std::string, Boxed_Value> retval;
+
+            for (const auto &child : this->children[0]->children) {
+              auto obj = child->children[1]->eval(t_ss);
+              if (!obj.is_return_value()) {
+                obj = t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions());
+              }
+
+              retval[t_ss->boxed_cast<std::string>(child->children[0]->eval(t_ss))] = std::move(obj);
+            }
+
+            return const_var(std::move(retval));
+          }
+          catch (const exception::dispatch_error &e) {
+            throw exception::eval_error("Can not find appropriate copy constructor or 'clone' while inserting into Map.", e.parameters, e.functions, false, *t_ss);
+          }
+        }
+
+      private:
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+    template<typename T>
+    struct Return_AST_Node final : AST_Node_Impl<T> {
+        Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+          if (!this->children.empty()) {
+            throw detail::Return_Value(this->children[0]->eval(t_ss));
+          }
+          else {
+            throw detail::Return_Value(void_var());
+          }
+        }
+    };
+
+    template<typename T>
+    struct File_AST_Node final : AST_Node_Impl<T> {
+        File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          try {
+            const auto num_children = this->children.size();
+
+            if (num_children > 0) {
+              for (size_t i = 0; i < num_children-1; ++i) {
+                this->children[i]->eval(t_ss);
+              }
+              return this->children.back()->eval(t_ss);
+            } else {
+              return void_var();
+            }
+          } catch (const detail::Continue_Loop &) {
+            throw exception::eval_error("Unexpected `continue` statement outside of a loop");
+          } catch (const detail::Break_Loop &) {
+            throw exception::eval_error("Unexpected `break` statement outside of a loop");
+          }
+        }
+    };
+
+    template<typename T>
+    struct Reference_AST_Node final : AST_Node_Impl<T> {
+        Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children))
+        { assert(this->children.size() == 1); }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+          Boxed_Value bv;
+          t_ss.add_object(this->children[0]->text, bv);
+          return bv;
+        }
+    };
+
+    template<typename T>
+    struct Prefix_AST_Node final : AST_Node_Impl<T> {
+        Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children)),
+          m_oper(Operators::to_operator(this->text, true))
+        { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+          Boxed_Value bv(this->children[0]->eval(t_ss));
+
+          try {
+            // short circuit arithmetic operations
+            if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic())
+            {
+              return Boxed_Number::do_oper(m_oper, bv);
+            } else {
+              chaiscript::eval::detail::Function_Push_Pop fpp(t_ss);
+              fpp.save_params({bv});
+              return t_ss->call_function(this->text, m_loc, {std::move(bv)}, t_ss.conversions());
+            }
+          } catch (const exception::dispatch_error &e) {
+            throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss);
+          }
+        }
+
+      private:
+        Operators::Opers m_oper = Operators::Opers::invalid;
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+    template<typename T>
+    struct Break_AST_Node final : AST_Node_Impl<T> {
+        Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{
+          throw detail::Break_Loop();
+        }
+    };
+
+    template<typename T>
+    struct Continue_AST_Node final : AST_Node_Impl<T> {
+        Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{
+          throw detail::Continue_Loop();
+        }
+    };
+
+    template<typename T>
+    struct Noop_AST_Node final : AST_Node_Impl<T> {
+        Noop_AST_Node() :
+          AST_Node_Impl<T>("", AST_Node_Type::Noop, Parse_Location())
+        { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{
+          // It's a no-op, that evaluates to "void"
+          return val;
+        }
+
+        Boxed_Value val = void_var();
+    };
+
+    template<typename T>
+    struct Map_Pair_AST_Node final : AST_Node_Impl<T> {
+        Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) { }
+    };
+
+    template<typename T>
+    struct Value_Range_AST_Node final : AST_Node_Impl<T> {
+        Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) { }
+    };
+
+    template<typename T>
+    struct Inline_Range_AST_Node final : AST_Node_Impl<T> {
+        Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+          try {
+            auto oper1 = this->children[0]->children[0]->children[0]->eval(t_ss);
+            auto oper2 = this->children[0]->children[0]->children[1]->eval(t_ss);
+            return t_ss->call_function("generate_range", m_loc, {oper1, oper2}, t_ss.conversions());
+          }
+          catch (const exception::dispatch_error &e) {
+            throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss);
+          }
+        }
+
+      private:
+        mutable std::atomic_uint_fast32_t m_loc = {0};
+    };
+
+    template<typename T>
+    struct Try_AST_Node final : AST_Node_Impl<T> {
+        Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value handle_exception(const chaiscript::detail::Dispatch_State &t_ss, const Boxed_Value &t_except) const
+        {
+          Boxed_Value retval;
+
+          size_t end_point = this->children.size();
+          if (this->children.back()->identifier == AST_Node_Type::Finally) {
+            assert(end_point > 0);
+            end_point = this->children.size() - 1;
+          }
+          for (size_t i = 1; i < end_point; ++i) {
+            chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss);
+            auto &catch_block = *this->children[i];
+
+            if (catch_block.children.size() == 1) {
+              //No variable capture, no guards
+              retval = catch_block.children[0]->eval(t_ss);
+              break;
+            } else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) {
+              const auto name = Arg_List_AST_Node<T>::get_arg_name(*catch_block.children[0]);
+
+              if (dispatch::Param_Types(
+                    std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(*catch_block.children[0], t_ss)}
+                    ).match(std::vector<Boxed_Value>{t_except}, t_ss.conversions()).first)
+              {
+                t_ss.add_object(name, t_except);
+
+                if (catch_block.children.size() == 2) {
+                  //Variable capture, no guards
+                  retval = catch_block.children[1]->eval(t_ss);
+                  break;
+                }
+                else if (catch_block.children.size() == 3) {
+                  //Variable capture, guards
+
+                  bool guard = false;
+                  try {
+                    guard = boxed_cast<bool>(catch_block.children[1]->eval(t_ss));
+                  } catch (const exception::bad_boxed_cast &) {
+                    if (this->children.back()->identifier == AST_Node_Type::Finally) {
+                      this->children.back()->children[0]->eval(t_ss);
+                    }
+                    throw exception::eval_error("Guard condition not boolean");
+                  }
+                  if (guard) {
+                    retval = catch_block.children[2]->eval(t_ss);
+                    break;
+                  }
+                }
+              }
+            }
+            else {
+              if (this->children.back()->identifier == AST_Node_Type::Finally) {
+                this->children.back()->children[0]->eval(t_ss);
+              }
+              throw exception::eval_error("Internal error: catch block size unrecognized");
+            }
+          }
+
+          return retval;
+        }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override {
+          Boxed_Value retval;
+
+          chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+
+          try {
+            retval = this->children[0]->eval(t_ss);
+          }
+          catch (const exception::eval_error &e) {
+            retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
+          }
+          catch (const std::runtime_error &e) {
+            retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
+          }
+          catch (const std::out_of_range &e) {
+            retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
+          }
+          catch (const std::exception &e) {
+            retval = handle_exception(t_ss, Boxed_Value(std::ref(e)));
+          }
+          catch (Boxed_Value &e) {
+            retval = handle_exception(t_ss, e);
+          }
+          catch (...) {
+            if (this->children.back()->identifier == AST_Node_Type::Finally) {
+              this->children.back()->children[0]->eval(t_ss);
+            }
+            throw;
+          }
+
+
+          if (this->children.back()->identifier == AST_Node_Type::Finally) {
+            retval = this->children.back()->children[0]->eval(t_ss);
+          }
+
+          return retval;
+        }
+
+    };
+
+    template<typename T>
+    struct Catch_AST_Node final : AST_Node_Impl<T> {
+        Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) { }
+    };
+
+    template<typename T>
+    struct Finally_AST_Node final : AST_Node_Impl<T> {
+        Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) { }
+    };
+
+    template<typename T>
+    struct Method_AST_Node final : AST_Node_Impl<T> {
+        std::shared_ptr<AST_Node_Impl<T>> m_body_node;
+        std::shared_ptr<AST_Node_Impl<T>> m_guard_node;
+
+        Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc),
+              std::vector<AST_Node_Impl_Ptr<T>>(std::make_move_iterator(t_children.begin()), 
+                                                std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node<T>::has_guard(t_children, 1)?2:1)))
+              ),
+            m_body_node(Def_AST_Node<T>::get_body_node(std::move(t_children))),
+            m_guard_node(Def_AST_Node<T>::get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2))
+          {
+          }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
+
+          AST_Node_Impl_Ptr<T> guardnode;
+
+          const std::string & class_name = this->children[0]->text;
+
+          //The first param of a method is always the implied this ptr.
+          std::vector<std::string> t_param_names{"this"};
+          dispatch::Param_Types param_types;
+
+          if ((this->children.size() > 2) 
+               && (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
+            auto args = Arg_List_AST_Node<T>::get_arg_names(*this->children[2]);
+            t_param_names.insert(t_param_names.end(), args.begin(), args.end());
+            param_types = Arg_List_AST_Node<T>::get_arg_types(*this->children[2], t_ss);
+          }
+
+          const size_t numparams = t_param_names.size();
+
+          std::shared_ptr<dispatch::Proxy_Function_Base> guard;
+          std::reference_wrapper<chaiscript::detail::Dispatch_Engine> engine(*t_ss);
+          if (m_guard_node) {
+            guard = dispatch::make_dynamic_proxy_function(
+                [engine, t_param_names, guardnode = m_guard_node](const std::vector<Boxed_Value> &t_params) {
+                  return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params);
+                }, 
+                static_cast<int>(numparams), m_guard_node);
+          }
+
+          try {
+            const std::string & function_name = this->children[1]->text;
+
+            if (function_name == class_name) {
+              param_types.push_front(class_name, Type_Info());
+
+              t_ss->add(
+                  std::make_shared<dispatch::detail::Dynamic_Object_Constructor>(class_name,
+                    dispatch::make_dynamic_proxy_function(
+                        [engine, t_param_names, node = m_body_node](const std::vector<Boxed_Value> &t_params) {
+                          return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
+                        },
+                        static_cast<int>(numparams), m_body_node, param_types, guard
+                      )
+                    ),
+                  function_name);
+
+            } else {
+              // if the type is unknown, then this generates a function that looks up the type
+              // at runtime. Defining the type first before this is called is better
+              auto type = t_ss->get_type(class_name, false);
+              param_types.push_front(class_name, type);
+
+              t_ss->add(
+                  std::make_shared<dispatch::detail::Dynamic_Object_Function>(class_name,
+                    dispatch::make_dynamic_proxy_function(
+                      [engine, t_param_names, node = m_body_node](const std::vector<Boxed_Value> &t_params) {
+                        return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params);
+                      },
+                      static_cast<int>(numparams), m_body_node, param_types, guard), type), 
+                  function_name);
+            }
+          } catch (const exception::name_conflict_error &e) {
+            std::cout << "Method!!" << std::endl;
+            throw exception::eval_error("Method redefined '" + e.name() + "'");
+          }
+          return void_var();
+        }
+
+    };
+
+    template<typename T>
+    struct Attr_Decl_AST_Node final : AST_Node_Impl<T> {
+        Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) { }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override 
+        {
+          std::string class_name = this->children[0]->text;
+
+          try {
+            std::string attr_name = this->children[1]->text;
+
+            t_ss->add(
+                std::make_shared<dispatch::detail::Dynamic_Object_Function>(
+                     std::move(class_name),
+                     fun([attr_name](dispatch::Dynamic_Object &t_obj) {
+                           return t_obj.get_attr(attr_name);
+                         }),
+                     true
+
+                ), this->children[1]->text);
+          } catch (const exception::name_conflict_error &e) {
+            throw exception::eval_error("Attribute redefined '" + e.name() + "'");
+          }
+          return void_var();
+        }
+
+    };
+
+
+    template<typename T>
+    struct Logical_And_AST_Node final : AST_Node_Impl<T> {
+        Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) 
+        { assert(this->children.size() == 2); }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
+        {
+          return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)
+              && this->get_bool_condition(this->children[1]->eval(t_ss), t_ss));
+        }
+
+    };
+
+    template<typename T>
+    struct Logical_Or_AST_Node final : AST_Node_Impl<T> {
+        Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
+          AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) 
+        { assert(this->children.size() == 2); }
+
+        Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
+        {
+          return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)
+              || this->get_bool_condition(this->children[1]->eval(t_ss), t_ss));
+        }
+    };
+  }
+
+
+}
+#endif /* CHAISCRIPT_EVAL_HPP_ */
+

+ 449 - 0
chaiscript/language/chaiscript_optimizer.hpp

@@ -0,0 +1,449 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_OPTIMIZER_HPP_
+#define CHAISCRIPT_OPTIMIZER_HPP_
+
+#include "chaiscript_eval.hpp"
+
+
+namespace chaiscript {
+  namespace optimizer {
+
+    template<typename ... T>
+      struct Optimizer : T...
+    {
+      Optimizer() = default;
+      explicit Optimizer(T ... t)
+        : T(std::move(t))...
+      {
+      }
+
+      template<typename Tracer>
+      auto optimize(eval::AST_Node_Impl_Ptr<Tracer> p) {
+        (void)std::initializer_list<int>{ (p = static_cast<T&>(*this).optimize(std::move(p)), 0)... };
+        return p;
+      }
+    };
+
+    template<typename T>
+      eval::AST_Node_Impl<T> &child_at(eval::AST_Node_Impl<T> &node, const size_t offset) {
+        if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
+          return *(dynamic_cast<eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
+        } else {
+          return *node.children[offset];
+        }
+      }
+
+    template<typename T>
+      const eval::AST_Node_Impl<T> &child_at(const eval::AST_Node_Impl<T> &node, const size_t offset) {
+        if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
+          return *(dynamic_cast<const eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
+        } else {
+          return *node.children[offset];
+        }
+
+
+        /*
+        if (node->identifier == AST_Node_Type::Compiled) {
+          return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node).m_original_node->children[offset];
+        } else {
+          return node->children[offset];
+        }
+        */
+      }
+
+    template<typename T>
+      auto child_count(const eval::AST_Node_Impl<T> &node) {
+        if (node.identifier == AST_Node_Type::Compiled) {
+          return dynamic_cast<const eval::Compiled_AST_Node<T>&>(node).m_original_node->children.size();
+        } else {
+          return node.children.size();
+        }
+      }
+
+    template<typename T, typename Callable>
+      auto make_compiled_node(eval::AST_Node_Impl_Ptr<T> original_node, std::vector<eval::AST_Node_Impl_Ptr<T>> children, Callable callable)
+      {
+        return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Compiled_AST_Node<T>>(std::move(original_node), std::move(children), std::move(callable));
+      }
+
+
+    struct Return {
+      template<typename T>
+      auto optimize(eval::AST_Node_Impl_Ptr<T> p)
+      {
+        if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda)
+            && !p->children.empty())
+        {
+          auto &last_child = p->children.back();
+          if (last_child->identifier == AST_Node_Type::Block) {
+            auto &block_last_child = last_child->children.back();
+            if (block_last_child->identifier == AST_Node_Type::Return) {
+              if (block_last_child->children.size() == 1) {
+                last_child->children.back() = std::move(block_last_child->children[0]);
+              }
+            }
+          }
+        }
+
+        return p;
+      }
+    };
+
+    template<typename T>
+    bool contains_var_decl_in_scope(const eval::AST_Node_Impl<T> &node)
+    {
+      if (node.identifier == AST_Node_Type::Var_Decl) {
+        return true;
+      }
+
+      const auto num = child_count(node);
+
+      for (size_t i = 0; i < num; ++i) {
+        const auto &child = child_at(node, i);
+        if (child.identifier != AST_Node_Type::Block
+            && child.identifier != AST_Node_Type::For
+            && contains_var_decl_in_scope(child)) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    struct Block {
+      template<typename T>
+      auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
+        if (node->identifier == AST_Node_Type::Block)
+        {
+          if (!contains_var_decl_in_scope(*node))
+          {
+            if (node->children.size() == 1) {
+              return std::move(node->children[0]);
+            } else {
+              return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text, node->location, 
+                  std::move(node->children));
+            }
+          }
+        }
+
+        return node;
+      }
+    };
+
+    struct Dead_Code {
+      template<typename T>
+        auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
+          if (node->identifier == AST_Node_Type::Block)
+          {
+            std::vector<size_t> keepers;
+            const auto num_children = node->children.size();
+            keepers.reserve(num_children);
+
+            for (size_t i = 0; i < num_children; ++i) {
+              const auto &child = *node->children[i];
+              if ( (child.identifier != AST_Node_Type::Id
+                    && child.identifier != AST_Node_Type::Constant
+                    && child.identifier != AST_Node_Type::Noop)
+                  || i == num_children - 1) {
+                keepers.push_back(i);
+              }
+            }
+
+            if (keepers.size() == num_children) {
+              return node;
+            } else {
+              const auto new_children = [&](){
+                std::vector<eval::AST_Node_Impl_Ptr<T>> retval;
+                for (const auto x : keepers)
+                {
+                  retval.push_back(std::move(node->children[x]));
+                }
+                return retval;
+              };
+
+              return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Block_AST_Node<T>>(node->text, node->location, new_children());
+            }
+          } else {
+            return node;
+          }
+        }
+    };
+
+    struct Unused_Return {
+      template<typename T>
+        auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
+          if ((node->identifier == AST_Node_Type::Block
+              || node->identifier == AST_Node_Type::Scopeless_Block)
+              && !node->children.empty())
+          {
+            for (size_t i = 0; i < node->children.size()-1; ++i) {
+              auto child = node->children[i].get();
+              if (child->identifier == AST_Node_Type::Fun_Call) {
+                node->children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(child->text, child->location, 
+                    std::move(child->children));
+              }
+            }
+          } else if ((node->identifier == AST_Node_Type::For
+                      || node->identifier == AST_Node_Type::While)
+                     && child_count(*node) > 0) {
+            auto &child = child_at(*node, child_count(*node) - 1);
+            if (child.identifier == AST_Node_Type::Block
+                || child.identifier == AST_Node_Type::Scopeless_Block)
+            {
+              auto num_sub_children = child_count(child);
+              for (size_t i = 0; i < num_sub_children; ++i) {
+                auto &sub_child = child_at(child, i);
+                if (sub_child.identifier == AST_Node_Type::Fun_Call) {
+                  child.children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(sub_child.text, sub_child.location, std::move(sub_child.children));
+                }
+              }
+            }
+          }
+          return node;
+        }
+    };
+
+    struct If {
+      template<typename T>
+      auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
+        if ((node->identifier == AST_Node_Type::If)
+             && node->children.size() >= 2
+             && node->children[0]->identifier == AST_Node_Type::Constant)
+        {
+          const auto condition = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
+          if (condition.get_type_info().bare_equal_type_info(typeid(bool))) {
+            if (boxed_cast<bool>(condition)) {
+              return std::move(node->children[1]);
+            } else if (node->children.size() == 3) {
+              return std::move(node->children[2]);
+            }
+          }
+        }
+
+        return node;
+      }
+    };
+
+    struct Partial_Fold {
+      template<typename T>
+      auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
+
+        // Fold right side
+        if (node->identifier == AST_Node_Type::Binary
+            && node->children.size() == 2
+            && node->children[0]->identifier != AST_Node_Type::Constant
+            && node->children[1]->identifier == AST_Node_Type::Constant)
+        {
+          try {
+            const auto &oper = node->text;
+            const auto parsed = Operators::to_operator(oper);
+            if (parsed != Operators::Opers::invalid) {
+              const auto rhs = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[1].get())->m_value;
+              if (rhs.get_type_info().is_arithmetic()) {
+                return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Fold_Right_Binary_Operator_AST_Node<T>>(node->text, node->location, 
+                    std::move(node->children), rhs);
+              }
+            }
+          } catch (const std::exception &) {
+            //failure to fold, that's OK
+          }
+        }
+
+        return node;
+      }
+    };
+
+    struct Constant_Fold {
+      template<typename T>
+      auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
+
+        if (node->identifier == AST_Node_Type::Prefix
+            && node->children.size() == 1
+            && node->children[0]->identifier == AST_Node_Type::Constant)
+        {
+          try {
+            const auto &oper = node->text;
+            const auto parsed = Operators::to_operator(oper, true);
+            const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
+            const auto match = oper + node->children[0]->text;
+
+            if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) {
+              const auto val  = Boxed_Number::do_oper(parsed, lhs);
+              return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
+            } else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") {
+              return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(!boxed_cast<bool>(lhs)));
+            }
+          } catch (const std::exception &) {
+            //failure to fold, that's OK
+          }
+        } else if ((node->identifier == AST_Node_Type::Logical_And || node->identifier == AST_Node_Type::Logical_Or)
+            && node->children.size() == 2
+            && node->children[0]->identifier == AST_Node_Type::Constant
+            && node->children[1]->identifier == AST_Node_Type::Constant)
+        {
+          try {
+            const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
+            const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
+            if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) {
+              const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text;
+              const auto val = [lhs_val = boxed_cast<bool>(lhs), rhs_val = boxed_cast<bool>(rhs), id = node->identifier] {
+                if (id == AST_Node_Type::Logical_And) { return Boxed_Value(lhs_val && rhs_val); }
+                else { return Boxed_Value(lhs_val || rhs_val); }
+              }();
+
+              return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
+            }
+          } catch (const std::exception &) {
+            //failure to fold, that's OK
+          }
+        } else if (node->identifier == AST_Node_Type::Binary
+            && node->children.size() == 2
+            && node->children[0]->identifier == AST_Node_Type::Constant
+            && node->children[1]->identifier == AST_Node_Type::Constant)
+        {
+          try {
+            const auto &oper = node->text;
+            const auto parsed = Operators::to_operator(oper);
+            if (parsed != Operators::Opers::invalid) {
+              const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
+              const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
+              if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) {
+                const auto val  = Boxed_Number::do_oper(parsed, lhs, rhs);
+                const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text;
+                return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
+              }
+            }
+          } catch (const std::exception &) {
+            //failure to fold, that's OK
+          }
+        } else if (node->identifier == AST_Node_Type::Fun_Call
+                   && node->children.size() == 2
+                   && node->children[0]->identifier == AST_Node_Type::Id
+                   && node->children[1]->identifier == AST_Node_Type::Arg_List
+                   && node->children[1]->children.size() == 1
+                   && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) {
+
+          const auto arg = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]->children[0]).m_value;
+          if (arg.get_type_info().is_arithmetic()) {
+            const auto &fun_name = node->children[0]->text;
+
+            const auto make_constant = [&node, &fun_name](auto val){
+              const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")";
+              return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(val));
+            };
+
+            if (fun_name == "double") {
+              return make_constant(Boxed_Number(arg).get_as<double>());
+            } else if (fun_name == "int") {
+              return make_constant(Boxed_Number(arg).get_as<int>());
+            } else if (fun_name == "float") {
+              return make_constant(Boxed_Number(arg).get_as<float>());
+            } else if (fun_name == "long") {
+              return make_constant(Boxed_Number(arg).get_as<long>());
+            } else if (fun_name == "size_t") {
+              return make_constant(Boxed_Number(arg).get_as<size_t>());
+            }
+
+
+          }
+
+        }
+
+        return node;
+      }
+    };
+
+    struct For_Loop {
+      template<typename T>
+      auto optimize(eval::AST_Node_Impl_Ptr<T> for_node) {
+
+        if (for_node->identifier != AST_Node_Type::For) {
+          return for_node;
+        }
+
+        const auto &eq_node = child_at(*for_node, 0);
+        const auto &binary_node = child_at(*for_node, 1);
+        const auto &prefix_node = child_at(*for_node, 2);
+
+        if (child_count(*for_node) == 4
+            && eq_node.identifier == AST_Node_Type::Equation
+            && child_count(eq_node) == 2
+            && child_at(eq_node, 0).identifier == AST_Node_Type::Var_Decl
+            && child_at(eq_node, 1).identifier == AST_Node_Type::Constant
+            && binary_node.identifier == AST_Node_Type::Binary
+            && binary_node.text == "<"
+            && child_count(binary_node) == 2
+            && child_at(binary_node, 0).identifier == AST_Node_Type::Id
+            && child_at(binary_node, 0).text == child_at(child_at(eq_node,0), 0).text
+            && child_at(binary_node, 1).identifier == AST_Node_Type::Constant
+            && prefix_node.identifier == AST_Node_Type::Prefix
+            && prefix_node.text == "++"
+            && child_count(prefix_node) == 1
+            && child_at(prefix_node, 0).identifier == AST_Node_Type::Id
+            && child_at(prefix_node, 0).text == child_at(child_at(eq_node,0), 0).text)
+        {
+          const Boxed_Value &begin = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(eq_node, 1)).m_value;
+          const Boxed_Value &end = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(binary_node, 1)).m_value;
+          const std::string &id = child_at(prefix_node, 0).text;
+
+          if (begin.get_type_info().bare_equal(user_type<int>()) 
+              && end.get_type_info().bare_equal(user_type<int>())) {
+
+            const auto start_int = boxed_cast<int>(begin);
+            const auto end_int = boxed_cast<int>(end);
+
+            // note that we are moving the last element out, then popping the empty shared_ptr 
+            // from the vector
+            std::vector<eval::AST_Node_Impl_Ptr<T>> body_vector;
+            auto body_child = std::move(for_node->children[3]);
+            for_node->children.pop_back();
+            body_vector.emplace_back(std::move(body_child));
+            
+            return make_compiled_node(std::move(for_node), std::move(body_vector), 
+                [id, start_int, end_int](const std::vector<eval::AST_Node_Impl_Ptr<T>> &children, const chaiscript::detail::Dispatch_State &t_ss) {
+                  assert(children.size() == 1);
+                  chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
+
+                  int i = start_int;
+                  t_ss.add_object(id, var(&i));
+
+                  try {
+                    for (; i < end_int; ++i) {
+                      try {
+                        // Body of Loop
+                        children[0]->eval(t_ss);
+                      } catch (eval::detail::Continue_Loop &) {
+                        // we got a continue exception, which means all of the remaining 
+                        // loop implementation is skipped and we just need to continue to
+                        // the next iteration step
+                      }
+                    }
+                  } catch (eval::detail::Break_Loop &) {
+                    // loop broken
+                  }
+
+                  return void_var();
+                }
+            );
+          } else {
+            return for_node;
+          }
+        } else {
+          return for_node;
+        }
+      }
+    };
+
+    typedef Optimizer<optimizer::Partial_Fold, optimizer::Unused_Return, optimizer::Constant_Fold, 
+      optimizer::If, optimizer::Return, optimizer::Dead_Code, optimizer::Block, optimizer::For_Loop> Optimizer_Default; 
+
+  }
+}
+
+
+#endif

+ 2596 - 0
chaiscript/language/chaiscript_parser.hpp

@@ -0,0 +1,2596 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_PARSER_HPP_
+#define CHAISCRIPT_PARSER_HPP_
+
+#include <exception>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <cctype>
+#include <cstring>
+
+
+
+
+#include "../dispatchkit/boxed_value.hpp"
+#include "chaiscript_common.hpp"
+#include "chaiscript_optimizer.hpp"
+#include "chaiscript_tracer.hpp"
+#include "../utility/fnv1a.hpp"
+#include "../utility/static_string.hpp"
+
+#if defined(CHAISCRIPT_UTF16_UTF32)
+#include <locale>
+#include <codecvt>
+#endif
+
+#if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min)
+#define CHAISCRIPT_PUSHED_MIN_MAX
+#pragma push_macro("max") // Why Microsoft? why? This is worse than bad
+#undef max
+#pragma push_macro("min")
+#undef min
+#endif
+
+
+namespace chaiscript
+{
+  /// \brief Classes and functions used during the parsing process.
+  namespace parser
+  {
+    /// \brief Classes and functions internal to the parsing process. Not supported for the end user.
+    namespace detail 
+    {
+      enum Alphabet
+      {   symbol_alphabet = 0
+        ,   keyword_alphabet
+          ,   int_alphabet
+          ,   float_alphabet
+          ,   x_alphabet
+          ,   hex_alphabet
+          ,   b_alphabet
+          ,   bin_alphabet
+          ,   id_alphabet
+          ,   white_alphabet
+          ,   int_suffix_alphabet
+          ,   float_suffix_alphabet
+          ,   max_alphabet
+          ,   lengthof_alphabet = 256
+      };
+
+      // Generic for u16, u32 and wchar
+      template<typename string_type>
+      struct Char_Parser_Helper
+      {
+        // common for all implementations
+        static std::string u8str_from_ll(long long val)
+        {
+          typedef std::string::value_type char_type;
+
+          char_type c[2];
+          c[1] = char_type(val);
+          c[0] = char_type(val >> 8);
+
+          if (c[0] == 0)
+          {
+            return std::string(1, c[1]); // size, character
+          }
+
+          return std::string(c, 2); // char buffer, size
+        }
+
+        static string_type str_from_ll(long long val)
+        {
+          typedef typename string_type::value_type target_char_type;
+#if defined (CHAISCRIPT_UTF16_UTF32)
+          // prepare converter
+          std::wstring_convert<std::codecvt_utf8<target_char_type>, target_char_type> converter;
+          // convert
+          return converter.from_bytes(u8str_from_ll(val));
+#else
+          // no conversion available, just put value as character
+          return string_type(1, target_char_type(val)); // size, character
+#endif
+        }
+      };
+
+      // Specialization for char AKA UTF-8
+      template<>
+      struct Char_Parser_Helper<std::string>
+      {
+        static std::string str_from_ll(long long val)
+        {
+          // little SFINAE trick to avoid base class
+          return Char_Parser_Helper<std::true_type>::u8str_from_ll(val);
+        }
+      };
+    }
+
+
+    template<typename Tracer, typename Optimizer>
+    class ChaiScript_Parser final : public ChaiScript_Parser_Base {
+      void *get_tracer_ptr() override {
+        return &m_tracer;
+      }
+
+      static std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> build_alphabet()
+      {
+        std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> alphabet;
+
+        for (auto &alpha : alphabet) {
+          alpha.fill(false);
+        }
+
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('?')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('+')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('-')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('*')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('/')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('|')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('&')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('^')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('=')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('.')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('<')]=true;
+        alphabet[detail::symbol_alphabet][static_cast<size_t>('>')]=true;
+
+        for ( size_t c = 'a' ; c <= 'z' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; }
+        for ( size_t c = 'A' ; c <= 'Z' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; }
+        for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; }
+        alphabet[detail::keyword_alphabet][static_cast<size_t>('_')]=true;
+
+        for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::int_alphabet][c]=true; }
+        for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::float_alphabet][c]=true; }
+        alphabet[detail::float_alphabet][static_cast<size_t>('.')]=true;
+
+        for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; }
+        for ( size_t c = 'a' ; c <= 'f' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; }
+        for ( size_t c = 'A' ; c <= 'F' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; }
+
+        alphabet[detail::x_alphabet][static_cast<size_t>('x')]=true;
+        alphabet[detail::x_alphabet][static_cast<size_t>('X')]=true;
+
+        for ( size_t c = '0' ; c <= '1' ; ++c ) { alphabet[detail::bin_alphabet][c]=true; }
+        alphabet[detail::b_alphabet][static_cast<size_t>('b')]=true;
+        alphabet[detail::b_alphabet][static_cast<size_t>('B')]=true;
+
+        for ( size_t c = 'a' ; c <= 'z' ; ++c ) { alphabet[detail::id_alphabet][c]=true; }
+        for ( size_t c = 'A' ; c <= 'Z' ; ++c ) { alphabet[detail::id_alphabet][c]=true; }
+        alphabet[detail::id_alphabet][static_cast<size_t>('_')] = true;
+
+        alphabet[detail::white_alphabet][static_cast<size_t>(' ')]=true;
+        alphabet[detail::white_alphabet][static_cast<size_t>('\t')]=true;
+
+        alphabet[detail::int_suffix_alphabet][static_cast<size_t>('l')] = true;
+        alphabet[detail::int_suffix_alphabet][static_cast<size_t>('L')] = true;
+        alphabet[detail::int_suffix_alphabet][static_cast<size_t>('u')] = true;
+        alphabet[detail::int_suffix_alphabet][static_cast<size_t>('U')] = true;
+
+        alphabet[detail::float_suffix_alphabet][static_cast<size_t>('l')] = true;
+        alphabet[detail::float_suffix_alphabet][static_cast<size_t>('L')] = true;
+        alphabet[detail::float_suffix_alphabet][static_cast<size_t>('f')] = true;
+        alphabet[detail::float_suffix_alphabet][static_cast<size_t>('F')] = true;
+
+        return alphabet;
+      }
+
+      static const std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> &create_alphabet()
+      {
+        static const auto alpha = build_alphabet();
+        return alpha;
+      }
+
+
+      static const std::vector<std::vector<utility::Static_String>> &create_operator_matches() {
+        static const std::vector<std::vector<utility::Static_String>> operator_matches {
+          {"?"},
+          {"||"},
+          {"&&"},
+          {"|"},
+          {"^"},
+          {"&"},
+          {"==", "!="},
+          {"<", "<=", ">", ">="},
+          {"<<", ">>"},
+          //We share precedence here but then separate them later
+          {"+", "-"},
+          {"*", "/", "%"},
+          {"++", "--", "-", "+", "!", "~"}
+        };
+
+        return operator_matches;
+      }
+
+
+      static const std::array<Operator_Precidence, 12> &create_operators() {
+        static const std::array<Operator_Precidence, 12> operators = { {
+          Operator_Precidence::Ternary_Cond,
+          Operator_Precidence::Logical_Or,
+          Operator_Precidence::Logical_And,
+          Operator_Precidence::Bitwise_Or,
+          Operator_Precidence::Bitwise_Xor,
+          Operator_Precidence::Bitwise_And,
+          Operator_Precidence::Equality,
+          Operator_Precidence::Comparison,
+          Operator_Precidence::Shift,
+          Operator_Precidence::Addition,
+          Operator_Precidence::Multiplication,
+          Operator_Precidence::Prefix
+        } };
+        return operators;
+      }
+
+      static const utility::Static_String &multiline_comment_end()
+      {
+        static const utility::Static_String s("*/");
+        return s;
+      }
+
+      static const utility::Static_String &multiline_comment_begin()
+      {
+        static const utility::Static_String s("/*");
+        return s;
+      }
+
+      static const utility::Static_String &singleline_comment()
+      {
+        static const utility::Static_String s("//");
+        return s;
+      }
+
+      static const utility::Static_String &annotation()
+      {
+        static const utility::Static_String s("#");
+        return s;
+      }
+
+      static const utility::Static_String &cr_lf()
+      {
+        static const utility::Static_String s("\r\n");
+        return s;
+      }
+
+      const std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> &m_alphabet = create_alphabet();
+      const std::vector<std::vector<utility::Static_String>> &m_operator_matches = create_operator_matches();
+      const std::array<Operator_Precidence, 12> &m_operators = create_operators();
+
+      std::shared_ptr<std::string> m_filename;
+      std::vector<eval::AST_Node_Impl_Ptr<Tracer>> m_match_stack;
+
+
+      struct Position
+      {
+        Position() = default;
+
+        Position(std::string::const_iterator t_pos, std::string::const_iterator t_end)
+          : line(1), col(1), m_pos(t_pos), m_end(t_end), m_last_col(1)
+        {
+        }
+
+        static std::string str(const Position &t_begin, const Position &t_end) {
+          return std::string(t_begin.m_pos, t_end.m_pos);
+        }
+
+        Position &operator++() {
+          if (m_pos != m_end) {
+            if (*m_pos == '\n') {
+              ++line;
+              m_last_col = std::exchange(col, 1);
+            } else {
+              ++col;
+            }
+
+            ++m_pos;
+          }
+          return *this;
+        }
+
+        Position &operator--() {
+          --m_pos;
+          if (*m_pos == '\n') {
+            --line;
+            col = m_last_col;
+          } else {
+            --col;
+          }
+          return *this;
+        }
+
+        Position &operator+=(size_t t_distance) {
+          *this = (*this) + t_distance;
+          return *this;
+        }
+
+        Position operator+(size_t t_distance) const {
+          Position ret(*this);
+          for (size_t i = 0; i < t_distance; ++i) {
+            ++ret;
+          }
+          return ret;
+        }
+
+        Position &operator-=(size_t t_distance) {
+          *this = (*this) - t_distance;
+          return *this;
+        }
+
+        Position operator-(size_t t_distance) const {
+          Position ret(*this);
+          for (size_t i = 0; i < t_distance; ++i) {
+            --ret;
+          }
+          return ret;
+        }
+
+        bool operator==(const Position &t_rhs) const {
+          return m_pos == t_rhs.m_pos;
+        }
+
+        bool operator!=(const Position &t_rhs) const {
+          return m_pos != t_rhs.m_pos;
+        }
+
+        bool has_more() const {
+          return m_pos != m_end;
+        }
+
+        size_t remaining() const {
+          return static_cast<size_t>(std::distance(m_pos, m_end));
+        }
+
+        const char& operator*() const {
+          if (m_pos == m_end) {
+            static const char ktmp ='\0';
+            return ktmp;
+          } else {
+            return *m_pos;
+          }
+        }
+
+        int line = -1;
+        int col = -1;
+
+        private:
+          std::string::const_iterator m_pos;
+          std::string::const_iterator m_end;
+          int m_last_col = -1;
+      };
+
+      Position m_position;
+
+      Tracer m_tracer;
+      Optimizer m_optimizer;
+
+      void validate_object_name(const std::string &name) const
+      {
+        if (!Name_Validator::valid_object_name(name)) {
+          throw exception::eval_error("Invalid Object Name: " + name, File_Position(m_position.line, m_position.col), *m_filename);
+        }
+      }
+
+      public:
+      explicit ChaiScript_Parser(Tracer tracer = Tracer(), Optimizer optimizer=Optimizer())
+        : m_tracer(std::move(tracer)),
+          m_optimizer(std::move(optimizer))
+      {
+        m_match_stack.reserve(2);
+      }
+
+      Tracer &get_tracer() 
+      {
+        return m_tracer;
+      }
+
+      Optimizer &get_optimizer()
+      {
+        return m_optimizer;
+      }
+
+      ChaiScript_Parser(const ChaiScript_Parser &) = delete;
+      ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete;
+      ChaiScript_Parser(ChaiScript_Parser &&) = default;
+      ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete;
+
+      /// test a char in an m_alphabet
+      bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast<uint8_t>(c)]; }
+
+      /// Prints the parsed ast_nodes as a tree
+      void debug_print(const AST_Node &t, std::string prepend = "") const override {
+        std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) << ") " << t.text << " : " << t.start().line << ", " << t.start().column << '\n';
+        for (const auto &node : t.get_children()) {
+          debug_print(node.get(), prepend + "  ");
+        }
+      }
+
+
+      /// Helper function that collects ast_nodes from a starting position to the top of the stack into a new AST node
+      template<typename NodeType>
+      void build_match(size_t t_match_start, std::string t_text = "") {
+        bool is_deep = false;
+
+        Parse_Location filepos = [&]()->Parse_Location{ 
+          //so we want to take everything to the right of this and make them children
+          if (t_match_start != m_match_stack.size()) {
+            is_deep = true;
+            return Parse_Location(
+                m_filename,
+                m_match_stack[t_match_start]->location.start.line,
+                m_match_stack[t_match_start]->location.start.column,
+                m_position.line,
+                m_position.col 
+              );
+          } else {
+            return Parse_Location(
+                m_filename,
+                m_position.line,
+                m_position.col,
+                m_position.line,
+                m_position.col
+              );
+          }
+        }();
+
+        std::vector<eval::AST_Node_Impl_Ptr<Tracer>> new_children;
+
+        if (is_deep) {
+          new_children.assign(std::make_move_iterator(m_match_stack.begin() + static_cast<int>(t_match_start)), 
+                              std::make_move_iterator(m_match_stack.end()));
+          m_match_stack.erase(m_match_stack.begin() + static_cast<int>(t_match_start), m_match_stack.end());
+        }
+
+        /// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position
+        m_match_stack.push_back(
+            m_optimizer.optimize(
+              chaiscript::make_unique<chaiscript::eval::AST_Node_Impl<Tracer>, NodeType>(
+                std::move(t_text),
+                std::move(filepos),
+                std::move(new_children)))
+            );
+      }
+
+
+      /// Reads a symbol group from input if it matches the parameter, without skipping initial whitespace
+      inline auto Symbol_(const utility::Static_String &sym)
+      {
+        const auto len = sym.size();
+        if (m_position.remaining() >= len) {
+          const char *file_pos = &(*m_position);
+          for (size_t pos = 0; pos < len; ++pos)
+          {
+            if (sym.c_str()[pos] != file_pos[pos]) { return false; }
+          }
+          m_position += len;
+          return true;
+        }
+        return false;
+      }
+
+      /// Skips any multi-line or single-line comment
+      bool SkipComment() {
+        if (Symbol_(multiline_comment_begin())) {
+          while (m_position.has_more()) {
+            if (Symbol_(multiline_comment_end())) {
+              break;
+            } else if (!Eol_()) {
+              ++m_position;
+            }
+          }
+          return true;
+        } else if (Symbol_(singleline_comment())) {
+          while (m_position.has_more()) {
+            if (Symbol_(cr_lf())) {
+              m_position -= 2;
+              break;
+            } else if (Char_('\n')) {
+              --m_position;
+              break;
+            } else {
+              ++m_position;
+            }
+          }
+          return true;
+        } else if (Symbol_(annotation())) {
+          while (m_position.has_more()) {
+            if (Symbol_(cr_lf())) {
+              m_position -= 2;
+              break;
+            } else if (Char_('\n')) {
+              --m_position;
+              break;
+            } else {
+              ++m_position;
+            }
+          }
+          return true;
+        }
+        return false;
+      }
+
+
+      /// Skips ChaiScript whitespace, which means space and tab, but not cr/lf
+      /// jespada: Modified SkipWS to skip optionally CR ('\n') and/or LF+CR ("\r\n")
+      bool SkipWS(bool skip_cr=false) {
+        bool retval = false;
+
+        while (m_position.has_more()) {
+          auto end_line = (*m_position != 0) && ((*m_position == '\n') || (*m_position == '\r' && *(m_position+1) == '\n'));
+
+          if ( char_in_alphabet(*m_position,detail::white_alphabet) || (skip_cr && end_line)) {
+
+            if(end_line) {
+              if(*m_position == '\r') {
+                // discards lf
+                ++m_position;
+              }
+            }
+
+            ++m_position;
+
+            retval = true;
+          }
+          else if (SkipComment()) {
+            retval = true;
+          } else {
+            break;
+          }
+        }
+        return retval;
+      }
+
+      /// Reads the optional exponent (scientific notation) and suffix for a Float
+      bool read_exponent_and_suffix() {
+        // Support a form of scientific notation: 1e-5, 35.5E+8, 0.01e19
+        if (m_position.has_more() && (std::tolower(*m_position) == 'e')) {
+          ++m_position;
+          if (m_position.has_more() && ((*m_position == '-') || (*m_position == '+'))) {
+            ++m_position;
+          }
+          auto exponent_pos = m_position;
+          while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) {
+            ++m_position;
+          }
+          if (m_position == exponent_pos) {
+            // Require at least one digit after the exponent
+            return false;
+          }
+        }
+
+        // Parse optional float suffix
+        while (m_position.has_more() && char_in_alphabet(*m_position, detail::float_suffix_alphabet))
+        {
+          ++m_position;
+        }
+
+        return true;
+      }
+
+
+      /// Reads a floating point value from input, without skipping initial whitespace
+      bool Float_() {
+        if (m_position.has_more() && char_in_alphabet(*m_position,detail::float_alphabet) ) {
+          while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) {
+            ++m_position;
+          }
+
+          if (m_position.has_more() && (std::tolower(*m_position) == 'e')) {
+            // The exponent is valid even without any decimal in the Float (1e8, 3e-15)
+            return read_exponent_and_suffix();
+          }
+          else if (m_position.has_more() && (*m_position == '.')) {
+            ++m_position;
+            if (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet)) {
+              while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) {
+                ++m_position;
+              }
+
+              // After any decimal digits, support an optional exponent (3.7e3)
+              return read_exponent_and_suffix();
+            } else {
+              --m_position;
+            }
+          }
+        }
+        return false;
+      }
+
+      /// Reads a hex value from input, without skipping initial whitespace
+      bool Hex_() {
+        if (m_position.has_more() && (*m_position == '0')) {
+          ++m_position;
+
+          if (m_position.has_more() && char_in_alphabet(*m_position, detail::x_alphabet) ) {
+            ++m_position;
+            if (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet)) {
+              while (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet) ) {
+                ++m_position;
+              }
+              while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet))
+              {
+                ++m_position;
+              }
+
+              return true;
+            }
+            else {
+              --m_position;
+            }
+          }
+          else {
+            --m_position;
+          }
+        }
+
+        return false;
+      }
+
+      /// Reads an integer suffix
+      void IntSuffix_() {
+        while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet))
+        {
+          ++m_position;
+        }
+      }
+
+      /// Reads a binary value from input, without skipping initial whitespace
+      bool Binary_() {
+        if (m_position.has_more() && (*m_position == '0')) {
+          ++m_position;
+
+          if (m_position.has_more() && char_in_alphabet(*m_position, detail::b_alphabet) ) {
+            ++m_position;
+            if (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) {
+              while (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) {
+                ++m_position;
+              }
+              return true;
+            } else {
+              --m_position;
+            }
+          } else {
+            --m_position;
+          }
+        }
+
+        return false;
+      }
+
+      /// Parses a floating point value and returns a Boxed_Value representation of it
+      static Boxed_Value buildFloat(const std::string &t_val)
+      {
+        bool float_ = false;
+        bool long_ = false;
+
+        auto i = t_val.size();
+
+        for (; i > 0; --i)
+        {
+          char val = t_val[i-1];
+
+          if (val == 'f' || val == 'F')
+          {
+            float_ = true;
+          } else if (val == 'l' || val == 'L') {
+            long_ = true;
+          } else {
+            break;
+          }
+        }
+
+        if (float_)
+        {
+          return const_var(parse_num<float>(t_val.substr(0,i)));
+        } else if (long_) {
+          return const_var(parse_num<long double>(t_val.substr(0,i)));
+        } else {
+          return const_var(parse_num<double>(t_val.substr(0,i)));
+        }
+      }
+
+
+
+      static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed)
+      {
+        bool unsigned_ = false;
+        bool long_ = false;
+        bool longlong_ = false;
+
+        auto i = t_val.size();
+
+        for (; i > 0; --i)
+        {
+          const char val = t_val[i-1];
+
+          if (val == 'u' || val == 'U')
+          {
+            unsigned_ = true;
+          } else if (val == 'l' || val == 'L') {
+            if (long_)
+            {
+              longlong_ = true;
+            }
+
+            long_ = true;
+          } else {
+            break;
+          }
+        }
+
+        const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val;
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-compare"
+
+#ifdef CHAISCRIPT_CLANG
+#pragma GCC diagnostic ignored "-Wtautological-compare"
+#endif
+
+#endif
+
+        try {
+          auto u = std::stoll(val,nullptr,base);
+
+
+          if (!unsigned_ && !long_ && u >= std::numeric_limits<int>::min() && u <= std::numeric_limits<int>::max()) {
+            return const_var(static_cast<int>(u));
+          } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits<unsigned int>::min() && u <= std::numeric_limits<unsigned int>::max()) {
+            return const_var(static_cast<unsigned int>(u));
+          } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits<long>::min() && u <= std::numeric_limits<long>::max()) {
+            return const_var(static_cast<long>(u));
+          } else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits<unsigned long>::min() && u <= std::numeric_limits<unsigned long>::max()) {
+            return const_var(static_cast<unsigned long>(u));
+          } else if (!unsigned_ && u >= std::numeric_limits<long long>::min() && u <= std::numeric_limits<long long>::max()) {
+            return const_var(static_cast<long long>(u));
+          } else {
+            return const_var(static_cast<unsigned long long>(u));
+          }
+
+        } catch (const std::out_of_range &) {
+          // too big to be signed
+          try {
+            auto u = std::stoull(val,nullptr,base);
+
+            if (!longlong_ && u >= std::numeric_limits<unsigned long>::min() && u <= std::numeric_limits<unsigned long>::max()) {
+              return const_var(static_cast<unsigned long>(u));
+            } else {
+              return const_var(static_cast<unsigned long long>(u));
+            }
+          } catch (const std::out_of_range &) {
+            // it's just simply too big
+            return const_var(std::numeric_limits<long long>::max());
+          }
+        }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+      }
+
+      template<typename T, typename ... Param>
+      std::unique_ptr<eval::AST_Node_Impl<Tracer>> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param)
+      {
+        return chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward<Param>(param)...);
+      }
+
+      /// Reads a number from the input, detecting if it's an integer or floating point
+      bool Num() {
+        SkipWS();
+
+        const auto start = m_position;
+        if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) {
+          try {
+            if (Hex_()) {
+              auto match = Position::str(start, m_position);
+              auto bv = buildInt(16, match, true);
+              m_match_stack.emplace_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
+              return true;
+            }
+
+            if (Binary_()) {
+              auto match = Position::str(start, m_position);
+              auto bv = buildInt(2, match, true);
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
+              return true;
+            }
+            if (Float_()) {
+              auto match = Position::str(start, m_position);
+              auto bv = buildFloat(match);
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
+              return true;
+            }
+            else {
+              IntSuffix_();
+              auto match = Position::str(start, m_position);
+              if (!match.empty() && (match[0] == '0')) {
+                auto bv = buildInt(8, match, false);
+                m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
+              }
+              else if (!match.empty()) {
+                auto bv = buildInt(10, match, false);
+                m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
+              } else {
+                return false;
+              }
+              return true;
+            }
+          } catch (const std::invalid_argument &) {
+            // error parsing number passed in to buildFloat/buildInt
+            return false;
+          }
+        }
+        else {
+          return false;
+        }
+      }
+
+      /// Reads an identifier from input which conforms to C's identifier naming conventions, without skipping initial whitespace
+      bool Id_() {
+        if (m_position.has_more() && char_in_alphabet(*m_position, detail::id_alphabet)) {
+          while (m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) {
+            ++m_position;
+          }
+
+          return true;
+        } else if (m_position.has_more() && (*m_position == '`')) {
+          ++m_position;
+          const auto start = m_position;
+
+          while (m_position.has_more() && (*m_position != '`')) {
+            if (Eol()) {
+              throw exception::eval_error("Carriage return in identifier literal", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+            else {
+              ++m_position;
+            }
+          }
+
+          if (start == m_position) {
+            throw exception::eval_error("Missing contents of identifier literal", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+          else if (!m_position.has_more()) {
+            throw exception::eval_error("Incomplete identifier literal", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          ++m_position;
+
+          return true;
+        }
+        return false;
+      }
+
+      /// Reads (and potentially captures) an identifier from input
+      bool Id(const bool validate) {
+        SkipWS();
+
+        const auto start = m_position;
+        if (Id_()) {
+
+          auto text = Position::str(start, m_position);
+          const auto text_hash = utility::fnv1a_32(text.c_str());
+
+          if (validate) {
+            validate_object_name(text);
+          }
+
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4307)
+#endif
+
+          switch (text_hash) {
+            case utility::fnv1a_32("true"): {
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col, const_var(true)));
+            } break;
+            case utility::fnv1a_32("false"): {
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col, const_var(false)));
+            } break;
+            case utility::fnv1a_32("Infinity"): {
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
+                const_var(std::numeric_limits<double>::infinity())));
+            } break;
+            case utility::fnv1a_32("NaN"): {
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
+                const_var(std::numeric_limits<double>::quiet_NaN())));
+            } break;
+            case utility::fnv1a_32("__LINE__"): {
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
+                const_var(start.line)));
+            } break;
+            case utility::fnv1a_32("__FILE__"): {
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
+                const_var(m_filename)));
+            } break;
+            case utility::fnv1a_32("__FUNC__"): {
+              std::string fun_name = "NOT_IN_FUNCTION";
+              for (size_t idx = m_match_stack.size() - 1; idx > 0; --idx)
+              {
+                if (m_match_stack[idx-1]->identifier == AST_Node_Type::Id
+                    && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) {
+                  fun_name = m_match_stack[idx-1]->text;
+                }
+              }
+
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
+                const_var(fun_name)));
+            } break;
+            case utility::fnv1a_32("__CLASS__"): {
+              std::string fun_name = "NOT_IN_CLASS";
+              for (size_t idx = m_match_stack.size() - 1; idx > 1; --idx)
+              {
+                if (m_match_stack[idx-2]->identifier == AST_Node_Type::Id
+                    && m_match_stack[idx-1]->identifier == AST_Node_Type::Id
+                    && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) {
+                  fun_name = m_match_stack[idx-2]->text;
+                }
+              }
+
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
+                const_var(fun_name)));
+            } break;
+            case utility::fnv1a_32("_"): {
+              m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
+                Boxed_Value(std::make_shared<dispatch::Placeholder_Object>())));
+            } break;
+            default: {
+                std::string val = std::move(text);
+              if (*start == '`') {
+                // 'escaped' literal, like an operator name
+                val = Position::str(start+1, m_position-1);
+              }
+              m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(val, start.line, start.col));
+            } break;
+          }
+
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+
+
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads an argument from input
+      bool Arg(const bool t_type_allowed = true) {
+        const auto prev_stack_top = m_match_stack.size();
+        SkipWS();
+
+        if (!Id(true)) {
+          return false;
+        }
+
+        SkipWS();
+
+        if (t_type_allowed) {
+          Id(true);
+        }
+
+        build_match<eval::Arg_AST_Node<Tracer>>(prev_stack_top);
+
+        return true;
+      }
+
+
+
+      /// Reads a quoted string from input, without skipping initial whitespace
+      bool Quoted_String_() {
+        if (m_position.has_more() && (*m_position == '\"')) {
+          char prev_char = *m_position;
+          ++m_position;
+
+          int in_interpolation = 0;
+          bool in_quote = false;
+
+          while (m_position.has_more() && ((*m_position != '\"') || ((*m_position == '\"') && (in_interpolation > 0)) ||  ((*m_position == '\"') && (prev_char == '\\')))) {
+
+            if (!Eol_()) {
+              if (prev_char == '$' && *m_position == '{') {
+                ++in_interpolation;
+              } else if (prev_char != '\\' && *m_position == '"') {
+                in_quote = !in_quote;
+              } else if (*m_position == '}' && !in_quote) {
+                --in_interpolation;
+              }
+
+              if (prev_char == '\\') {
+                prev_char = 0;
+              } else {
+                prev_char = *m_position;
+              }
+              ++m_position;
+            }
+          }
+
+          if (m_position.has_more()) {
+            ++m_position;
+          } else {
+            throw exception::eval_error("Unclosed quoted string", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          return true;
+        }
+        return false;
+      }
+
+      template<typename string_type>
+      struct Char_Parser
+      {
+        string_type &match;
+        typedef typename string_type::value_type char_type;
+        bool is_escaped = false;
+        bool is_interpolated = false;
+        bool saw_interpolation_marker = false;
+        bool is_octal = false;
+        bool is_hex = false;
+        bool is_unicode = false;
+        const bool interpolation_allowed;
+
+        string_type octal_matches;
+        string_type hex_matches;
+
+        Char_Parser(string_type &t_match, const bool t_interpolation_allowed)
+          : match(t_match),
+            interpolation_allowed(t_interpolation_allowed)
+        {
+        }
+
+        Char_Parser &operator=(const Char_Parser &) = delete;
+
+        ~Char_Parser(){
+          try {
+            if (is_octal) {
+              process_octal();
+            }
+
+            if (is_hex) {
+              process_hex();
+            }
+
+            if (is_unicode) {
+              process_unicode();
+            }
+          } catch (const std::invalid_argument &) {
+            // escape sequence was invalid somehow, we'll pick this
+            // up in the next part of parsing
+          }
+        }
+
+        void process_hex()
+        {
+          if (!hex_matches.empty()) {
+            auto val = stoll(hex_matches, nullptr, 16);
+            match.push_back(char_type(val));
+          }
+          hex_matches.clear();
+          is_escaped = false;
+          is_hex = false;
+        }
+
+
+        void process_octal()
+        {
+          if (!octal_matches.empty()) {
+            auto val = stoll(octal_matches, nullptr, 8);
+            match.push_back(char_type(val));
+          }
+          octal_matches.clear();
+          is_escaped = false;
+          is_octal = false;
+        }
+
+
+        void process_unicode()
+        {
+          if (!hex_matches.empty()) {
+            auto val = stoll(hex_matches, nullptr, 16);
+            hex_matches.clear();
+            match += detail::Char_Parser_Helper<string_type>::str_from_ll(val);
+          }
+          is_escaped = false;
+          is_unicode = false;
+        }
+
+        void parse(const char_type t_char, const int line, const int col, const std::string &filename) {
+          const bool is_octal_char = t_char >= '0' && t_char <= '7';
+
+          const bool is_hex_char  = (t_char >= '0' && t_char <= '9')
+                                 || (t_char >= 'a' && t_char <= 'f')
+                                 || (t_char >= 'A' && t_char <= 'F');
+
+          if (is_octal) {
+            if (is_octal_char) {
+              octal_matches.push_back(t_char);
+
+              if (octal_matches.size() == 3) {
+                process_octal();
+              }
+              return;
+            } else {
+              process_octal();
+            }
+          } else if (is_hex) {
+            if (is_hex_char) {
+              hex_matches.push_back(t_char);
+
+              if (hex_matches.size() == 2*sizeof(char_type)) {
+                // This rule differs from the C/C++ standard, but ChaiScript
+                // does not offer the same workaround options, and having
+                // hexadecimal sequences longer than can fit into the char
+                // type is undefined behavior anyway.
+                process_hex();
+              }
+              return;
+            } else {
+              process_hex();
+            }
+          } else if (is_unicode) {
+            if (is_hex_char) {
+              hex_matches.push_back(t_char);
+
+            if(hex_matches.size() == 4) {
+              // Format is specified to be 'slash'uABCD
+              // on collecting from A to D do parsing
+              process_unicode();
+            }
+            return;
+            } else {
+              // Not a unicode anymore, try parsing any way
+              // May be someone used 'slash'uAA only
+              process_unicode();
+            }
+          }
+
+          if (t_char == '\\') {
+            if (is_escaped) {
+              match.push_back('\\');
+              is_escaped = false;
+            } else {
+              is_escaped = true;
+            }
+          } else {
+            if (is_escaped) {
+              if (is_octal_char) {
+                is_octal = true;
+                octal_matches.push_back(t_char);
+              } else if (t_char == 'x') {
+                is_hex = true;
+              } else if (t_char == 'u') {
+                is_unicode = true;
+              } else {
+                switch (t_char) {
+                  case ('\'') : match.push_back('\''); break;
+                  case ('\"') : match.push_back('\"'); break;
+                  case ('?') : match.push_back('?'); break;
+                  case ('a') : match.push_back('\a'); break;
+                  case ('b') : match.push_back('\b'); break;
+                  case ('f') : match.push_back('\f'); break;
+                  case ('n') : match.push_back('\n'); break;
+                  case ('r') : match.push_back('\r'); break;
+                  case ('t') : match.push_back('\t'); break;
+                  case ('v') : match.push_back('\v'); break;
+                  case ('$') : match.push_back('$'); break;
+                  default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename);
+                }
+                is_escaped = false;
+              }
+            } else if (interpolation_allowed && t_char == '$') {
+              saw_interpolation_marker = true;
+            } else {
+              match.push_back(t_char);
+            }
+          }
+        }
+
+      };
+
+
+      /// Reads (and potentially captures) a quoted string from input.  Translates escaped sequences.
+      bool Quoted_String() {
+        SkipWS();
+
+        const auto start = m_position;
+
+        if (Quoted_String_()) {
+          std::string match;
+          const auto prev_stack_top = m_match_stack.size();
+
+          bool is_interpolated = [&]()->bool {
+            Char_Parser<std::string> cparser(match, true);
+
+
+            auto s = start + 1, end = m_position - 1;
+
+            while (s != end) {
+              if (cparser.saw_interpolation_marker) {
+                if (*s == '{') {
+                  //We've found an interpolation point
+
+                  m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(match)));
+
+                  if (cparser.is_interpolated) {
+                    //If we've seen previous interpolation, add on instead of making a new one
+                    build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, "+");
+                  }
+
+                  //We've finished with the part of the string up to this point, so clear it
+                  match.clear();
+
+                  std::string eval_match;
+
+                  ++s;
+                  while ((s != end) && (*s != '}')) {
+                    eval_match.push_back(*s);
+                    ++s;
+                  }
+
+                  if (*s == '}') {
+                    cparser.is_interpolated = true;
+                    ++s;
+
+                    const auto tostr_stack_top = m_match_stack.size();
+
+                    m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>("to_string", start.line, start.col));
+
+                    const auto ev_stack_top = m_match_stack.size();
+
+                    try {
+                      m_match_stack.push_back(parse_instr_eval(eval_match));
+                    } catch (const exception::eval_error &e) {
+                      throw exception::eval_error(e.what(), File_Position(start.line, start.col), *m_filename);
+                    }
+
+                    build_match<eval::Arg_List_AST_Node<Tracer>>(ev_stack_top);
+                    build_match<eval::Fun_Call_AST_Node<Tracer>>(tostr_stack_top);
+                    build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, "+");
+                  } else {
+                    throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename);
+                  }
+                } else {
+                  match.push_back('$');
+                }
+                cparser.saw_interpolation_marker = false;
+              } else {
+                cparser.parse(*s, start.line, start.col, *m_filename);
+
+                ++s;
+              }
+            }
+
+            return cparser.is_interpolated;
+          }();
+
+          m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(match)));
+
+          if (is_interpolated) {
+            build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, "+");
+          }
+
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads a character group from input, without skipping initial whitespace
+      bool Single_Quoted_String_() {
+        bool retval = false;
+        if (m_position.has_more() && (*m_position == '\'')) {
+          retval = true;
+          char prev_char = *m_position;
+          ++m_position;
+
+          while (m_position.has_more() && ((*m_position != '\'') || ((*m_position == '\'') && (prev_char == '\\')))) {
+            if (!Eol_()) {
+              if (prev_char == '\\') {
+                prev_char = 0;
+              } else {
+                prev_char = *m_position;
+              }
+              ++m_position;
+            }
+          }
+
+          if (m_position.has_more()) {
+            ++m_position;
+          } else {
+            throw exception::eval_error("Unclosed single-quoted string", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+        }
+        return retval;
+      }
+
+      /// Reads (and potentially captures) a char group from input.  Translates escaped sequences.
+      bool Single_Quoted_String() {
+        SkipWS();
+
+        const auto start = m_position;
+        if (Single_Quoted_String_()) {
+          std::string match;
+
+          {
+            // scope for cparser destructor
+            Char_Parser<std::string> cparser(match, false);
+
+            for (auto s = start + 1, end = m_position - 1; s != end; ++s) {
+              cparser.parse(*s, start.line, start.col, *m_filename);
+            }
+          }
+
+          if (match.size() != 1) {
+            throw exception::eval_error("Single-quoted strings must be 1 character long", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(char(match.at(0)))));
+          return true;
+        }
+        else {
+          return false;
+        }
+      }
+
+      /// Reads a char from input if it matches the parameter, without skipping initial whitespace
+      bool Char_(const char c) {
+        if (m_position.has_more() && (*m_position == c)) {
+          ++m_position;
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads (and potentially captures) a char from input if it matches the parameter
+      bool Char(const char t_c) {
+        SkipWS();
+        return Char_(t_c);
+      }
+
+      /// Reads a string from input if it matches the parameter, without skipping initial whitespace
+      bool Keyword_(const utility::Static_String &t_s) {
+        const auto len = t_s.size();
+        if (m_position.remaining() >= len) {
+          auto tmp = m_position;
+          for (size_t i = 0; tmp.has_more() && i < len; ++i) {
+            if (*tmp != t_s.c_str()[i]) {
+              return false;
+            }
+            ++tmp;
+          }
+          m_position = tmp;
+          return true;
+        }
+
+        return false;
+      }
+
+      /// Reads (and potentially captures) a string from input if it matches the parameter
+      bool Keyword(const utility::Static_String &t_s) {
+        SkipWS();
+        const auto start = m_position;
+        bool retval = Keyword_(t_s);
+        // ignore substring matches
+        if ( retval && m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) {
+          m_position = start;
+          retval = false;
+        }
+
+        return retval;
+      }
+
+      bool is_operator(const std::string &t_s) const {
+        return std::any_of(m_operator_matches.begin(), m_operator_matches.end(),
+            [t_s](const std::vector<utility::Static_String> &opers) {
+              return std::any_of(opers.begin(), opers.end(), 
+                [t_s](const utility::Static_String &s) {
+                  return t_s == s.c_str();
+                });
+            });
+      }
+
+      /// Reads (and potentially captures) a symbol group from input if it matches the parameter
+      bool Symbol(const utility::Static_String &t_s, const bool t_disallow_prevention=false) {
+        SkipWS();
+        const auto start = m_position;
+        bool retval = Symbol_(t_s);
+
+        // ignore substring matches
+        if (retval && m_position.has_more() && (t_disallow_prevention == false) && char_in_alphabet(*m_position,detail::symbol_alphabet)) {
+          if (*m_position != '=' && is_operator(Position::str(start, m_position)) && !is_operator(Position::str(start, m_position+1))) {
+            // don't throw this away, it's a good match and the next is not
+          } else {
+            m_position = start;
+            retval = false;
+          }
+        }
+
+        return retval;
+      }
+
+      /// Reads an end-of-line group from input, without skipping initial whitespace
+      bool Eol_(const bool t_eos = false) {
+        bool retval = false;
+
+        if (m_position.has_more() && (Symbol_(cr_lf()) || Char_('\n'))) {
+          retval = true;
+          //++m_position.line;
+          m_position.col = 1;
+        } else if (m_position.has_more() && !t_eos && Char_(';')) {
+          retval = true;
+        }
+
+        return retval;
+      }
+
+      /// Reads until the end of the current statement
+      bool Eos() {
+        SkipWS();
+
+        return Eol_(true);
+      }
+
+      /// Reads (and potentially captures) an end-of-line group from input
+      bool Eol() {
+        SkipWS();
+
+        return Eol_();
+      }
+
+      /// Reads a comma-separated list of values from input. Id's only, no types allowed
+      bool Id_Arg_List() {
+        SkipWS(true);
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Arg(false)) {
+          retval = true;
+          while (Eol()) {}
+
+          while (Char(',')) {
+            while (Eol()) {}
+            if (!Arg(false)) {
+              throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          } 
+        }
+        build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
+
+        SkipWS(true);
+
+        return retval;
+      }
+
+      /// Reads a comma-separated list of values from input, for function declarations
+      bool Decl_Arg_List() {
+        SkipWS(true);
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Arg()) {
+          retval = true;
+          while (Eol()) {}
+
+          while (Char(',')) {
+            while (Eol()) {}
+            if (!Arg()) {
+              throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+        }
+        build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
+
+        SkipWS(true);
+
+        return retval;
+      }
+
+
+      /// Reads a comma-separated list of values from input
+      bool Arg_List() {
+        SkipWS(true);
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Equation()) {
+          retval = true;
+          while (Eol()) {}
+          while (Char(',')) {
+            while (Eol()) {}
+            if (!Equation()) {
+              throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+        }
+
+        build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
+
+        SkipWS(true);
+
+        return retval;
+      }
+
+      /// Reads possible special container values, including ranges and map_pairs
+      bool Container_Arg_List() {
+        bool retval = false;
+        SkipWS(true);
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Value_Range()) {
+          retval = true;
+          build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
+        } else if (Map_Pair()) {
+          retval = true;
+          while (Eol()) {}
+          while (Char(',')) {
+            while (Eol()) {}
+            if (!Map_Pair()) {
+              throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+          build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
+        } else if (Operator()) {
+          retval = true;
+          while (Eol()) {}
+          while (Char(',')) {
+            while (Eol()) {}
+            if (!Operator()) {
+              throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+          build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        SkipWS(true);
+
+        return retval;
+      }
+
+      /// Reads a lambda (anonymous function) from input
+      bool Lambda() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("fun")) {
+          retval = true;
+
+          if (Char('[')) {
+            Id_Arg_List();
+            if (!Char(']')) {
+              throw exception::eval_error("Incomplete anonymous function bind", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          } else {
+            // make sure we always have the same number of nodes
+            build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
+          }
+
+          if (Char('(')) {
+            Decl_Arg_List();
+            if (!Char(')')) {
+              throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          } else {
+            throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+
+          while (Eol()) {}
+
+          if (!Block()) {
+            throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Lambda_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+      /// Reads a function definition from input
+      bool Def(const bool t_class_context = false, const std::string &t_class_name = "") {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("def")) {
+          retval = true;
+
+          if (t_class_context) {
+            m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(t_class_name, m_position.line, m_position.col));
+          }
+
+          if (!Id(true)) {
+            throw exception::eval_error("Missing function name in definition", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          bool is_method = false;
+
+          if (Symbol("::")) {
+            //We're now a method
+            is_method = true;
+
+            if (!Id(true)) {
+              throw exception::eval_error("Missing method name in definition", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+
+          if (Char('(')) {
+            Decl_Arg_List();
+            if (!Char(')')) {
+              throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+
+          while (Eos()) {}
+
+          if (Char(':')) {
+            if (!Operator()) {
+              throw exception::eval_error("Missing guard expression for function", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+
+          while (Eol()) {}
+          if (!Block()) {
+            throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          if (is_method || t_class_context) {
+            build_match<eval::Method_AST_Node<Tracer>>(prev_stack_top);
+          } else {
+            build_match<eval::Def_AST_Node<Tracer>>(prev_stack_top);
+          }
+
+        }
+
+        return retval;
+      }
+
+      /// Reads a function definition from input
+      bool Try() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("try")) {
+          retval = true;
+
+          while (Eol()) {}
+
+          if (!Block()) {
+            throw exception::eval_error("Incomplete 'try' block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          bool has_matches = true;
+          while (has_matches) {
+            while (Eol()) {}
+            has_matches = false;
+            if (Keyword("catch")) {
+              const auto catch_stack_top = m_match_stack.size();
+              if (Char('(')) {
+                if (!(Arg() && Char(')'))) {
+                  throw exception::eval_error("Incomplete 'catch' expression", File_Position(m_position.line, m_position.col), *m_filename);
+                }
+                if (Char(':')) {
+                  if (!Operator()) {
+                    throw exception::eval_error("Missing guard expression for catch", File_Position(m_position.line, m_position.col), *m_filename);
+                  }
+                }
+              }
+
+              while (Eol()) {}
+
+              if (!Block()) {
+                throw exception::eval_error("Incomplete 'catch' block", File_Position(m_position.line, m_position.col), *m_filename);
+              }
+              build_match<eval::Catch_AST_Node<Tracer>>(catch_stack_top);
+              has_matches = true;
+            }
+          }
+          while (Eol()) {}
+          if (Keyword("finally")) {
+            const auto finally_stack_top = m_match_stack.size();
+
+            while (Eol()) {}
+
+            if (!Block()) {
+              throw exception::eval_error("Incomplete 'finally' block", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+            build_match<eval::Finally_AST_Node<Tracer>>(finally_stack_top);
+          }
+
+          build_match<eval::Try_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+      /// Reads an if/else if/else block from input
+      bool If() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("if")) {
+          retval = true;
+
+          if (!Char('(')) {
+            throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          if (!Equation()) {
+            throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          const bool is_if_init = Eol() && Equation();
+
+          if (!Char(')')) {
+            throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          while (Eol()) {}
+
+          if (!Block()) {
+            throw exception::eval_error("Incomplete 'if' block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          bool has_matches = true;
+          while (has_matches) {
+            while (Eol()) {}
+            has_matches = false;
+            if (Keyword("else")) {
+              if (If()) {
+                has_matches = true;
+              } else {
+                while (Eol()) {}
+
+                if (!Block()) {
+                  throw exception::eval_error("Incomplete 'else' block", File_Position(m_position.line, m_position.col), *m_filename);
+                }
+                has_matches = true;
+              }
+            }
+          }
+
+          const auto num_children = m_match_stack.size() - prev_stack_top;
+
+          if ((is_if_init && num_children == 3)
+              || (!is_if_init && num_children == 2)) {
+            m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
+          }
+
+          if (!is_if_init) {
+            build_match<eval::If_AST_Node<Tracer>>(prev_stack_top);
+          } else {
+            build_match<eval::If_AST_Node<Tracer>>(prev_stack_top+1);
+            build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
+          }
+        }
+
+        return retval;
+      }
+
+      /// Reads a class block from input
+      bool Class(const bool t_class_allowed) {
+        bool retval = false;
+
+        size_t prev_stack_top = m_match_stack.size();
+
+        if (Keyword("class")) {
+          if (!t_class_allowed) {
+            throw exception::eval_error("Class definitions only allowed at top scope", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          retval = true;
+
+          if (!Id(true)) {
+            throw exception::eval_error("Missing class name in definition", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          const auto class_name = m_match_stack.back()->text;
+
+          while (Eol()) {}
+
+          if (!Class_Block(class_name)) {
+            throw exception::eval_error("Incomplete 'class' block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Class_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+
+      /// Reads a while block from input
+      bool While() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("while")) {
+          retval = true;
+
+          if (!Char('(')) {
+            throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          if (!(Operator() && Char(')'))) {
+            throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          while (Eol()) {}
+
+          if (!Block()) {
+            throw exception::eval_error("Incomplete 'while' block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::While_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+      /// Reads the ranged `for` conditions from input
+      bool Range_Expression() {
+        // the first element will have already been captured by the For_Guards() call that preceeds it
+        return Char(':') && Equation();
+      }
+
+
+      /// Reads the C-style `for` conditions from input
+      bool For_Guards() {
+        if (!(Equation() && Eol()))
+        {
+          if (!Eol())
+          {
+            return false;
+          } else {
+            m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
+          }
+        }
+
+        if (!(Equation() && Eol()))
+        {
+          if (!Eol())
+          {
+            return false;
+          } else {
+            m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Constant_AST_Node<Tracer>>(Boxed_Value(true)));
+          }
+        }
+
+        if (!Equation())
+        {
+          m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
+        }
+
+        return true; 
+      }
+
+
+      /// Reads a for block from input
+      bool For() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("for")) {
+          retval = true;
+
+          if (!Char('(')) {
+            throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          const bool classic_for = For_Guards() && Char(')');
+          if (!classic_for && !(Range_Expression() && Char(')'))) {
+            throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          while (Eol()) {}
+
+          if (!Block()) {
+            throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          const auto num_children = m_match_stack.size() - prev_stack_top;
+
+          if (classic_for) {
+            if (num_children != 4) {
+              throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+            build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
+          } else {
+            if (num_children != 3) {
+              throw exception::eval_error("Incomplete ranged-for expression", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+            build_match<eval::Ranged_For_AST_Node<Tracer>>(prev_stack_top);
+          }
+        }
+
+        return retval;
+      }
+
+
+      /// Reads a case block from input
+      bool Case() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("case")) {
+          retval = true;
+
+          if (!Char('(')) {
+            throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          if (!(Operator() && Char(')'))) {
+            throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          while (Eol()) {}
+
+          if (!Block()) {
+            throw exception::eval_error("Incomplete 'case' block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Case_AST_Node<Tracer>>(prev_stack_top);
+        } else if (Keyword("default")) {
+          retval = true;
+
+          while (Eol()) {}
+
+          if (!Block()) {
+            throw exception::eval_error("Incomplete 'default' block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Default_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+
+      /// Reads a switch statement from input
+      bool Switch() {
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("switch")) {
+
+          if (!Char('(')) {
+            throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          if (!(Operator() && Char(')'))) {
+            throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          while (Eol()) {}
+
+          if (Char('{')) {
+            while (Eol()) {}
+
+            while (Case()) {
+              while (Eol()) { } // eat
+            }
+
+            while (Eol()) { } // eat
+
+            if (!Char('}')) {
+              throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+          }
+          else {
+            throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Switch_AST_Node<Tracer>>(prev_stack_top);
+          return true;
+
+        } else {
+          return false;
+        }
+
+      }
+
+
+      /// Reads a curly-brace C-style class block from input
+      bool Class_Block(const std::string &t_class_name) {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Char('{')) {
+          retval = true;
+
+          Class_Statements(t_class_name);
+          if (!Char('}')) {
+            throw exception::eval_error("Incomplete class block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          if (m_match_stack.size() == prev_stack_top) {
+            m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
+          }
+
+          build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+      /// Reads a curly-brace C-style block from input
+      bool Block() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Char('{')) {
+          retval = true;
+
+          Statements();
+          if (!Char('}')) {
+            throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          if (m_match_stack.size() == prev_stack_top) {
+            m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
+          }
+
+          build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+      /// Reads a return statement from input
+      bool Return() {
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("return")) {
+          Operator();
+          build_match<eval::Return_AST_Node<Tracer>>(prev_stack_top);
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads a break statement from input
+      bool Break() {
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("break")) {
+          build_match<eval::Break_AST_Node<Tracer>>(prev_stack_top);
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads a continue statement from input
+      bool Continue() {
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Keyword("continue")) {
+          build_match<eval::Continue_AST_Node<Tracer>>(prev_stack_top);
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads a dot expression(member access), then proceeds to check if it's a function or array call
+      bool Dot_Fun_Array() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+        if (Lambda() || Num() || Quoted_String() || Single_Quoted_String() ||
+            Paren_Expression() || Inline_Container() || Id(false))
+        {
+          retval = true;
+          bool has_more = true;
+
+          while (has_more) {
+            has_more = false;
+
+            if (Char('(')) {
+              has_more = true;
+
+              Arg_List();
+              if (!Char(')')) {
+                throw exception::eval_error("Incomplete function call", File_Position(m_position.line, m_position.col), *m_filename);
+              }
+
+              build_match<eval::Fun_Call_AST_Node<Tracer>>(prev_stack_top);
+              /// \todo Work around for method calls until we have a better solution
+              if (!m_match_stack.back()->children.empty()) {
+                if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Dot_Access) {
+                  if (m_match_stack.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
+}
+                  if (m_match_stack.back()->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
+}
+                  auto dot_access = std::move(m_match_stack.back()->children[0]);
+                  auto func_call = std::move(m_match_stack.back());
+                  m_match_stack.pop_back();
+                  func_call->children.erase(func_call->children.begin());
+                  if (dot_access->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
+}
+                  func_call->children.insert(func_call->children.begin(), std::move(dot_access->children.back()));
+                  dot_access->children.pop_back();
+                  dot_access->children.push_back(std::move(func_call));
+                  if (dot_access->children.size() != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
+}
+                  m_match_stack.push_back(std::move(dot_access));
+                }
+              }
+            } else if (Char('[')) {
+              has_more = true;
+
+              if (!(Operator() && Char(']'))) {
+                throw exception::eval_error("Incomplete array access", File_Position(m_position.line, m_position.col), *m_filename);
+              }
+
+              build_match<eval::Array_Call_AST_Node<Tracer>>(prev_stack_top);
+            }
+            else if (Symbol(".")) {
+              has_more = true;
+              if (!(Id(true))) {
+                throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
+              }
+
+              if ( std::distance(m_match_stack.begin() + static_cast<int>(prev_stack_top), m_match_stack.end()) != 2) {
+                throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
+              }
+              build_match<eval::Dot_Access_AST_Node<Tracer>>(prev_stack_top);
+            }
+          }
+        }
+
+        return retval;
+      }
+
+      /// Reads a variable declaration from input
+      bool Var_Decl(const bool t_class_context = false, const std::string &t_class_name = "") {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (t_class_context && (Keyword("attr") || Keyword("auto") || Keyword("var"))) {
+          retval = true;
+
+          m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(t_class_name, m_position.line, m_position.col));
+
+          if (!Id(true)) {
+            throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Attr_Decl_AST_Node<Tracer>>(prev_stack_top);
+        } else if (Keyword("auto") || Keyword("var") ) {
+          retval = true;
+
+          if (Reference()) {
+            // we built a reference node - continue
+          } else if (Id(true)) {
+            build_match<eval::Var_Decl_AST_Node<Tracer>>(prev_stack_top);
+          } else {
+            throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+        } else if (Keyword("global")) {
+          retval = true;
+
+          if (!(Reference() || Id(true))) {
+            throw exception::eval_error("Incomplete global declaration", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Global_Decl_AST_Node<Tracer>>(prev_stack_top);
+        } else if (Keyword("attr")) {
+          retval = true;
+
+          if (!Id(true)) {
+            throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+          if (!Symbol("::")) {
+            throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+          if (!Id(true)) {
+            throw exception::eval_error("Missing attribute name in definition", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+
+          build_match<eval::Attr_Decl_AST_Node<Tracer>>(prev_stack_top);
+        }
+
+        return retval;
+      }
+
+      /// Reads an expression surrounded by parentheses from input
+      bool Paren_Expression() {
+        if (Char('(')) {
+          if (!Operator()) {
+            throw exception::eval_error("Incomplete expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+          if (!Char(')')) {
+            throw exception::eval_error("Missing closing parenthesis ')'", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads, and identifies, a short-form container initialization from input
+      bool Inline_Container() {
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Char('[')) {
+          Container_Arg_List();
+
+          if (!Char(']')) {
+            throw exception::eval_error("Missing closing square bracket ']' in container initializer", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+          if ((prev_stack_top != m_match_stack.size()) && (!m_match_stack.back()->children.empty())) {
+            if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Value_Range) {
+              build_match<eval::Inline_Range_AST_Node<Tracer>>(prev_stack_top);
+            }
+            else if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Map_Pair) {
+              build_match<eval::Inline_Map_AST_Node<Tracer>>(prev_stack_top);
+            }
+            else {
+              build_match<eval::Inline_Array_AST_Node<Tracer>>(prev_stack_top);
+            }
+          }
+          else {
+            build_match<eval::Inline_Array_AST_Node<Tracer>>(prev_stack_top);
+          }
+
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Parses a variable specified with a & aka reference
+      bool Reference() {
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (Symbol("&")) {
+          if (!Id(true)) {
+            throw exception::eval_error("Incomplete '&' expression", File_Position(m_position.line, m_position.col), *m_filename);
+          }
+
+          build_match<eval::Reference_AST_Node<Tracer>>(prev_stack_top);
+          return true;
+        } else {
+          return false;
+        }
+      }
+
+      /// Reads a unary prefixed expression from input
+      bool Prefix() {
+        const auto prev_stack_top = m_match_stack.size();
+        using SS = utility::Static_String;
+        constexpr const std::array<utility::Static_String, 6> prefix_opers{{
+            SS{"++"}, 
+            SS{"--"}, 
+            SS{"-"}, 
+            SS{"+"}, 
+            SS{"!"}, 
+            SS{"~"}
+        }};
+
+        for (const auto &oper : prefix_opers)
+        {
+          const bool is_char = oper.size() == 1;
+          if ((is_char && Char(oper.c_str()[0])) || (!is_char && Symbol(oper)))
+          {
+            if (!Operator(m_operators.size()-1)) {
+              throw exception::eval_error("Incomplete prefix '" + std::string(oper.c_str()) + "' expression", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+
+            build_match<eval::Prefix_AST_Node<Tracer>>(prev_stack_top, oper.c_str());
+            return true;
+          }
+        }
+
+        return false;
+      }
+
+      /// Parses any of a group of 'value' style ast_node groups from input
+      bool Value() {
+        return Var_Decl() || Dot_Fun_Array() || Prefix();
+      }
+
+      bool Operator_Helper(const size_t t_precedence, std::string &oper) {
+        for (auto & elem : m_operator_matches[t_precedence]) {
+          if (Symbol(elem)) {
+            oper = elem.c_str();
+            return true;
+          }
+        }
+        return false;
+      }
+
+      bool Operator(const size_t t_precedence = 0) {
+        bool retval = false;
+        const auto prev_stack_top = m_match_stack.size();
+
+        if (m_operators[t_precedence] != Operator_Precidence::Prefix) {
+          if (Operator(t_precedence+1)) {
+            retval = true;
+            std::string oper;
+            while (Operator_Helper(t_precedence, oper)) {
+              while (Eol()) {}
+              if (!Operator(t_precedence+1)) {
+                throw exception::eval_error("Incomplete '" + oper + "' expression",
+                    File_Position(m_position.line, m_position.col), *m_filename);
+              }
+
+              switch (m_operators[t_precedence]) {
+                case(Operator_Precidence::Ternary_Cond) :
+                  if (Symbol(":")) {
+                    if (!Operator(t_precedence+1)) {
+                      throw exception::eval_error("Incomplete '" + oper + "' expression",
+                          File_Position(m_position.line, m_position.col), *m_filename);
+                    }
+                    build_match<eval::If_AST_Node<Tracer>>(prev_stack_top);
+                  }
+                  else {
+                    throw exception::eval_error("Incomplete '" + oper + "' expression",
+                        File_Position(m_position.line, m_position.col), *m_filename);
+                  }
+                  break;
+
+                case(Operator_Precidence::Addition) :
+                case(Operator_Precidence::Multiplication) :
+                case(Operator_Precidence::Shift) :
+                case(Operator_Precidence::Equality) :
+                case(Operator_Precidence::Bitwise_And) :
+                case(Operator_Precidence::Bitwise_Xor) :
+                case(Operator_Precidence::Bitwise_Or) :
+                case(Operator_Precidence::Comparison) :
+                  build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, oper);
+                  break;
+
+                case(Operator_Precidence::Logical_And) :
+                  build_match<eval::Logical_And_AST_Node<Tracer>>(prev_stack_top, oper);
+                  break;
+                case(Operator_Precidence::Logical_Or) :
+                  build_match<eval::Logical_Or_AST_Node<Tracer>>(prev_stack_top, oper);
+                  break;
+                case(Operator_Precidence::Prefix) :
+                  assert(false); // cannot reach here because of if() statement at the top
+                  break;
+
+//                default:
+//                  throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_position.line, m_position.col), *m_filename);
+              }
+            }
+          }
+        } else {
+          return Value();
+        }
+
+        return retval;
+      }
+
+      /// Reads a pair of values used to create a map initialization from input
+      bool Map_Pair() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+        const auto prev_pos = m_position;
+
+        if (Operator()) {
+          if (Symbol(":")) {
+            retval = true;
+            if (!Operator()) {
+              throw exception::eval_error("Incomplete map pair", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+
+            build_match<eval::Map_Pair_AST_Node<Tracer>>(prev_stack_top);
+          }
+          else {
+            m_position = prev_pos;
+            while (prev_stack_top != m_match_stack.size()) {
+              m_match_stack.pop_back();
+            }
+          }
+        }
+
+        return retval;
+      }
+
+      /// Reads a pair of values used to create a range initialization from input
+      bool Value_Range() {
+        bool retval = false;
+
+        const auto prev_stack_top = m_match_stack.size();
+        const auto prev_pos = m_position;
+
+        if (Operator()) {
+          if (Symbol("..")) {
+            retval = true;
+            if (!Operator()) {
+              throw exception::eval_error("Incomplete value range", File_Position(m_position.line, m_position.col), *m_filename);
+            }
+
+            build_match<eval::Value_Range_AST_Node<Tracer>>(prev_stack_top);
+          }
+          else {
+            m_position = prev_pos;
+            while (prev_stack_top != m_match_stack.size()) {
+              m_match_stack.pop_back();
+            }
+          }
+        }
+
+        return retval;
+      }
+
+      /// Parses a string of binary equation operators
+      bool Equation() {
+        const auto prev_stack_top = m_match_stack.size();
+
+        using SS = utility::Static_String;
+
+        if (Operator()) {
+          for (const auto &sym : {SS{"="}, SS{":="}, SS{"+="}, SS{"-="}, SS{"*="}, SS{"/="}, SS{"%="}, SS{"<<="}, SS{">>="}, SS{"&="}, SS{"^="}, SS{"|="}}) 
+          {
+            if (Symbol(sym, true)) {
+              SkipWS(true);
+              if (!Equation()) {
+                throw exception::eval_error("Incomplete equation", File_Position(m_position.line, m_position.col), *m_filename);
+              }
+
+              build_match<eval::Equation_AST_Node<Tracer>>(prev_stack_top, sym.c_str());
+              return true;
+            }
+          }
+          return true;
+        }
+
+        return false;
+      }
+
+      /// Parses statements allowed inside of a class block
+      bool Class_Statements(const std::string &t_class_name) {
+        bool retval = false;
+
+        bool has_more = true;
+        bool saw_eol = true;
+
+        while (has_more) {
+          const auto start = m_position;
+          if (Def(true, t_class_name) || Var_Decl(true, t_class_name)) {
+            if (!saw_eol) {
+              throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename);
+            }
+            has_more = true;
+            retval = true;
+            saw_eol = true;
+          } else if (Eol()) {
+            has_more = true;
+            retval = true;
+            saw_eol = true;
+          } else {
+            has_more = false;
+          }
+        }
+
+        return retval;
+      }
+
+      /// Top level parser, starts parsing of all known parses
+      bool Statements(const bool t_class_allowed = false) {
+        bool retval = false;
+
+        bool has_more = true;
+        bool saw_eol = true;
+
+        while (has_more) {
+          const auto start = m_position;
+          if (Def() || Try() || If() || While() || Class(t_class_allowed) || For() || Switch()) {
+            if (!saw_eol) {
+              throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename);
+            }
+            has_more = true;
+            retval = true;
+            saw_eol = true;
+          }
+          else if (Return() || Break() || Continue() || Equation()) {
+            if (!saw_eol) {
+              throw exception::eval_error("Two expressions missing line separator", File_Position(start.line, start.col), *m_filename);
+            }
+            has_more = true;
+            retval = true;
+            saw_eol = false;
+          }
+          else if (Block() || Eol()) {
+            has_more = true;
+            retval = true;
+            saw_eol = true;
+          }
+          else {
+            has_more = false;
+          }
+        }
+
+        return retval;
+      }
+
+      AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override
+      {
+        ChaiScript_Parser<Tracer, Optimizer> parser(m_tracer, m_optimizer);
+        return parser.parse_internal(t_input, t_fname);
+      }
+
+      eval::AST_Node_Impl_Ptr<Tracer> parse_instr_eval(const std::string &t_input)
+      {
+        auto last_position    = m_position;
+        auto last_filename    = m_filename;
+        auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){});
+
+        auto retval = parse_internal(t_input, "instr eval");
+
+        m_position = std::move(last_position);
+        m_filename = std::move(last_filename);
+        m_match_stack = std::move(last_match_stack);
+
+        return eval::AST_Node_Impl_Ptr<Tracer>(dynamic_cast<eval::AST_Node_Impl<Tracer>*>(retval.release()));
+      }
+
+      /// Parses the given input string, tagging parsed ast_nodes with the given m_filename.
+      AST_NodePtr parse_internal(const std::string &t_input, std::string t_fname) {
+        m_position = Position(t_input.begin(), t_input.end());
+        m_filename = std::make_shared<std::string>(std::move(t_fname));
+
+        if ((t_input.size() > 1) && (t_input[0] == '#') && (t_input[1] == '!')) {
+          while (m_position.has_more() && (!Eol())) {
+            ++m_position;
+          }
+        }
+
+        if (Statements(true)) {
+          if (m_position.has_more()) {
+            throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), *m_filename);
+          } else {
+            build_match<eval::File_AST_Node<Tracer>>(0);
+          }
+        } else {
+          m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
+        }
+
+        AST_NodePtr retval(std::move(m_match_stack.front()));
+        m_match_stack.clear();
+        return retval;
+      }
+    };
+  }
+}
+
+#if defined(CHAISCRIPT_MSVC) && defined(CHAISCRIPT_PUSHED_MIN_MAX)
+#undef CHAISCRIPT_PUSHED_MIN_MAX
+#pragma pop_macro("min")
+#pragma pop_macro("max")
+#endif
+
+
+#endif /* CHAISCRIPT_PARSER_HPP_ */
+

+ 81 - 0
chaiscript/language/chaiscript_posix.hpp

@@ -0,0 +1,81 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_POSIX_HPP_
+#define CHAISCRIPT_POSIX_HPP_
+
+namespace chaiscript
+{
+  namespace detail
+  {
+    struct Loadable_Module
+    {
+      struct DLModule
+      {
+        explicit DLModule(const std::string &t_filename)
+          : m_data(dlopen(t_filename.c_str(), RTLD_NOW))
+        {
+          if (m_data == nullptr)
+          {
+            throw chaiscript::exception::load_module_error(dlerror());
+          }
+        }
+
+        DLModule(DLModule &&) = default;
+        DLModule &operator=(DLModule &&) = default;
+        DLModule(const DLModule &) = delete;
+        DLModule &operator=(const DLModule &) = delete;
+
+        ~DLModule()
+        {
+          dlclose(m_data);
+        }
+
+        void *m_data;
+      };
+
+      template<typename T>
+        struct DLSym
+        {
+          DLSym(DLModule &t_mod, const std::string &t_symbol)
+            : m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str())))
+          {
+            if (!m_symbol)
+            {
+              throw chaiscript::exception::load_module_error(dlerror());
+            }
+          }
+
+          static T cast_symbol(void *p)
+          {
+            union cast_union
+            {
+              T func_ptr;
+              void *in_ptr;
+            };
+
+            cast_union c;
+            c.in_ptr = p;
+            return c.func_ptr;
+          }
+
+          T m_symbol;
+        };
+
+      Loadable_Module(const std::string &t_module_name, const std::string &t_filename)
+        : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name),
+        m_moduleptr(m_func.m_symbol())
+      {
+      }
+
+      DLModule m_dlmodule;
+      DLSym<Create_Module_Func> m_func;
+      ModulePtr m_moduleptr;
+    };
+  }
+}
+#endif
+

+ 562 - 0
chaiscript/language/chaiscript_prelude.hpp

@@ -0,0 +1,562 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// and 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_PRELUDE_HPP_
+#define CHAISCRIPT_PRELUDE_HPP_
+
+namespace chaiscript {
+struct ChaiScript_Prelude { 
+  static std::string chaiscript_prelude() {  return R"chaiscript(
+
+def lt(l, r) { 
+  if (call_exists(`<`, l, r)) { 
+    l < r 
+  } else { 
+    type_name(l) < type_name(r) 
+  } 
+}
+
+
+def gt(l, r) { 
+  if (call_exists(`>`, l, r)) { 
+    l > r 
+  } else { 
+    type_name(l) > type_name(r) 
+  } 
+}
+
+def eq(l, r) { 
+  if (call_exists(`==`, l, r)) { 
+    l == r 
+  } else { 
+    false 
+  } 
+}
+
+def new(x) { 
+  eval(type_name(x))(); 
+}
+
+def clone(double x) {
+  double(x).clone_var_attrs(x)
+}
+
+def clone(string x) {
+  string(x).clone_var_attrs(x)
+}
+
+def clone(vector x) {
+  vector(x).clone_var_attrs(x)
+}
+
+
+def clone(int x) {
+  int(x).clone_var_attrs(x)
+}
+
+def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x)
+{
+  eval(type_name(x))(x).clone_var_attrs(x);
+}
+
+
+# to_string for Pair()
+def to_string(x) : call_exists(first, x) && call_exists(second, x) { 
+  "<" + x.first.to_string() + ", " + x.second.to_string() + ">"; 
+}
+
+# to_string for containers
+def to_string(x) : call_exists(range, x) && !x.is_type("string"){ 
+  "[" + x.join(", ") + "]"; 
+}
+
+# Prints to console with no carriage return
+def puts(x) { 
+  print_string(x.to_string()); 
+}
+
+# Prints to console with carriage return
+def print(x) { 
+  println_string(x.to_string()); 
+}
+
+# Returns the maximum value of two numbers
+def max(a, b) {
+  if (a>b) { 
+    a 
+  } else { 
+    b 
+  } 
+} 
+
+# Returns the minimum value of two numbers
+def min(a, b) 
+{ 
+  if (a<b) 
+  { 
+    a 
+  } else { 
+    b 
+  } 
+}
+
+
+# Returns true if the value is odd
+def odd(x)  { 
+  if (x % 2 == 1) 
+  { 
+    true 
+  } else { 
+    false 
+  } 
+}
+
+
+# Returns true if the value is even
+def even(x) 
+{ 
+  if (x % 2 == 0) 
+  { 
+    true 
+  } else { 
+    false 
+  } 
+} 
+
+
+# Inserts the third value at the position of the second value into the container of the first
+# while making a clone. 
+def insert_at(container, pos, x) 
+{ 
+  container.insert_ref_at(pos, clone(x)); 
+} 
+
+# Returns the reverse of the given container
+def reverse(container) {
+  auto retval := new(container); 
+  auto r := range(container); 
+  while (!r.empty()) { 
+    retval.push_back(r.back()); 
+    r.pop_back(); 
+  } 
+  retval; 
+}
+
+
+def range(r) : call_exists(range_internal, r)
+{
+  var ri := range_internal(r);
+  ri.get_var_attr("internal_obj") := r;
+  ri;
+}
+
+# Return a range from a range 
+def range(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r) 
+{
+  clone(r);
+}
+
+
+# The retro attribute that contains the underlying range 
+attr retro::m_range; 
+
+# Creates a retro from a retro by returning the original range
+def retro(r) : call_exists(get_type_name, r) && get_type_name(r) == "retro" 
+{ 
+  clone(r.m_range) 
+}
+
+
+# Creates a retro range from a range
+def retro::retro(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r) 
+{ 
+  this.m_range = r; 
+}
+
+# Returns the first value of a retro
+def retro::front()  
+{ 
+  back(this.m_range) 
+}
+
+# Returns the last value of a retro
+def retro::back()  
+{ 
+  front(this.m_range) 
+}
+
+# Moves the back iterator of a retro towards the front by one 
+def retro::pop_back()  
+{
+  pop_front(this.m_range) 
+}
+
+# Moves the front iterator of a retro towards the back by one 
+def retro::pop_front()  
+{ 
+  pop_back(this.m_range) 
+} 
+
+# returns true if the retro is out of elements 
+def retro::empty() 
+{ 
+  empty(this.m_range); 
+} 
+
+# Performs the second value function over the container first value
+def for_each(container, func) : call_exists(range, container) { 
+  var t_range := range(container); 
+  while (!t_range.empty()) { 
+    func(t_range.front()); 
+    t_range.pop_front(); 
+  } 
+} 
+
+def any_of(container, func) : call_exists(range, container) { 
+  var t_range := range(container); 
+  while (!t_range.empty()) { 
+    if (func(t_range.front())) {
+      return true;
+    }
+    t_range.pop_front(); 
+  }
+  false;
+}
+
+def all_of(container, func) : call_exists(range, container) { 
+  var t_range := range(container); 
+  while (!t_range.empty()) { 
+    if (!func(t_range.front())) {
+      return false;
+    }
+    t_range.pop_front(); 
+  }
+
+  true;
+}
+
+def back_inserter(container) {
+  bind(push_back, container, _);
+}
+
+def contains(container, item, compare_func) : call_exists(range, container) { 
+  auto t_range := range(container); 
+  while (!t_range.empty()) { 
+    if ( compare_func(t_range.front(), item) ) { 
+      return true; 
+    } 
+
+    t_range.pop_front(); 
+  } 
+  false; 
+} 
+
+def contains(container, item) { 
+  contains(container, item, eq) 
+} 
+
+def map(container, func, inserter) : call_exists(range, container) { 
+  auto range := range(container); 
+  while (!range.empty()) { 
+    inserter(func(range.front())); 
+    range.pop_front(); 
+  } 
+} 
+
+# Performs the second value function over the container first value. Creates a new container with the results
+def map(container, func) { 
+  auto retval := new(container); 
+  map(container, func, back_inserter(retval));
+  retval;
+}
+
+# Performs the second value function over the container first value. Starts with initial and continues with each element.
+def foldl(container, func, initial) : call_exists(range, container){ 
+  auto retval = initial; 
+  auto range := range(container); 
+  while (!range.empty()) { 
+    retval = (func(range.front(), retval)); 
+    range.pop_front(); 
+  } 
+  retval; 
+}
+
+# Returns the sum of the elements of the given value
+def sum(container) { 
+  foldl(container, `+`, 0.0) 
+} 
+
+# Returns the product of the elements of the given value
+def product(container) { 
+  foldl(container, `*`, 1.0) 
+} 
+
+# Returns a new container with the elements of the first value concatenated with the elements of the second value
+def concat(x, y) : call_exists(clone, x) { 
+  auto retval = x; 
+  auto inserter := back_inserter(retval); 
+  auto range := range(y); 
+  while (!range.empty()) { 
+    inserter(range.front()); 
+    range.pop_front(); 
+  } 
+  retval; 
+}
+
+
+def take(container, num, inserter) : call_exists(range, container) { 
+  auto r := range(container); 
+  auto i = num; 
+  while ((i > 0) && (!r.empty())) { 
+    inserter(r.front()); 
+    r.pop_front(); 
+    --i; 
+  } 
+}
+
+
+# Returns a new container with the given number of elements taken from the container
+def take(container, num) {
+  auto retval := new(container); 
+  take(container, num, back_inserter(retval)); 
+  retval; 
+}
+
+
+def take_while(container, f, inserter) : call_exists(range, container) { 
+  auto r := range(container); 
+  while ((!r.empty()) && f(r.front())) { 
+    inserter(r.front()); 
+    r.pop_front(); 
+  } 
+} 
+
+
+# Returns a new container with the given elements match the second value function
+def take_while(container, f) {
+  auto retval := new(container); 
+  take_while(container, f, back_inserter(retval)); 
+  retval;
+}
+
+
+def drop(container, num, inserter) : call_exists(range, container) { 
+  auto r := range(container); 
+  auto i = num; 
+  while ((i > 0) && (!r.empty())) { 
+    r.pop_front(); 
+    --i; 
+  } 
+  while (!r.empty()) { 
+    inserter(r.front()); 
+    r.pop_front(); 
+  } 
+}
+
+
+# Returns a new container with the given number of elements dropped from the given container 
+def drop(container, num) {
+  auto retval := new(container); 
+  drop(container, num, back_inserter(retval)); 
+  retval; 
+}
+
+
+def drop_while(container, f, inserter) : call_exists(range, container) { 
+  auto r := range(container); 
+  while ((!r.empty())&& f(r.front())) { 
+    r.pop_front(); 
+  } 
+  while (!r.empty()) { 
+    inserter(r.front()); 
+    r.pop_front(); 
+  } 
+}
+
+
+# Returns a new container with the given elements dropped that match the second value function
+def drop_while(container, f) {
+  auto retval := new(container); 
+  drop_while(container, f, back_inserter(retval)); 
+  retval; 
+}
+
+
+# Applies the second value function to the container. Starts with the first two elements. Expects at least 2 elements.
+def reduce(container, func) : container.size() >= 2 && call_exists(range, container) { 
+  auto r := range(container); 
+  auto retval = r.front(); 
+  r.pop_front(); 
+  retval = func(retval, r.front()); 
+  r.pop_front(); 
+  while (!r.empty()) { 
+    retval = func(retval, r.front()); 
+    r.pop_front(); 
+  } 
+  retval; 
+}
+
+
+# Returns a string of the elements in container delimited by the second value string
+def join(container, delim) { 
+  auto retval = ""; 
+  auto range := range(container); 
+  if (!range.empty()) { 
+    retval += to_string(range.front()); 
+    range.pop_front(); 
+    while (!range.empty()) { 
+      retval += delim; 
+      retval += to_string(range.front()); 
+      range.pop_front(); 
+    } 
+  } 
+  retval; 
+}
+
+
+def filter(container, f, inserter) : call_exists(range, container) { 
+  auto r := range(container); 
+  while (!r.empty()) { 
+    if (f(r.front())) { 
+      inserter(r.front()); 
+    } 
+    r.pop_front(); 
+  } 
+} 
+
+
+# Returns a new Vector which match the second value function
+def filter(container, f) { 
+  auto retval := new(container); 
+  filter(container, f, back_inserter(retval));
+  retval;
+}
+
+
+def generate_range(x, y, inserter) { 
+  auto i = x; 
+  while (i <= y) { 
+    inserter(i); 
+    ++i; 
+  } 
+}
+
+
+# Returns a new Vector which represents the range from the first value to the second value
+def generate_range(x, y) { 
+  auto retval := Vector(); 
+  generate_range(x,y,back_inserter(retval)); 
+  retval; 
+}
+
+
+# Returns a new Vector with the first value to the second value as its elements
+def collate(x, y) { 
+  return [x, y]; 
+}
+
+
+def zip_with(f, x, y, inserter) : call_exists(range, x) && call_exists(range, y) { 
+  auto r_x := range(x); 
+  auto r_y := range(y); 
+  while (!r_x.empty() && !r_y.empty()) { 
+    inserter(f(r_x.front(), r_y.front())); 
+    r_x.pop_front(); 
+    r_y.pop_front(); 
+  } 
+}
+
+
+# Returns a new Vector which joins matching elements of the second and third value with the first value function
+def zip_with(f, x, y) { 
+  auto retval := Vector(); 
+  zip_with(f,x,y,back_inserter(retval)); 
+  retval;
+}
+
+
+# Returns a new Vector which joins matching elements of the first and second
+def zip(x, y) { 
+  zip_with(collate, x, y); 
+}
+
+
+# Returns the position of the second value string in the first value string
+def string::find(string substr) { 
+  find(this, substr, size_t(0)); 
+}
+
+
+# Returns the position of last match of the second value string in the first value string
+def string::rfind(string substr) { 
+  rfind(this, substr, size_t(-1)); 
+}
+
+
+# Returns the position of the first match of elements in the second value string in the first value string
+def string::find_first_of(string list) { 
+  find_first_of(this, list, size_t(0)); 
+} 
+
+
+# Returns the position of the last match of elements in the second value string in the first value string
+def string::find_last_of(string list) {
+  find_last_of(this, list, size_t(-1)); 
+} 
+
+
+# Returns the position of the first non-matching element in the second value string in the first value string
+def string::find_first_not_of(string list) { 
+  find_first_not_of(this, list, size_t(0)); 
+} 
+
+
+# Returns the position of the last non-matching element in the second value string in the first value string
+def string::find_last_not_of(string list) { 
+  find_last_not_of(this, list, size_t(-1)); 
+} 
+
+
+def string::ltrim() { 
+  drop_while(this, fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'}); 
+} 
+
+
+def string::rtrim() { 
+  reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'})); 
+} 
+
+
+def string::trim() { 
+  ltrim(rtrim(this)); 
+}
+
+
+def find(container, value, Function compare_func) : call_exists(range, container) { 
+  auto range := range(container); 
+  while (!range.empty()) { 
+    if (compare_func(range.front(), value)) { 
+      return range; 
+    } else { 
+      range.pop_front(); 
+    } 
+  } 
+  range; 
+} 
+
+
+def find(container, value) { 
+  find(container, value, eq) 
+} 
+
+
+)chaiscript";
+}
+
+};
+}
+
+#endif /* CHAISCRIPT_PRELUDE_HPP_ */

+ 830 - 0
chaiscript/language/chaiscript_prelude_docs.hpp

@@ -0,0 +1,830 @@
+/// This file is not technically part of the ChaiScript API. It is used solely for generating Doxygen docs
+/// regarding the ChaiScript standard runtime library.
+
+/// \brief Items in this namespace exist in the ChaiScript language runtime. They are not part of the C++ API
+namespace ChaiScript_Language
+{
+
+/// \page LangStandardLibraryRef ChaiScript Language Standard Library Reference
+///
+/// ChaiScript, at its core, has some very functional programming-inspired habits. Few places show this off as clearly 
+/// as the prelude, itself a name taken as a nod to the popular functional language Haskell. This prelude is available 
+/// to all standard ChaiScript applications, and provides a simple foundation for using numbers, strings, and ranges 
+/// (the general category of Range cs and their iteration).
+///
+
+
+/// \brief Generic concept of a value in ChaiScript. 
+///
+/// The Object type exists merely as a concept. All objects in ChaiScript support this concept 
+/// and have the following methods available to them. All objects are stored internally as chaiscript::Boxed_Value types.
+///
+/// \sa chaiscript::Boxed_Value
+class Object
+{
+  public:
+    /// \brief Returns the Type_Info value for this Object
+    Type_Info get_type_info() const;
+
+    /// \brief Returns true if the Object is of the named type
+    bool is_type(string) const;
+
+    /// \brief Returns true if the Object is of the Type_Info passed in
+    bool is_type(Type_Info) const;
+
+    /// \brief Returns true if the Object is immutable
+    bool is_var_const() const;
+
+    /// \brief Returns true if the Object is a pointer and the pointer is null
+    bool is_var_null() const;
+
+    /// \brief Returns true if the Object is stored as a pointer
+    bool is_var_pointer() const;
+
+    /// \brief Returns true if the Object is stored as a reference    
+    bool is_var_reference() const;
+
+    /// \brief Returns true if the Object does not contain a value is is undefined.
+    bool is_var_undef() const;
+
+    /// \brief Returns the registered name of the type of the object. 
+    ///
+    /// \sa Type_Info::name();
+    string type_name() const;
+};
+
+/// \brief Item returned from a Range object from a Map
+class Map_Pair
+{
+  public:
+    /// \brief Returns the key of the Map entry
+    const string first();
+
+    /// \brief Returns the value Object of the Map entry 
+    Object second();
+};
+
+
+/// \brief Maps strings to Objects
+///
+/// ChaiScript has a built in shortcut for generating Map objects:
+/// 
+/// Example:
+/// \code
+/// eval> var m = ["a":1, "b":2];
+/// [<a,1>, <b,2>]
+/// eval> m.count("a");
+/// 1
+/// eval> m.count("c");
+/// 0
+/// eval> m.size();
+/// 2
+/// \endcode
+///
+/// Implemented as std::map<Boxed_Value>
+///
+/// \sa Map_Pair
+/// \sa chaiscript::bootstrap::standard_library::map_type
+class Map
+{
+  public:
+    /// \brief Returns an object that implements the Range concept for the Map_Pair's in this Map
+    Range range();
+
+    /// \brief Returns an object that implements the Const_Range concept for the Map_Pair's in this Map
+    Const_Range range() const;
+
+    /// \brief Returns the number of elements in the Map
+    int size() const;
+
+    /// \brief Returns the item at the given key, creating an undefined Object if the key does not yet exist in the map
+    Object operator[](string);
+
+    /// \brief Clears the map of all items
+    void clear();
+
+    /// \brief Returns the number of items in the Map with the given key. Returns 0 or 1 since this is not an std::multimap.
+    int count(string) const;
+
+    /// \brief Returns true if the map contains no items
+    bool empty() const;
+
+};
+
+
+/// \brief A concept implemented by string, Vector and Map. It is convertible to Range, default constructable and back_insertable
+class Container
+{
+  public:
+    void push_back(Object);
+    Range range();
+    Const_Range range() const;
+};
+
+
+/// \brief Converts o into a string.
+///
+/// \code
+/// eval> to_string(3).is_type("string") <br>
+/// true<br>
+/// \endcode
+string to_string(Object o);
+
+
+/// \brief Prints o to the terminal, without a trailing carriage return. Applies conversions to string automatically.
+/// \code
+/// eval> puts("hi, "); puts("there")
+/// hi, thereeval>
+/// \endcode
+/// \sa to_string
+/// \sa print
+void puts(Object o);
+
+
+/// \brief Prints o to the terminal, with a trailing carriage return. Applies conversions to string automatically
+/// \code
+/// eval> print("hello")
+/// hello
+/// eval>
+/// \endcode
+/// \sa to_string
+/// \sa puts
+void print(Object o);
+
+/// \brief ChaiScript representation of std::string. It is an std::string but only some member are exposed to ChaiScript.
+/// 
+/// Because the ChaiScript string object is an std::string, it is directly convertible to and from std::string
+/// using the chaiscript::boxed_cast and chaiscript::var functions.
+///
+/// With the exception of string::trim, string::rtrim, string::ltrim, all members are direct pass-throughs to the
+/// std::string of the same name. 
+///
+/// \note Object and function notations are equivalent in ChaiScript. This means that
+///       \c "bob".find("b") and \c find("bob", "b") are exactly the same. Most examples below follow the
+///       second formation of the function calls.
+/// \sa \ref keyworddef for extending existing C++ classes in ChaiScript
+/// \sa chaiscript::bootstrap::standard_library::string_type
+class string
+{
+  public:
+    /// \brief Finds the first instance of substr.
+    /// \code
+    /// eval> find("abab", "ab")
+    /// 0
+    /// \endcode
+    int find(string s) const;
+
+
+    /// \brief Finds the last instance of substr.
+    /// \code
+    /// eval> rfind("abab", "ab")
+    /// 2
+    /// \endcode
+    int rfind(string s) const;
+
+    /// \brief Finds the first of characters in list in the string.
+    ///
+    /// \code
+    /// eval> find_first_of("abab", "bec")
+    /// 1
+    /// \endcode
+    int find_first_of(string list) const;
+
+    /// \brief Finds the last of characters in list in the string.
+    ///
+    /// \code
+    /// eval> find_last_of("abab", "bec")
+    /// 3
+    /// \endcode
+    int find_last_of(string list) const;
+
+    /// \brief Finds the first non-matching character to list in the str string.
+    ///
+    /// \code
+    /// eval> find_first_not_of("abcd", "fec")
+    /// 0
+    /// \endcode
+    int find_first_not_of(string list) const;
+
+    /// \brief Finds the last non-matching character to list in the list string.
+    ///
+    /// \code
+    /// eval> find_last_not_of("abcd", "fec")
+    /// 3
+    /// \endcode 
+    int find_last_not_of(string list) const;
+
+    /// \brief Removes whitespace from the front of the string, returning a new string
+    ///
+    /// \note This function is implemented as a ChaiScript function using the def member function notation.
+    ///
+    /// \code
+    /// eval> ltrim("  bob")
+    /// bob
+    /// \endcode
+    ///
+    /// \sa \ref keyworddef
+    string lstrim() const;
+
+    /// \brief Removes whitespace from the back of the string, returning a new string
+    ///
+    /// \note This function is implemented as a ChaiScript function using the def member function notation.
+    ///
+    /// \code
+    /// eval> rtrim("bob  ") + "|"
+    /// bob|
+    /// \endcode
+    ///
+    /// \sa \ref keyworddef
+    string rtrim() const;
+
+    /// \brief Removes whitespace from the front and back of the string, returning a new string
+    ///
+    /// \note This function is implemented as a ChaiScript function using the def member function notation.
+    ///
+    /// \code
+    /// eval> trim("  bob  ") + "|"
+    /// bob|
+    /// \endcode
+    /// 
+    /// Equivalent to rtrim(ltrim("  bob  "));
+    ///
+    /// \sa \ref keyworddef
+    string trim() const;
+
+    /// \brief Returns the character at the given index in the string, const version
+    const char &operator[](int t_index) const;
+
+    /// \brief Returns the character at the given index in the string
+    char &operator[](int t_index);
+
+    /// \brief Returns underlying const char * for C api compatibility
+    const char *c_str() const;
+
+    /// \brief Returns a pointer to the raw data in the string
+    const char *data() const;
+
+    /// \brief Resets the string to empty
+    void clear();
+
+    /// \brief Returns true if the string is empty
+    bool empty() const;
+
+    /// \brief Returns the size of the string in bytes.
+    ///
+    /// This function normally returns size_t in C++. In ChaiScript the return value is cast to int
+    /// for ease of use.
+    int size() const;
+
+    /// \brief Returns an object that implements the Range concept for the characters of this string
+    Range range();
+
+    /// \brief Returns an object that implements the Const_Range concept for the characters of this string
+    Const_Range range() const;
+};
+
+/// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides
+///        easy iteration over the elements in a container.
+///
+/// Implemented by the template chaiscript::bootstrap::standard_library::Bidir_Range
+///
+/// \sa Const_Range
+class Range
+{
+  public:
+    /// \brief Returns the last item of the range
+    Object back();
+
+    /// \brief Returns true if the front and back pointers have passed each other, if no items
+    ///        are left in the Range
+    bool empty() const;
+
+    /// \brief Returns the first item of the range
+    Object front();
+
+    /// \brief Moves the back pointer back one.
+    /// 
+    /// \post back() returns the element at back() - 1;
+    void pop_back();
+
+    /// \brief Moves the front pointer forward one
+    /// 
+    /// \post front() returns the element at front() + 1;
+    void pop_front();
+
+};
+
+/// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides
+///        easy iteration over the elements in a container. Contained values are const.
+///
+/// Implemented by the template chaiscript::bootstrap::standard_library::Const_Bidir_Range
+///
+/// \sa Range
+class Const_Range
+{
+  public:
+    /// \brief Returns the last item of the range
+    const Object back();
+
+    /// \brief Returns true if the front and back pointers have passed each other, if no items
+    ///        are left in the Range
+    bool empty() const;
+
+    /// \brief Returns the first item of the range
+    const Object front();
+
+    /// \brief Moves the back pointer back one.
+    /// 
+    /// \post back() returns the element at back() - 1;
+    void pop_back();
+
+    /// \brief Moves the front pointer forward one
+    /// 
+    /// \post front() returns the element at front() + 1;
+    void pop_front();
+
+};
+
+/// \brief A vector of Objects
+///
+/// ChaiScript includes a shortcut for creating a Vector of Objects
+/// 
+/// Example:
+/// \code
+/// eval> var v = [1,2,3,4]
+/// [1, 2, 3, 4]
+/// eval> v[0];
+/// 1
+/// eval> v.size();
+/// 4
+/// \endcode 
+///
+/// Implemented with std::vector<chaiscript::Boxed_Value>
+/// 
+/// \sa chaiscript::bootstrap::standard_library::vector_type
+class Vector
+{
+  public:
+    /// \brief returns the Object at the given index. Throws an exception if the index does not exist
+    Object operator[](int t_index);
+
+    /// \brief returns a const Object at the given index. Throws an exception if the index does not exist.
+    const Object operator[](int t_index) const;
+
+    /// \brief returns the last item in the Vector
+    Object back();
+
+    /// \brief Clears the Vector of all items
+    void clear();
+
+    /// \brief Returns true if the Vector is contains 0 items
+    bool empty();
+
+    /// \brief Erases the element at the given index
+    void erase_at(int t_index);
+
+    /// \brief Returns the first item in the Vector
+    Object front();
+
+    /// \brief Inserts a new item in the Vector at the given index. The item is not cloned on insert
+    /// 
+    /// \sa insert_ref
+    void insert_ref_at(int, Object);
+
+    /// \brief Inserts a new item in the Vector at the given index. The item is cloned on insert
+    /// 
+    /// \sa insert_ref
+    void insert_at(int, Object);
+   
+    /// \brief Removes the last item from the Vector
+    void pop_back();
+
+    /// \brief Adds an item to the end of the Vector. The item is not cloned.
+    ///
+    /// \sa push_back
+    void push_back_ref(Object);
+
+    /// \brief Adds an item to the end of the Vector. The item is cloned.
+    ///
+    /// \sa push_back_ref
+    void push_back(Object);
+
+    /// \brief Returns a Range object for the entire vector
+    Range range();
+
+    /// \brief Returns a Const_Range object for the entire vector
+    Const_Range range() const;
+
+    /// \brief Returns the number of elements in the Vector
+    int size() const;
+
+};
+
+class Type_Info
+{
+  public:
+    /// \brief Compares this Type_Info object with another one and returns true if the two types are the same
+    ///        after const, pointer, reference are removed.
+    bool bare_equal(Type_Info t_ti) const;
+
+    /// \brief Returns the mangled C++ name for the type given by the compiler after const, pointer, reference is removed.
+    string cpp_bare_name() const;
+
+    /// \brief Returns the mangled C++ name for the type given by the compiler.
+    string cpp_name() const;
+
+    /// \brief Returns true if the type is const
+    bool is_type_const() const;
+
+    /// \brief Returns true if the type is a pointer
+    bool is_type_pointer() const;
+
+    /// \brief Returns true if the type is a reference 
+    bool is_type_reference() const;
+
+    /// \brief Returns true if the type is undefined 
+    bool is_type_undef() const;
+
+    /// \brief Returns true if the type is "void"
+    bool is_type_void() const;
+
+    /// \brief Returns the ChaiScript registered name for the type if one exists.
+    string name() const;
+
+};
+
+
+/// \brief Represents a function object in ChaiScript
+///
+/// A function object may be one function, such as:
+/// \code
+/// var f = fun(x) { return x; }
+/// \endcode
+/// 
+/// Or it may represent multiple functions
+/// \code
+/// var f2 = `-`; // represents the unary - as well as the set of binary - operators
+/// \endcode
+///
+/// Guarded function example
+/// \code
+/// def f3(x) : x > 2 {
+///  return x;
+/// }
+/// \endcode
+///
+/// Examples in the function definitions below will reference these examples
+class Function
+{
+  public:
+    /// \brief Returns the annotation description of the function
+    string get_annotation() const;
+
+    /// \brief Returns the arity of the function, -1 if the function takes a variable number of parameters
+    ///
+    /// Example:
+    /// \code
+    /// eval> f.get_arity()
+    /// 1
+    /// eval> f2.get_arity()
+    /// -1
+    /// \endcode
+    int get_arity() const;
+
+    /// \brief Returns a vector of the contained functions
+    /// 
+    /// Example:
+    /// \code
+    /// eval> f.get_contained_functions().size()
+    /// 0
+    /// eval> f2.get_contained_functions().size()
+    /// 11
+    /// eval> var v = f2.get_contained_functions();
+    /// v[0].get_arity()
+    /// 2
+    /// \endcode
+    Vector get_contained_functions() const;
+
+    /// \brief Returns a function guard as function
+    /// 
+    /// Example:
+    /// \code
+    /// eval> f.get_guard() // Throws exception
+    /// Function does not have a guard
+    /// eval> f3.get_guard().get_arity()
+    /// 1
+    /// \endcode
+    Function get_guard() const;
+
+    /// \brief Returns a vector of Type_Info objects that represent the param types for this function.
+    ///        The first value in the list is the return type.
+    /// 
+    /// If this function is a conglomerate of several functions (get_contained_values().size() > 0)
+    /// then the function returns as many Type_Info objects as it can. If the functions contained all have
+    /// the same arity, then it represents the arity. If they have different arities, it returns only
+    /// one value - the return type. 
+    /// 
+    /// For each parameter that is the same type, the type is returned. If the types are different
+    /// then a Type_Info for Object is returned.
+    ///
+    /// Example:
+    /// \code
+    /// eval> f2.get_param_types().size(); // Returns a Type_Info for Object for the return type
+    /// 1
+    /// \endcode
+    Vector get_param_types() const;
+
+    /// \brief Returns true if the function has a guard to it. Always returns false for a conglomerate function
+    bool has_guard() const;
+
+    /// \brief Calls the function with the given set of parameters and returns the value;
+    /// 
+    /// Example:
+    /// \code
+    /// eval> `-`.call([2,1]);
+    /// 1
+    /// \endcode
+    Object call(Vector t_params) const;
+}
+
+
+
+/// \brief Returns the max of a or b. Requires that operator>(a, b) exists
+/// Equivalent to 
+/// \code
+/// return a>b?a:b;
+/// \endcode
+///
+/// Example:
+/// \code
+/// eval> max(4, 10)
+/// 10
+/// \endcode
+Object max(Object a, Object b);
+
+/// \brief Returns the min of a or b. Requires that operator<(a, b) exists
+///
+/// Equivalent to 
+/// \code
+/// return a<b?a:b;
+/// \endcode
+///
+/// Example:
+/// \code
+/// eval> min(4, 10)
+/// 4
+/// \endcode
+Object min(Object a, Object b);
+
+/// \brief Returns true if x is an even integer. 
+/// 
+/// Will also work on any non-integer type for which an operator%(x, int) exists
+///
+/// Example:
+/// \code
+/// eval> even(4)
+/// true
+/// \endcode
+bool even(Object x);
+
+/// \brief Returns true if x is an odd integer. 
+/// 
+/// Will also work on any non-integer type for which an operator%(x, int) exists
+///
+/// Example:
+/// \code
+/// eval> odd(4)
+/// false 
+/// \endcode
+bool even(Object x);
+
+
+/// \brief Applies the function f over each element in the Range c.
+///
+/// Example:
+/// \code
+/// eval> for_each([1, 2, 3], print)
+/// 1
+/// 2
+/// 3
+/// \endcode
+void for_each(Range c, Function f);
+
+
+/// \brief Applies f over each element in the Range c, joining all the results.
+///
+/// Example:
+/// \code
+/// eval> map([1, 2, 3], odd)
+/// [true, false, true]
+/// \endcode
+Object map(Range c, Function f);
+
+
+/// \brief Starts with the initial value and applies the function f to it and the first element of the Range c. 
+///        The result is then applied to the second element, and so on until the elements are exhausted.
+///
+/// Example:
+/// \code
+/// eval> foldl([1, 2, 3, 4], `+`, 0)
+/// 10
+/// \endcode
+Object foldl(Range c, Function f, Object initial);
+
+
+/// \brief Returns the sum total of the values in the Range c.
+///
+/// Example:
+/// \code
+/// eval> sum([1, 2, 3, 4])
+/// 10
+/// \endcode
+///
+/// Equivalent to:
+/// \code
+/// foldl(c, `+`, 0.0);
+/// \endcode
+Numeric sum(Range c);
+
+
+/// \brief Returns the product of the value in the Range c.
+///
+/// Example:
+/// \code
+/// eval> product([1, 2, 3, 4])
+/// 24
+/// \endcode
+///
+/// Equivalent to:
+/// \code
+/// foldl(c, `*`, 1.0);
+/// \endcode
+Numeric product(Range c);
+
+
+/// \brief Takes num elements from the Range c, returning them.
+///
+/// Example:
+/// \code
+/// eval> take([1, 2, 3, 4], 2)
+/// [1, 2]
+/// \endcode
+///
+/// \returns A container of the same type that was passed in
+Object take(Range c, int num);
+
+
+/// \brief Takes elements from the Range c that match function f, stopping at the first non-match, returning them as a new Vector.
+///
+/// Example:
+/// \code
+/// eval> take_while([1, 2, 3], odd)
+/// [1]
+/// \endcode
+///
+/// \returns A container of the same type that was passed in
+Object take_while(Range c, Function f);
+
+
+/// \brief Drops num elements from the Range c, returning the remainder.
+///
+/// Example:
+/// \code
+/// eval> drop([1, 2, 3, 4], 2)
+/// [3, 4]
+/// \endcode
+///
+/// \returns A container of the same type that was passed in
+Object drop(Range c, int num);
+
+
+/// \brief Drops elements from the Range c that match f, stopping at the first non-match, returning the remainder.
+///
+/// Example:
+/// \code
+/// eval> drop_while([1, 2, 3], odd)         
+/// [2, 3]
+/// \endcode
+Object drop_while(Range c, Function f);
+
+
+/// \brief Similar to foldl, this takes the first two elements as its starting values for f. This assumes Range c has at least 2 elements.
+///
+/// Example:
+/// \code
+/// eval> reduce([1, 2, 3, 4], `+`)
+/// 10
+/// \endcode
+Object reduce(Range c, Function f);
+
+
+/// \brief Takes elements from Container c that match function f, return them.
+///
+/// Example:
+/// \code
+/// eval> filter([1, 2, 3, 4], odd)
+/// [1, 3]
+/// \endcode
+Object filter(Container c, Function f);
+
+
+/// \brief Joins the elements of the Range c into a string, delimiting each with the delim string.
+///
+/// Example:
+/// \code
+/// eval> join([1, 2, 3], "*") 
+/// 1*2*3
+/// \endcode
+string join(Range c, string delim);
+
+
+/// \brief Returns the contents of the Container c in reversed order.
+///
+/// Example:
+/// \code
+/// eval> reverse([1, 2, 3, 4, 5, 6, 7])
+/// [7, 6, 5, 4, 3, 2, 1]
+/// \endcode
+Container reverse(Container c);
+
+
+/// \brief Generates a new Vector filled with values starting at x and ending with y.
+///
+/// Works on types supporting operator<=(x, y) and operator++(x)
+///
+/// Example:
+/// \code
+/// eval> generate_range(1, 10)
+/// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+/// \endcode
+Vector generate_range(Object x, Object y);
+
+
+/// \brief Returns a new Range with x and y concatenated.
+///
+/// Example:
+/// \code
+/// eval> concat([1, 2, 3], [4, 5, 6])
+/// [1, 2, 3, 4, 5, 6]
+/// \endcode
+Object concat(Range x, Range y);
+
+
+/// \brief Returns a new Vector with x and y as its values.
+///
+/// Example:
+/// \code
+/// eval> collate(1, 2)
+/// [1, 2]
+/// \endcode
+Vector collate(Object x, Object y);
+
+
+/// \brief Applies f to elements of x and y, returning a new Vector with the result of each application.
+///
+/// Example:
+/// \code
+/// eval> zip_with(`+`, [1, 2, 3], [4, 5, 6])
+/// [5, 7, 9]
+/// \endcode
+Vector zip_with(Function f, Range x, Range y);
+
+
+/// \brief Collates elements of x and y, returning a new Vector with the result.
+///
+/// Example:
+/// \code
+/// eval> zip([1, 2, 3], [4, 5, 6])
+/// [[1, 4], [2, 5], [3, 6]]
+/// \endcode
+Vector zip(Range x, Range y);
+
+
+/// \brief returns true if there exists a call to the Function f that takes the given parameters
+///
+/// Example:
+/// \code
+/// eval> call_exists(`+`, 1, 2)
+/// true
+/// \endcode
+bool call_exists(Function f, ...);
+
+/// \brief Reverses a Range object so that the elements are accessed in reverse
+Range retro(Range);
+
+/// \brief Reverses a Const_Range object so that the elements are accessed in reverse
+Const_Range retro(Const_Range);
+
+
+/// \brief Raises the given object as an exception. Any type of object can be thrown.
+///
+/// Example:
+/// \code
+/// eval> try { throw(1); } catch (e) { print("Exception caught: " + to_string(e)); }
+/// Exception caught: 1
+/// \endcode
+/// 
+/// \sa \ref keywordtry
+void throw(Object); 
+}
+

+ 46 - 0
chaiscript/language/chaiscript_tracer.hpp

@@ -0,0 +1,46 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_TRACER_HPP_
+#define CHAISCRIPT_TRACER_HPP_
+
+namespace chaiscript {
+  namespace eval {
+
+
+    struct Noop_Tracer_Detail
+    {
+      template<typename T>
+        void trace(const chaiscript::detail::Dispatch_State &, const AST_Node_Impl<T> *)
+        {
+        }
+    };
+
+    template<typename ... T>
+      struct Tracer : T...
+    {
+      Tracer() = default;
+      explicit Tracer(T ... t)
+        : T(std::move(t))...
+      {
+      }
+
+      void do_trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl<Tracer<T...>> *node) {
+        (void)std::initializer_list<int>{ (static_cast<T&>(*this).trace(ds, node), 0)... };
+      }
+
+      static void trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl<Tracer<T...>> *node) {
+        ds->get_parser().get_tracer<Tracer<T...>>().do_trace(ds, node);
+      }
+    };
+
+    typedef Tracer<Noop_Tracer_Detail> Noop_Tracer;
+
+  }
+}
+
+#endif
+

+ 31 - 0
chaiscript/language/chaiscript_unknown.hpp

@@ -0,0 +1,31 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_UNKNOWN_HPP_
+#define CHAISCRIPT_UNKNOWN_HPP_
+
+
+namespace chaiscript
+{
+  namespace detail
+  {
+    struct Loadable_Module
+    {
+      Loadable_Module(const std::string &, const std::string &)
+      {
+#ifdef CHAISCRIPT_NO_DYNLOAD
+        throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
+#else
+        throw chaiscript::exception::load_module_error("Loadable module support not available for your platform");
+#endif
+      }
+
+      ModulePtr m_moduleptr;
+    };
+  }
+}
+#endif
+

+ 133 - 0
chaiscript/language/chaiscript_windows.hpp

@@ -0,0 +1,133 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_WINDOWS_HPP_
+#define CHAISCRIPT_WINDOWS_HPP_
+
+#include <string>
+
+#ifdef CHAISCRIPT_WINDOWS
+#define VC_EXTRA_LEAN
+#if !defined(WIN32_LEAN_AND_MEAN)
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#endif
+
+
+namespace chaiscript
+{
+  namespace detail
+  {
+    struct Loadable_Module
+    {
+      template<typename T>
+        static std::wstring to_wstring(const T &t_str) 
+        {
+          return std::wstring(t_str.begin(), t_str.end());
+        }
+
+      template<typename T>
+        static std::string to_string(const T &t_str)
+        {
+          return std::string(t_str.begin(), t_str.end());
+        }
+
+#if defined(_UNICODE) || defined(UNICODE)
+        template<typename T>
+        static std::wstring to_proper_string(const T &t_str)
+        {
+          return to_wstring(t_str);
+        }
+#else
+      template<typename T>
+        static std::string to_proper_string(const T &t_str)
+        {
+          return to_string(t_str);
+        }
+#endif
+
+      static std::string get_error_message(DWORD t_err)
+      {
+        typedef LPTSTR StringType;
+
+#if defined(_UNICODE) || defined(UNICODE)
+        std::wstring retval = L"Unknown Error";
+#else
+        std::string retval = "Unknown Error";
+#endif
+        StringType lpMsgBuf = nullptr;
+
+        if (FormatMessage(
+            FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+            FORMAT_MESSAGE_FROM_SYSTEM |
+            FORMAT_MESSAGE_IGNORE_INSERTS,
+            nullptr,
+            t_err,
+            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+            reinterpret_cast<StringType>(&lpMsgBuf),
+            0, nullptr ) != 0 && lpMsgBuf)
+        {
+          retval = lpMsgBuf;
+          LocalFree(lpMsgBuf);
+        }
+
+        return to_string(retval);
+      }
+
+      struct DLModule
+      {
+        explicit DLModule(const std::string &t_filename)
+          : m_data(LoadLibrary(to_proper_string(t_filename).c_str()))
+        {
+          if (!m_data)
+          {
+            throw chaiscript::exception::load_module_error(get_error_message(GetLastError()));
+          }
+        }
+
+        DLModule(DLModule &&) = default;
+        DLModule &operator=(DLModule &&) = default;
+        DLModule(const DLModule &) = delete;
+        DLModule &operator=(const DLModule &) = delete;
+
+        ~DLModule()
+        {
+          FreeLibrary(m_data);
+        }
+
+        HMODULE m_data;
+      };
+
+      template<typename T>
+        struct DLSym
+        {
+          DLSym(DLModule &t_mod, const std::string &t_symbol)
+            : m_symbol(reinterpret_cast<T>(GetProcAddress(t_mod.m_data, t_symbol.c_str())))
+          {
+            if (!m_symbol)
+            {
+              throw chaiscript::exception::load_module_error(get_error_message(GetLastError()));
+            }
+          }
+
+          T m_symbol;
+        };
+
+      Loadable_Module(const std::string &t_module_name, const std::string &t_filename)
+        : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name),
+        m_moduleptr(m_func.m_symbol())
+      {
+      }
+
+      DLModule m_dlmodule;
+      DLSym<Create_Module_Func> m_func;
+      ModulePtr m_moduleptr;
+    };
+  }
+}
+#endif 
+

+ 50 - 0
chaiscript/utility/fnv1a.hpp

@@ -0,0 +1,50 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_UTILITY_FNV1A_HPP_
+#define CHAISCRIPT_UTILITY_FNV1A_HPP_
+
+
+#include <cstdint>
+#include "../chaiscript_defines.hpp"
+
+
+namespace chaiscript
+{
+
+
+  namespace utility
+  {
+
+
+    static constexpr std::uint32_t fnv1a_32(const char *s, std::uint32_t h = 0x811c9dc5) {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4307)
+#endif
+      return (*s == 0) ? h : fnv1a_32(s+1, ((h ^ (*s)) * 0x01000193));
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+    }
+
+
+  }
+
+
+}
+
+#endif

+ 674 - 0
chaiscript/utility/json.hpp

@@ -0,0 +1,674 @@
+// From github.com/nbsdx/SimpleJSON. 
+// Released under the DWTFYW PL
+//
+
+
+#pragma once
+
+#ifndef SIMPLEJSON_HPP
+#define SIMPLEJSON_HPP
+
+
+#include <cstdint>
+#include <cmath>
+#include <cctype>
+#include <string>
+#include <vector>
+#include <map>
+#include <type_traits>
+#include <initializer_list>
+#include <ostream>
+#include <iostream>
+#include "../chaiscript_defines.hpp"
+
+namespace json {
+
+using std::enable_if;
+using std::initializer_list;
+using std::is_same;
+using std::is_convertible;
+using std::is_integral;
+using std::is_floating_point;
+
+
+
+
+class JSON
+{
+  public:
+    enum class Class {
+      Null,
+      Object,
+      Array,
+      String,
+      Floating,
+      Integral,
+      Boolean
+    };
+
+  private:
+
+    struct QuickFlatMap
+    {
+      auto find(const std::string &s) {
+        return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; });
+      }
+
+      auto find(const std::string &s) const {
+        return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; });
+      }
+
+      auto size() const {
+        return data.size();
+      }
+
+      auto begin() const {
+        return data.begin();
+      }
+
+      auto end() const {
+        return data.end();
+      }
+
+
+      auto begin() {
+        return data.begin();
+      }
+
+      auto end() {
+        return data.end();
+      }
+
+
+      JSON &operator[](const std::string &s) {
+        const auto itr = find(s);
+        if (itr != data.end()) {
+          return itr->second;
+        } else {
+          data.emplace_back(s, JSON());
+          return data.back().second;
+        }
+      }
+
+      JSON &at(const std::string &s) {
+        const auto itr = find(s);
+        if (itr != data.end()) {
+          return itr->second;
+        } else {
+          throw std::out_of_range("Unknown key: " + s);
+        }
+      }
+
+      const JSON &at(const std::string &s) const {
+        const auto itr = find(s);
+        if (itr != data.end()) {
+          return itr->second;
+        } else {
+          throw std::out_of_range("Unknown key: " + s);
+        }
+      }
+
+      size_t count(const std::string &s) const {
+        return (find(s) != data.end())?1:0;
+      }
+
+      std::vector<std::pair<std::string, JSON>> data;
+
+      using iterator = decltype(data)::iterator;
+      using const_iterator = decltype(data)::const_iterator;
+
+
+    };
+
+    struct Internal {
+      template<typename T>
+        auto clone(const std::unique_ptr<T> &ptr) {
+          if (ptr != nullptr) {
+            return std::make_unique<T>(*ptr);
+          } else {
+            return std::unique_ptr<T>(nullptr);
+          }
+        }
+
+      Internal( double d ) : Float( d ), Type(Class::Floating) {}
+      Internal( long   l ) : Int( l ), Type(Class::Integral) {}
+      Internal( bool   b ) : Bool( b ), Type(Class::Boolean) {}
+      Internal( std::string s ) : String(std::make_unique<std::string>(std::move(s))), Type(Class::String) {}
+      Internal()           : Type(Class::Null) {}
+
+      Internal(Class t_type) {
+        set_type(t_type);
+      }
+
+      Internal(const Internal &other)
+        : List(clone(other.List)),
+        Map(clone(other.Map)),
+        String(clone(other.String)),
+        Float(other.Float),
+        Int(other.Int),
+        Bool(other.Bool),
+        Type(other.Type)
+      {
+      }
+
+      Internal &operator=(const Internal &other)
+      {
+        List = clone(other.List);
+        Map = clone(other.Map);
+        String = clone(other.String);
+        Float = other.Float;
+        Int = other.Int;
+        Bool = other.Bool;
+        Type = other.Type;
+        return *this;
+      }
+
+      void set_type( Class type ) {
+        if( type == Type ) {
+          return;
+        }
+
+        Map.reset();
+        List.reset();
+        String.reset();
+
+        switch( type ) {
+          case Class::Object:    Map    = std::make_unique<QuickFlatMap>(); break;
+          case Class::Array:     List   = std::make_unique<std::vector<JSON>>();      break;
+          case Class::String:    String = std::make_unique<std::string>();           break;
+          case Class::Floating:  Float  = 0.0;                    break;
+          case Class::Integral:  Int    = 0;                      break;
+          case Class::Boolean:   Bool   = false;                  break;
+          case Class::Null:      break;
+        }
+
+        Type = type;
+      }
+
+      Internal(Internal &&) = default;
+      Internal &operator=(Internal &&) = default;
+
+      std::unique_ptr<std::vector<JSON>>      List;
+      std::unique_ptr<QuickFlatMap> Map;
+      std::unique_ptr<std::string>           String;
+      double              Float = 0;
+      long                Int = 0;
+      bool                Bool = false;
+
+      Class Type = Class::Null;
+    };
+
+    Internal internal;
+
+  public:
+
+    template <typename Container>
+      class JSONWrapper {
+        Container *object = nullptr;
+
+        public:
+        JSONWrapper( Container *val ) : object( val ) {}
+        JSONWrapper( std::nullptr_t ) {}
+
+        typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); }
+        typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); }
+        typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); }
+        typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); }
+      };
+
+    template <typename Container>
+      class JSONConstWrapper {
+        const Container *object = nullptr;
+
+        public:
+        JSONConstWrapper( const Container *val ) : object( val ) {}
+        JSONConstWrapper( std::nullptr_t ) {}
+
+        typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); }
+        typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); }
+      };
+
+    JSON() = default;
+    JSON( std::nullptr_t ) {} 
+
+    explicit JSON(Class type)
+      : internal(type)
+    {
+    }
+
+    JSON( initializer_list<JSON> list ) 
+      : internal(Class::Object)
+    {
+      for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) {
+        operator[]( i->to_string() ) = *std::next( i );
+      }
+    }
+
+    template <typename T>
+      explicit JSON( T b, typename enable_if<is_same<T,bool>::value>::type* = nullptr ) : internal( static_cast<bool>(b) ) {}
+
+    template <typename T>
+      explicit JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = nullptr ) : internal( static_cast<long>(i) ) {}
+
+    template <typename T>
+      explicit JSON( T f, typename enable_if<is_floating_point<T>::value>::type* = nullptr ) : internal( static_cast<double>(f) ) {}
+
+    template <typename T>
+      explicit JSON( T s, typename enable_if<is_convertible<T,std::string>::value>::type* = nullptr ) : internal( static_cast<std::string>(s) ) {}
+
+
+
+    static JSON Load( const std::string & );
+
+    JSON& operator[]( const std::string &key ) {
+      internal.set_type( Class::Object ); 
+      return internal.Map->operator[]( key );
+    }
+
+    JSON& operator[]( const size_t index ) {
+      internal.set_type( Class::Array );
+      if( index >= internal.List->size() ) {
+        internal.List->resize( index + 1 );
+      }
+
+      return internal.List->operator[]( index );
+    }
+
+
+    JSON &at( const std::string &key ) {
+      return operator[]( key );
+    }
+
+    const JSON &at( const std::string &key ) const {
+      return internal.Map->at( key );
+    }
+
+    JSON &at( size_t index ) {
+      return operator[]( index );
+    }
+
+    const JSON &at( size_t index ) const {
+      return internal.List->at( index );
+    }
+
+
+    long length() const {
+      if( internal.Type == Class::Array ) {
+        return static_cast<long>(internal.List->size());
+      } else {
+        return -1;
+      }
+    }
+
+    bool has_key( const std::string &key ) const {
+      if( internal.Type == Class::Object ) {
+        return internal.Map->count(key) != 0;
+      }
+
+      return false;
+    }
+
+    int size() const {
+      if( internal.Type == Class::Object ) {
+        return static_cast<int>(internal.Map->size());
+      } else if( internal.Type == Class::Array ) {
+        return static_cast<int>(internal.List->size());
+      } else {
+        return -1;
+      }
+    }
+
+    Class JSONType() const { return internal.Type; }
+
+    /// Functions for getting primitives from the JSON object.
+    bool is_null() const { return internal.Type == Class::Null; }
+
+    std::string to_string() const { bool b; return to_string( b ); }
+    std::string to_string( bool &ok ) const {
+      ok = (internal.Type == Class::String);
+      return ok ? *internal.String : std::string("");
+    }
+
+    double to_float() const { bool b; return to_float( b ); }
+    double to_float( bool &ok ) const {
+      ok = (internal.Type == Class::Floating);
+      return ok ? internal.Float : 0.0;
+    }
+
+    long to_int() const { bool b; return to_int( b ); }
+    long to_int( bool &ok ) const {
+      ok = (internal.Type == Class::Integral);
+      return ok ? internal.Int : 0;
+    }
+
+    bool to_bool() const { bool b; return to_bool( b ); }
+    bool to_bool( bool &ok ) const {
+      ok = (internal.Type == Class::Boolean);
+      return ok ? internal.Bool : false;
+    }
+
+    JSONWrapper<QuickFlatMap> object_range() {
+      if( internal.Type == Class::Object ) {
+        return JSONWrapper<QuickFlatMap>( internal.Map.get() );
+      } else {
+        return JSONWrapper<QuickFlatMap>( nullptr );
+      }
+    }
+
+    JSONWrapper<std::vector<JSON>> array_range() {
+      if( internal.Type == Class::Array ) {
+        return JSONWrapper<std::vector<JSON>>( internal.List.get() );
+      } else {
+        return JSONWrapper<std::vector<JSON>>( nullptr );
+      }
+    }
+
+    JSONConstWrapper<QuickFlatMap> object_range() const {
+      if( internal.Type == Class::Object ) {
+        return JSONConstWrapper<QuickFlatMap>( internal.Map.get() );
+      } else {
+        return JSONConstWrapper<QuickFlatMap>( nullptr );
+      }
+    }
+
+
+    JSONConstWrapper<std::vector<JSON>> array_range() const { 
+      if( internal.Type == Class::Array ) {
+        return JSONConstWrapper<std::vector<JSON>>( internal.List.get() );
+      } else {
+        return JSONConstWrapper<std::vector<JSON>>( nullptr );
+      }
+    }
+
+    std::string dump( long depth = 1, std::string tab = "  ") const {
+      switch( internal.Type ) {
+        case Class::Null:
+          return "null";
+        case Class::Object: {
+                              std::string pad = "";
+                              for( long i = 0; i < depth; ++i, pad += tab ) { }
+
+                              std::string s = "{\n";
+                              bool skip = true;
+                              for( auto &p : *internal.Map ) {
+                                if( !skip ) { s += ",\n"; }
+                                s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) );
+                                skip = false;
+                              }
+                              s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ;
+                              return s;
+                            }
+        case Class::Array: {
+                             std::string s = "[";
+                             bool skip = true;
+                             for( auto &p : *internal.List ) {
+                               if( !skip ) { s += ", "; }
+                               s += p.dump( depth + 1, tab );
+                               skip = false;
+                             }
+                             s += "]";
+                             return s;
+                           }
+        case Class::String:
+                           return "\"" + json_escape( *internal.String ) + "\"";
+        case Class::Floating:
+                           return std::to_string( internal.Float );
+        case Class::Integral:
+                           return std::to_string( internal.Int );
+        case Class::Boolean:
+                           return internal.Bool ? "true" : "false";
+      }
+
+      throw std::runtime_error("Unhandled JSON type");
+    }
+
+
+  private:
+    static std::string json_escape( const std::string &str ) {
+      std::string output;
+      for(char i : str) {
+        switch( i ) {
+          case '\"': output += "\\\""; break;
+          case '\\': output += "\\\\"; break;
+          case '\b': output += "\\b";  break;
+          case '\f': output += "\\f";  break;
+          case '\n': output += "\\n";  break;
+          case '\r': output += "\\r";  break;
+          case '\t': output += "\\t";  break;
+          default  : output += i; break;
+        }
+}
+      return output;
+    }
+
+
+  private:
+};
+
+
+struct JSONParser {
+  static bool isspace(const char c)
+  {
+#ifdef CHAISCRIPT_MSVC
+    // MSVC warns on these line in some circumstances
+#pragma warning(push)
+#pragma warning(disable : 6330)
+#endif
+    return ::isspace(c) != 0;
+#ifdef CHAISCRIPT_MSVC
+#pragma warning(pop)
+#endif
+
+
+  }
+
+  static void consume_ws( const std::string &str, size_t &offset ) {
+    while( isspace( str.at(offset) ) && offset <= str.size() ) { ++offset; }
+  }
+
+  static JSON parse_object( const std::string &str, size_t &offset ) {
+    JSON Object( JSON::Class::Object );
+
+    ++offset;
+    consume_ws( str, offset );
+    if( str.at(offset) == '}' ) {
+      ++offset; return Object;
+    }
+
+    for (;offset<str.size();) {
+      JSON Key = parse_next( str, offset );
+      consume_ws( str, offset );
+      if( str.at(offset) != ':' ) {
+        throw std::runtime_error(std::string("JSON ERROR: Object: Expected colon, found '") + str.at(offset) + "'\n");
+      }
+      consume_ws( str, ++offset );
+      JSON Value = parse_next( str, offset );
+      Object[Key.to_string()] = Value;
+
+      consume_ws( str, offset );
+      if( str.at(offset) == ',' ) {
+        ++offset; continue;
+      }
+      else if( str.at(offset) == '}' ) {
+        ++offset; break;
+      }
+      else {
+        throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str.at(offset) + "'\n");
+      }
+    }
+
+    return Object;
+  }
+
+  static JSON parse_array( const std::string &str, size_t &offset ) {
+    JSON Array( JSON::Class::Array );
+    size_t index = 0;
+
+    ++offset;
+    consume_ws( str, offset );
+    if( str.at(offset) == ']' ) {
+      ++offset; return Array;
+    }
+
+    for (;offset < str.size();) {
+      Array[index++] = parse_next( str, offset );
+      consume_ws( str, offset );
+
+      if( str.at(offset) == ',' ) {
+        ++offset; continue;
+      }
+      else if( str.at(offset) == ']' ) {
+        ++offset; break;
+      }
+      else {
+        throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str.at(offset) + "'\n");
+      }
+    }
+
+    return Array;
+  }
+
+  static JSON parse_string( const std::string &str, size_t &offset ) {
+    std::string val;
+    for( char c = str.at(++offset); c != '\"' ; c = str.at(++offset) ) {
+      if( c == '\\' ) {
+        switch( str.at(++offset) ) {
+          case '\"': val += '\"'; break;
+          case '\\': val += '\\'; break;
+          case '/' : val += '/' ; break;
+          case 'b' : val += '\b'; break;
+          case 'f' : val += '\f'; break;
+          case 'n' : val += '\n'; break;
+          case 'r' : val += '\r'; break;
+          case 't' : val += '\t'; break;
+          case 'u' : {
+                       val += "\\u" ;
+                       for( size_t i = 1; i <= 4; ++i ) {
+                         c = str.at(offset+i);
+                         if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) {
+                           val += c;
+                         } else {
+                           throw std::runtime_error(std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'");
+                         }
+                       }
+                       offset += 4;
+                     } break;
+          default  : val += '\\'; break;
+        }
+      } else {
+        val += c;
+      }
+    }
+    ++offset;
+    return JSON(val);
+  }
+
+  static JSON parse_number( const std::string &str, size_t &offset ) {
+    std::string val, exp_str;
+    char c = '\0';
+    bool isDouble = false;
+    bool isNegative = false;
+    long exp = 0;
+    if( offset < str.size() && str.at(offset) == '-' ) {
+      isNegative = true;
+      ++offset;
+    }
+    for (; offset < str.size() ;) {
+      c = str.at(offset++);
+      if( c >= '0' && c <= '9' ) {
+        val += c;
+      } else if( c == '.' && !isDouble ) {
+        val += c; 
+        isDouble = true;
+      } else {
+        break;
+      }
+    }
+    if( offset < str.size() && (c == 'E' || c == 'e' )) {
+      c = str.at(offset++);
+      if( c == '-' ) { 
+        exp_str += '-';
+      } else if( c == '+' ) {
+        // do nothing
+      } else { 
+        --offset; 
+      }
+
+      for (; offset < str.size() ;) {
+        c = str.at(offset++);
+        if( c >= '0' && c <= '9' ) {
+          exp_str += c;
+        } else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
+          throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'");
+        }
+        else {
+          break;
+}
+      }
+      exp = chaiscript::parse_num<long>( exp_str );
+    }
+    else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) {
+      throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'");
+    }
+    --offset;
+
+    if( isDouble ) {
+      return JSON((isNegative?-1:1) * chaiscript::parse_num<double>( val ) * std::pow( 10, exp ));
+    } else {
+      if( !exp_str.empty() ) {
+        return JSON((isNegative?-1:1) * static_cast<double>(chaiscript::parse_num<long>( val )) * std::pow( 10, exp ));
+      } else {
+        return JSON((isNegative?-1:1) * chaiscript::parse_num<long>( val ));
+      }
+    }
+  }
+
+  static JSON parse_bool( const std::string &str, size_t &offset ) {
+    if( str.substr( offset, 4 ) == "true" ) {
+      offset += 4;
+      return JSON(true);
+    } else if( str.substr( offset, 5 ) == "false" ) {
+      offset += 5;
+      return JSON(false);
+    } else {
+      throw std::runtime_error(std::string("JSON ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'");
+    }
+  }
+
+  static JSON parse_null( const std::string &str, size_t &offset ) {
+    if( str.substr( offset, 4 ) != "null" ) {
+      throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'");
+    }
+    offset += 4;
+    return JSON();
+  }
+
+  static JSON parse_next( const std::string &str, size_t &offset ) {
+    char value;
+    consume_ws( str, offset );
+    value = str.at(offset);
+    switch( value ) {
+      case '[' : return parse_array( str, offset );
+      case '{' : return parse_object( str, offset );
+      case '\"': return parse_string( str, offset );
+      case 't' :
+      case 'f' : return parse_bool( str, offset );
+      case 'n' : return parse_null( str, offset );
+      default  : if( ( value <= '9' && value >= '0' ) || value == '-' ) {
+                   return parse_number( str, offset );
+                 }
+    }
+    throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'");
+  }
+
+};
+
+inline JSON JSON::Load( const std::string &str ) {
+  size_t offset = 0;
+  return JSONParser::parse_next( str, offset );
+}
+
+} // End Namespace json
+
+
+#endif 

+ 154 - 0
chaiscript/utility/json_wrap.hpp

@@ -0,0 +1,154 @@
+#ifndef CHAISCRIPT_SIMPLEJSON_WRAP_HPP
+#define CHAISCRIPT_SIMPLEJSON_WRAP_HPP
+
+#include "json.hpp"
+
+namespace chaiscript
+{
+  class json_wrap
+  {
+    public:
+
+      static Module& library(Module& m)
+      {
+
+        m.add(chaiscript::fun([](const std::string &t_str) { return from_json(t_str); }), "from_json");
+        m.add(chaiscript::fun(&json_wrap::to_json), "to_json");
+
+        return m;
+
+      }
+
+    private:
+
+      static Boxed_Value from_json(const json::JSON &t_json)
+      {
+        switch( t_json.JSONType() ) {
+          case json::JSON::Class::Null:
+            return Boxed_Value();
+          case json::JSON::Class::Object:
+            {
+              std::map<std::string, Boxed_Value> m;
+
+              for (const auto &p : t_json.object_range())
+              {
+                m.insert(std::make_pair(p.first, from_json(p.second)));
+              }
+
+              return Boxed_Value(m);
+            }
+          case json::JSON::Class::Array:
+            {
+              std::vector<Boxed_Value> vec;
+
+              for (const auto &p : t_json.array_range()) 
+              {
+                vec.emplace_back(from_json(p));
+              }
+
+              return Boxed_Value(vec);
+            }
+          case json::JSON::Class::String:
+            return Boxed_Value(t_json.to_string());
+          case json::JSON::Class::Floating:
+            return Boxed_Value(t_json.to_float());
+          case json::JSON::Class::Integral:
+            return Boxed_Value(t_json.to_int());
+          case json::JSON::Class::Boolean:
+            return Boxed_Value(t_json.to_bool());
+        }
+
+        throw std::runtime_error("Unknown JSON type");
+      }
+
+      static Boxed_Value from_json(const std::string &t_json)
+      {
+        try {
+          return from_json( json::JSON::Load(t_json) );
+        } catch (const std::out_of_range& ) {
+          throw std::runtime_error("Unparsed JSON input");
+        }
+      }
+
+      static std::string to_json(const Boxed_Value &t_bv)
+      {
+        return to_json_object(t_bv).dump();
+      }
+
+      static json::JSON to_json_object(const Boxed_Value &t_bv)
+      {
+        try {
+          const std::map<std::string, Boxed_Value> m = chaiscript::boxed_cast<const std::map<std::string, Boxed_Value> &>(t_bv);
+
+          json::JSON obj;
+          for (const auto &o : m)
+          {
+            obj[o.first] = to_json_object(o.second);
+          }
+          return obj;
+        } catch (const chaiscript::exception::bad_boxed_cast &) {
+          // not a map
+        }
+
+        try {
+          const std::vector<Boxed_Value> v = chaiscript::boxed_cast<const std::vector<Boxed_Value> &>(t_bv);
+
+          json::JSON obj;
+          for (size_t i = 0; i < v.size(); ++i)
+          {
+            obj[i] = to_json_object(v[i]);
+          }
+          return obj;
+        } catch (const chaiscript::exception::bad_boxed_cast &) {
+          // not a vector
+        }
+
+
+        try {
+          Boxed_Number bn(t_bv);
+          if (Boxed_Number::is_floating_point(t_bv))
+          {
+            return json::JSON(bn.get_as<double>());
+          } else {
+            return json::JSON(bn.get_as<long>());
+          }
+        } catch (const chaiscript::detail::exception::bad_any_cast &) {
+          // not a number
+        }
+
+        try {
+          return json::JSON(boxed_cast<bool>(t_bv));
+        } catch (const chaiscript::exception::bad_boxed_cast &) {
+          // not a bool
+        }
+
+        try {
+          return json::JSON(boxed_cast<std::string>(t_bv));
+        } catch (const chaiscript::exception::bad_boxed_cast &) {
+          // not a string
+        }
+
+
+        try {
+          const chaiscript::dispatch::Dynamic_Object &o = boxed_cast<const dispatch::Dynamic_Object &>(t_bv);
+
+          json::JSON obj;
+          for (const auto &attr : o.get_attrs())
+          {
+            obj[attr.first] = to_json_object(attr.second);
+          }
+          return obj;
+        } catch (const chaiscript::exception::bad_boxed_cast &) {
+          // not a dynamic object
+        }
+
+        throw std::runtime_error("Unknown object type to convert to JSON");
+      }
+
+
+  };
+
+
+}
+
+#endif

+ 37 - 0
chaiscript/utility/static_string.hpp

@@ -0,0 +1,37 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+#ifndef CHAISCRIPT_UTILITY_STATIC_STRING_HPP_
+#define CHAISCRIPT_UTILITY_STATIC_STRING_HPP_
+
+namespace chaiscript
+{
+  namespace utility
+  {
+
+    struct Static_String
+    {
+      template<size_t N>
+        constexpr Static_String(const char (&str)[N])
+        : m_size(N-1), data(&str[0])
+        {
+        }
+
+      constexpr size_t size() const {
+        return m_size;
+      }
+
+      constexpr const char *c_str() const {
+        return data;
+      }
+
+      const size_t m_size;
+      const char *data = nullptr;
+    };
+  }
+}
+
+#endif

+ 123 - 0
chaiscript/utility/utility.hpp

@@ -0,0 +1,123 @@
+// This file is distributed under the BSD License.
+// See "license.txt" for details.
+// Copyright 2009-2012, Jonathan Turner ([email protected])
+// Copyright 2009-2017, Jason Turner ([email protected])
+// http://www.chaiscript.com
+
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+
+#ifndef CHAISCRIPT_UTILITY_UTILITY_HPP_
+#define CHAISCRIPT_UTILITY_UTILITY_HPP_
+
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "../language/chaiscript_common.hpp"
+#include "../dispatchkit/register_function.hpp"
+#include "../dispatchkit/operators.hpp"
+
+
+namespace chaiscript 
+{
+  namespace utility
+  {
+
+    /// Single step command for registering a class with ChaiScript
+    /// 
+    /// \param[in,out] t_module Model to add class to
+    /// \param[in] t_class_name Name of the class being registered
+    /// \param[in] t_constructors Vector of constructors to add
+    /// \param[in] t_funcs Vector of methods to add
+    ///
+    /// \example Adding a basic class to ChaiScript in one step
+    /// 
+    /// \code
+    /// chaiscript::utility::add_class<test>(*m,
+    ///      "test",
+    ///      { constructor<test ()>(),
+    ///        constructor<test (const test &)>() },
+    ///      { {fun(&test::function), "function"},
+    ///        {fun(&test::function2), "function2"},
+    ///        {fun(&test::function3), "function3"},
+    ///        {fun(static_cast<std::string(test::*)(double)>(&test::function_overload)), "function_overload" },
+    ///        {fun(static_cast<std::string(test::*)(int)>(&test::function_overload)), "function_overload" },
+    ///        {fun(static_cast<test & (test::*)(const test &)>(&test::operator=)), "=" }
+    ///        }
+    ///      );
+    /// 
+    template<typename Class, typename ModuleType>
+      void add_class(ModuleType &t_module,
+          const std::string &t_class_name,
+          const std::vector<chaiscript::Proxy_Function> &t_constructors,
+          const std::vector<std::pair<chaiscript::Proxy_Function, std::string>> &t_funcs)
+      {
+        t_module.add(chaiscript::user_type<Class>(), t_class_name); 
+
+        for(const chaiscript::Proxy_Function &ctor: t_constructors)
+        {
+          t_module.add(ctor, t_class_name);
+        }
+
+        for(const auto &fun: t_funcs)
+        {
+          t_module.add(fun.first, fun.second);
+        }
+      }
+
+    template<typename Enum, typename ModuleType>
+      typename std::enable_if<std::is_enum<Enum>::value, void>::type
+      add_class(ModuleType &t_module,
+        const std::string &t_class_name,
+        const std::vector<std::pair<typename std::underlying_type<Enum>::type, std::string>> &t_constants
+        )
+      {
+        t_module.add(chaiscript::user_type<Enum>(), t_class_name);
+
+        t_module.add(chaiscript::constructor<Enum ()>(), t_class_name);
+        t_module.add(chaiscript::constructor<Enum (const Enum &)>(), t_class_name);
+
+        using namespace chaiscript::bootstrap::operators;
+        equal<Enum>(t_module);
+        not_equal<Enum>(t_module);
+        assign<Enum>(t_module);
+
+        t_module.add(chaiscript::fun([](const Enum &e, const int &i) { return e == i; }), "==");
+        t_module.add(chaiscript::fun([](const int &i, const Enum &e) { return i == e; }), "==");
+
+        for (const auto &constant : t_constants)
+        {
+          t_module.add_global_const(chaiscript::const_var(Enum(constant.first)), constant.second);
+        }
+      }
+
+    template<typename EnumClass, typename ModuleType>
+      typename std::enable_if<std::is_enum<EnumClass>::value, void>::type
+      add_class(ModuleType &t_module,
+        const std::string &t_class_name,
+        const std::vector<std::pair<EnumClass, std::string>> &t_constants
+        )
+      {
+        t_module.add(chaiscript::user_type<EnumClass>(), t_class_name);
+
+        t_module.add(chaiscript::constructor<EnumClass()>(), t_class_name);
+        t_module.add(chaiscript::constructor<EnumClass(const EnumClass &)>(), t_class_name);
+
+        using namespace chaiscript::bootstrap::operators;
+        equal<EnumClass>(t_module);
+        not_equal<EnumClass>(t_module);
+        assign<EnumClass>(t_module);
+
+        for (const auto &constant : t_constants)
+        {
+          t_module.add_global_const(chaiscript::const_var(EnumClass(constant.first)), constant.second);
+        }
+      }
+  }
+}
+
+#endif
+