├── .gitignore ├── CMakeLists.txt ├── README.md ├── contributor_code_of_conduct.md ├── croc.cpp ├── crocgrammar.txt ├── croctest.cpp ├── docs ├── Doxyfile ├── DoxygenLayout.xml ├── crocstyle.css ├── footer.html └── header.html ├── downloads ├── Croc.chl ├── croc.lang ├── croc.vim └── croc.xml ├── libpcre.dll ├── samples ├── autoctor.croc ├── glfwtest.croc ├── imguitest.croc ├── interfaces.croc ├── nettest.croc ├── particles.croc ├── sdltest.croc ├── sieve.croc ├── speed.croc ├── speed.py ├── threadtest.croc └── tinyes.croc ├── src ├── CMakeLists.txt └── croc │ ├── README.md │ ├── addons │ ├── al.cpp │ ├── al.hpp │ ├── all.hpp │ ├── gl.cpp │ ├── gl.hpp │ ├── glfw.cpp │ ├── glfw.hpp │ ├── il.cpp │ ├── il.hpp │ ├── imgui.cpp │ └── pcre.cpp │ ├── api.h │ ├── api │ ├── apichecks.hpp │ ├── array.cpp │ ├── basic.cpp │ ├── calls.cpp │ ├── class.cpp │ ├── compiler.cpp │ ├── debug.cpp │ ├── eh.cpp │ ├── funcdef.cpp │ ├── function.cpp │ ├── gc.cpp │ ├── mem.cpp │ ├── memblock.cpp │ ├── namespace.cpp │ ├── nativeref.cpp │ ├── reflection.cpp │ ├── stack.cpp │ ├── table.cpp │ ├── thread.cpp │ ├── typequeries.cpp │ ├── valuetypes.cpp │ ├── variables.cpp │ ├── vm.cpp │ └── weakref.cpp │ ├── apiex.h │ ├── apifuncs.h │ ├── apitypes.h │ ├── base │ ├── darray.hpp │ ├── deque.cpp │ ├── deque.hpp │ ├── gc.cpp │ ├── gc.hpp │ ├── gcobject.hpp │ ├── hash.hpp │ ├── leakdetector.cpp │ ├── leakdetector.hpp │ ├── memory.cpp │ ├── memory.hpp │ ├── metamethods.cpp │ ├── metamethods.hpp │ ├── opcodes.cpp │ ├── opcodes.hpp │ ├── sanity.hpp │ ├── writebarrier.cpp │ └── writebarrier.hpp │ ├── compiler │ ├── ast.cpp │ ├── ast.hpp │ ├── astvisitor.cpp │ ├── astvisitor.hpp │ ├── builder.cpp │ ├── builder.hpp │ ├── codegen.cpp │ ├── codegen.hpp │ ├── docgen.cpp │ ├── docgen.hpp │ ├── docparser.cpp │ ├── docparser.hpp │ ├── lexer.cpp │ ├── lexer.hpp │ ├── parser.cpp │ ├── parser.hpp │ ├── semantic.cpp │ ├── semantic.hpp │ ├── types.cpp │ └── types.hpp │ ├── ex │ ├── common.cpp │ ├── compilation.cpp │ ├── docs.cpp │ ├── imports.cpp │ ├── library.cpp │ ├── paramchecks.cpp │ └── strbuffer.cpp │ ├── ext │ ├── KHR │ │ └── khrplatform.hpp │ ├── convert.cpp │ ├── glad.cpp │ ├── glad │ │ └── glad.hpp │ ├── imgui │ │ ├── CMakeLists.txt │ │ ├── imconfig.h │ │ ├── imgui.cpp │ │ ├── imgui.cpp.bak │ │ ├── imgui.h │ │ ├── imgui.h.bak │ │ ├── imgui_user.inl │ │ └── stb_textedit.h │ ├── jhash.cpp │ └── jhash.hpp │ ├── internal │ ├── basic.cpp │ ├── basic.hpp │ ├── calls.cpp │ ├── calls.hpp │ ├── class.cpp │ ├── class.hpp │ ├── debug.cpp │ ├── debug.hpp │ ├── eh.cpp │ ├── eh.hpp │ ├── gc.cpp │ ├── gc.hpp │ ├── interpreter.cpp │ ├── interpreter.hpp │ ├── stack.cpp │ ├── stack.hpp │ ├── thread.cpp │ ├── thread.hpp │ ├── variables.cpp │ └── variables.hpp │ ├── mainpage.dox │ ├── stdlib │ ├── all.hpp │ ├── array.cpp │ ├── ascii.cpp │ ├── compiler.cpp │ ├── console.cpp │ ├── console.croc │ ├── debug.cpp │ ├── docs.cpp │ ├── docs.croc │ ├── doctools.cpp │ ├── doctools_console.croc │ ├── doctools_output.croc │ ├── doctools_trac.croc │ ├── env.cpp │ ├── exceptions.cpp │ ├── file.cpp │ ├── gc.cpp │ ├── hash.cpp │ ├── hash_weaktables.croc │ ├── helpers │ │ ├── format.cpp │ │ ├── format.hpp │ │ ├── json.cpp │ │ ├── json.hpp │ │ ├── oscompat.cpp │ │ ├── oscompat.hpp │ │ ├── register.cpp │ │ └── register.hpp │ ├── json.cpp │ ├── math.cpp │ ├── memblock.cpp │ ├── misc.cpp │ ├── misc_vector.cpp │ ├── modules.cpp │ ├── modules.croc │ ├── object.cpp │ ├── os.cpp │ ├── path.cpp │ ├── repl.cpp │ ├── repl.croc │ ├── serialization.cpp │ ├── serialization.croc │ ├── stream.cpp │ ├── stream.croc │ ├── string.cpp │ ├── string_stringbuffer.cpp │ ├── text.cpp │ ├── text.croc │ ├── thread.cpp │ └── time.cpp │ ├── types │ ├── array.cpp │ ├── base.cpp │ ├── base.hpp │ ├── class.cpp │ ├── funcdef.cpp │ ├── function.cpp │ ├── instance.cpp │ ├── memblock.cpp │ ├── namespace.cpp │ ├── string.cpp │ ├── table.cpp │ ├── thread.cpp │ └── weakref.cpp │ └── util │ ├── array.hpp │ ├── misc.cpp │ ├── misc.hpp │ ├── rng.cpp │ ├── rng.hpp │ ├── str.cpp │ ├── str.hpp │ ├── utf.cpp │ └── utf.hpp ├── tests ├── codegen.croc └── harness.croc └── tools ├── depgrapher.croc ├── stdlibdocs.croc └── wikiconv.croc /.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | !libpcre.dll 3 | *.exe 4 | *.sublime-workspace 5 | *.sublime-project 6 | docs/html/ 7 | commitmsg.txt 8 | test.croc -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(croctest CXX) 4 | 5 | include_directories("${PROJECT_SOURCE_DIR}/src") 6 | add_subdirectory(src) 7 | 8 | add_executable(croctest croctest.cpp) 9 | target_link_libraries(croctest croc) 10 | 11 | add_executable(croci croc.cpp) 12 | target_link_libraries(croci croc) 13 | set_property(TARGET croci PROPERTY OUTPUT_NAME "croc") 14 | 15 | if(MINGW) 16 | # for some reason, linking both croc and croctest simultaneously on MinGW is INCREDIBLY slow, causing the ld 17 | # processes to eat two cores for ~10 seconds. So let's force them to link serially :P 18 | add_dependencies(croci croctest) 19 | endif() 20 | 21 | if(NOT DEFINED CROC_BUILD_BITS) 22 | set(CROC_BUILD_BITS 32) 23 | endif() 24 | 25 | if(CMAKE_COMPILER_IS_GNUCXX) 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pedantic-errors -Wall -Wextra -Werror -fno-exceptions") 27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=2 -m${CROC_BUILD_BITS}") 28 | 29 | if(CROC_BUILD_SHARED AND WIN32) 30 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCROCAPI=__declspec(dllimport)") 31 | else() 32 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCROCAPI=extern") 33 | endif() 34 | 35 | if(CROC_BUILD_BITS EQUAL 32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) 36 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xlinker \"-melf_i386\" -Xlinker \"--oformat=elf32-i386\"") 37 | endif() 38 | 39 | if(MINGW) 40 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pedantic-ms-format") 41 | endif() 42 | 43 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti -O3") 44 | elseif(MSVC) 45 | message(FATAL_ERROR "VC not supported yet") 46 | else() 47 | message(FATAL_ERROR "Dunno what compiler you have but I don't support it") 48 | endif() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Croc :crocodile: Programming Language 2 | ============================= 3 | 4 | What's Croc? 5 | ------------ 6 | 7 | Croc is a small, dynamically-typed language most closely related to Lua, with C-style syntax. Its semantics are borrowed mainly from Lua, D, Squirrel, and Io, 8 | though many other languages served as inspirations. 9 | 10 | # It's not developed anymore. No one ever used it. But I still love it :pensive: 11 | -------------------------------------------------------------------------------- /contributor_code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | ## Version 0.3a 3 | 4 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 5 | 6 | If any participant in this project has issues or takes exception with a contribution, they are obligated to provide constructive feedback and never resort to personal attacks, trolling, public or private harrassment, insults, or other unprofessional conduct. 7 | 8 | We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, ability or disability, ethnicity, religion, or level of experience. 9 | -------------------------------------------------------------------------------- /croctest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "croc/api.h" 6 | 7 | void _try(CrocThread* t, const char* name, CrocNativeFunc f, std::function _catch) 8 | { 9 | croc_function_new(t, name, 0, f, 0); 10 | croc_pushNull(t); 11 | if(croc_tryCall(t, -2, 0) < 0) 12 | _catch(); 13 | } 14 | 15 | int main() 16 | { 17 | auto t = croc_vm_openDefault(); 18 | croc_vm_loadUnsafeLibs(t, CrocUnsafeLib_ReallyAll); 19 | croc_vm_loadAllAvailableAddons(t); 20 | 21 | _try(t, "
", [](CrocThread* t) -> word_t 22 | { 23 | croc_compiler_setFlags(t, CrocCompilerFlags_AllDocs); 24 | 25 | croc_pushGlobal(t, "modules"); 26 | croc_field(t, -1, "path"); 27 | croc_pushString(t, ";.."); 28 | croc_cat(t, 2); 29 | croc_fielda(t, -2, "path"); 30 | croc_popTop(t); 31 | 32 | croc_ex_runModule(t, "test", 0); 33 | return 0; 34 | }, 35 | [&]{ 36 | if(croc_ex_isHaltException(t, -1)) 37 | printf("\n------------ Thread halted. ------------\n"); 38 | else 39 | { 40 | croc_pushNull(t); 41 | croc_debug_setHookFunc(t, 0, 0); 42 | 43 | printf("\n------------ ERROR ------------\n"); 44 | croc_pushToString(t, -1); 45 | printf("%s\n", croc_getString(t, -1)); 46 | croc_popTop(t); 47 | 48 | croc_dupTop(t); 49 | croc_pushNull(t); 50 | croc_methodCall(t, -2, "tracebackString", 1); 51 | printf("%s\n", croc_getString(t, -1)); 52 | 53 | croc_pop(t, 2); 54 | } 55 | }); 56 | 57 | fflush(stdout); 58 | croc_vm_close(t); 59 | return 0; 60 | } -------------------------------------------------------------------------------- /docs/crocstyle.css: -------------------------------------------------------------------------------- 1 | body, table, div, dl, tr 2 | { 3 | margin: 0px; 4 | padding: 0px; 5 | } 6 | 7 | p 8 | { 9 | margin-top: 0px; 10 | margin-bottom: 4px; 11 | padding: 0px; 12 | } 13 | 14 | body 15 | { 16 | font-family: Arial,Verdana,'Bitstream Vera Sans',Helvetica,sans-serif; 17 | font-size: 13px; 18 | color: #000; 19 | width: 1018px; 20 | margin-left: auto; 21 | margin-right: auto; 22 | background-color: #656565; 23 | background-image: url('http://jfbillingsley.com/croc/chrome/site/backs.gif'); 24 | background-repeat: repeat-x; 25 | } 26 | 27 | h1, h2, h3, h4, h5, h6 28 | { 29 | margin-bottom: 2px; 30 | } 31 | 32 | h2.groupheader 33 | { 34 | border-bottom-color: black; 35 | color: black; 36 | } 37 | 38 | hr 39 | { 40 | border-color: black; 41 | } 42 | 43 | div.header 44 | { 45 | background-image: none; 46 | background-color: #E5E0CB; 47 | border-bottom-color: black; 48 | } 49 | 50 | #navrow4 51 | { 52 | border-bottom: 1px solid black; 53 | } 54 | 55 | .mdescLeft, .mdescRight, 56 | .memItemLeft, .memItemRight, 57 | .memTemplItemLeft, .memTemplItemRight, .memTemplParams 58 | { 59 | background-color: #E5E0CB; 60 | } 61 | 62 | /* Style used for all hyperlinks. */ 63 | .contents a:visited 64 | { 65 | color: #6e9835; 66 | } 67 | 68 | :link, *:visited 69 | { 70 | color: #6e9835; 71 | font-weight: bold; 72 | border-bottom: 1px dotted #bbb; 73 | text-decoration: none; 74 | } 75 | 76 | :link:hover, :visited:hover 77 | { 78 | background-color: #eee; 79 | color: #555 80 | } 81 | 82 | a.code, a.code:visited, a.line, a.line:visited { color: #6e9835; border-bottom: none; text-decoration: underline; } 83 | a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { color: #6e9835; border-bottom: none; text-decoration: underline; } 84 | 85 | .memname tr 86 | { 87 | float: left; 88 | } 89 | 90 | table.memname td 91 | { 92 | padding: 0px; 93 | } 94 | 95 | div.memproto 96 | { 97 | padding: 0px; 98 | } 99 | 100 | div.fragment 101 | { 102 | font-family: 'Consolas', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 103 | border: dotted 1px #9c9; 104 | background-color: #282828; 105 | color: white; 106 | padding: 4px 4px 4px 4px; 107 | } 108 | 109 | div.line 110 | { 111 | font-family: 'Consolas', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 112 | line-height: 105%; 113 | } 114 | 115 | span.keyword { color: #FF3333 } 116 | span.keywordtype { color: #FF3333 } 117 | span.keywordflow { color: #FF3333 } 118 | span.comment { color: #00A000 } 119 | span.preprocessor { color: #FF3333 } 120 | span.stringliteral { color: #FF7F00 } 121 | span.charliteral { color: #FF7F00 } 122 | 123 | pre.fragment 124 | { 125 | font-family: 'Consolas', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 126 | background: #f7f7f7; 127 | border: 1px solid #d7d7d7; 128 | margin: 1em 1.75em; 129 | padding: 0.25em; 130 | overflow: auto; 131 | } 132 | 133 | code 134 | { 135 | font-family: 'Consolas', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 136 | padding: 0em 0.1em; 137 | margin: 0px; 138 | background-color: #e5e0cb; 139 | border-radius: 3px; 140 | -webkit-border-radius: 3px; 141 | -moz-border-radius: 3px; 142 | } 143 | 144 | .memdoc, dl.reflist dd 145 | { 146 | box-shadow: none; 147 | background-image: none; 148 | border: none; 149 | } 150 | 151 | .memproto, dl.reflist dt 152 | { 153 | box-shadow: none; 154 | background-image: none; 155 | text-shadow: none; 156 | font-family: monospace; 157 | background-color: #E5E0CB; 158 | border: none; 159 | border-radius: 4px; 160 | -webkit-border-radius: 4px; 161 | -moz-border-radius: 4px; 162 | } 163 | 164 | .tabs, .tabs2, .tabs3 165 | { 166 | background-image: none; 167 | background-color: #E5E0CB; 168 | } 169 | 170 | .tablist li 171 | { 172 | background-image: none; 173 | background-color: #E5E0CB; 174 | } 175 | 176 | .navpath ul 177 | { 178 | background-image: none; 179 | background-color: #E5E0CB; 180 | } 181 | 182 | #titlearea 183 | { 184 | text-align: center; 185 | padding: 10px 0px; 186 | } 187 | 188 | #container 189 | { 190 | background-color: #FFF; 191 | padding: 0px 20px; 192 | } 193 | 194 | address.footer 195 | { 196 | padding-bottom: 10px; 197 | } 198 | 199 | table.params 200 | { 201 | border: 2px solid #CCC; 202 | font-size: 13px; 203 | border-collapse: collapse; 204 | border-spacing: 0px; 205 | border-collapse: collapse; 206 | max-width: 800px; 207 | } 208 | 209 | table.params td 210 | { 211 | border: 1px solid grey; 212 | padding: 3px 3px 3px 3px; 213 | vertical-align: middle; 214 | } 215 | 216 | td.paramname 217 | { 218 | color: black; 219 | background-color: #e5e0cb; 220 | text-align: right; 221 | } 222 | -------------------------------------------------------------------------------- /docs/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $projectname: $title 9 | $title 10 | 11 | 12 | 13 | $treeview 14 | $search 15 | $mathjax 16 | 17 | $extrastylesheet 18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 | The Croc Programming Language 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /downloads/croc.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: Croc 3 | " Maintainer: Jarrett Billingsley 4 | " Last Change: 2011-06-18 5 | 6 | if !exists("main_syntax") 7 | if version < 600 8 | syntax clear 9 | elseif exists("b:current_syntax") 10 | finish 11 | endif 12 | let main_syntax = 'croc' 13 | endif 14 | 15 | syn case match 16 | 17 | " Keywords 18 | syn keyword crocExternal import module 19 | syn keyword crocConditional if else switch case default 20 | syn keyword crocBranch break continue 21 | syn keyword crocRepeat while for do foreach 22 | syn keyword crocBoolean true false 23 | syn keyword crocConstant null 24 | syn keyword crocStructure class coroutine namespace function 25 | syn keyword crocExceptions throw try catch finally scope 26 | syn keyword crocStatement return yield assert 27 | syn keyword crocReserved in vararg this super with is as 28 | syn keyword crocStorageClass local global 29 | if exists("croc_hl_operator_overload") 30 | syn keyword crocOpOverload opApply opCall opCat opCat_r opCatAssign opCmp 31 | syn keyword crocOpOverload opEquals opField opFieldAssign opIn opIndex opIndexAssign 32 | syn keyword crocOpOverload opLength opLengthAssign opMethod opSlice opSliceAssign 33 | endif 34 | 35 | " Comments 36 | syn keyword crocTodo contained TODO FIXME TEMP XXX 37 | syn match crocCommentStar contained "^\s*\*[^/]"me=e-1 38 | syn match crocCommentStar contained "^\s*\*$" 39 | syn match crocCommentPlus contained "^\s*+[^/]"me=e-1 40 | syn match crocCommentPlus contained "^\s*+$" 41 | syn region crocBlockComment start="/\*" end="\*/" contains=crocBlockCommentString,crocTodo,@Spell 42 | syn region crocNestedComment start="/+" end="+/" contains=crocNestedComment,crocNestedCommentString,crocTodo,@Spell 43 | syn match crocLineComment "//.*" contains=crocLineCommentString,crocTodo,@Spell 44 | 45 | hi link crocLineCommentString crocBlockCommentString 46 | hi link crocBlockCommentString crocString 47 | hi link crocNestedCommentString crocString 48 | hi link crocCommentStar crocBlockComment 49 | hi link crocCommentPlus crocNestedComment 50 | 51 | syn sync minlines=25 52 | 53 | " Characters 54 | syn match crocSpecialCharError contained "[^']" 55 | 56 | " Escape sequences 57 | syn match crocEscSequence "\\\([\"\\'abfnrtv]\|x\x\x\|u\x\{4}\|U\x\{8}\|\d\{1,3}\)" 58 | syn match crocCharacter "'[^']*'" contains=crocEscSequence,crocSpecialCharError 59 | syn match crocCharacter "'\\''" contains=crocEscSequence 60 | syn match crocCharacter "'[^\\]'" 61 | 62 | " Strings 63 | syn region crocString start=+"+ end=+"+ contains=crocEscSequence,@Spell 64 | syn region crocRawString start=+`+ end=+`+ contains=@Spell 65 | syn region crocRawString start=+@"+ skip=+""+ end=+"+ contains=@Spell 66 | 67 | " Numbers 68 | syn case ignore 69 | syn match crocInt display "\<\d[0-9_]*\>" 70 | " Hex number 71 | syn match crocHex display "\<0x[0-9a-f_]\+\>" 72 | " Octal number 73 | syn match crocOctal display "\<0c[0-7_]\+\>" 74 | "floating point number, with dot, optional exponent 75 | syn match crocFloat display "\<\d[0-9_]*\.[0-9_]*\(e[-+]\=[0-9_]\+\)\=" 76 | "floating point number, starting with a dot, optional exponent 77 | syn match crocFloat display "\(\.[0-9_]\+\)\(e[-+]\=[0-9_]\+\)\=\>" 78 | "floating point number, without dot, with exponent 79 | syn match crocFloat display "\<\d[0-9_]*e[-+]\=[0-9_]\+\>" 80 | " binary number 81 | syn match crocBinary display "\<0b[01_]\+\>" 82 | syn case match 83 | 84 | " The default highlighting. 85 | hi def link crocBinary Number 86 | hi def link crocInt Number 87 | hi def link crocHex Number 88 | hi def link crocOctal Number 89 | hi def link crocFloat Float 90 | hi def link crocBranch Conditional 91 | hi def link crocConditional Conditional 92 | hi def link crocRepeat Repeat 93 | hi def link crocExceptions Exception 94 | hi def link crocStatement Statement 95 | hi def link crocStorageClass StorageClass 96 | hi def link crocBoolean Boolean 97 | hi def link crocRawString String 98 | hi def link crocString String 99 | hi def link crocCharacter Character 100 | hi def link crocEscSequence SpecialChar 101 | hi def link crocSpecialCharError Error 102 | hi def link crocOpOverload Operator 103 | hi def link crocConstant Constant 104 | hi def link crocStructure Structure 105 | hi def link crocTodo Todo 106 | hi def link crocLineComment Comment 107 | hi def link crocBlockComment Comment 108 | hi def link crocNestedComment Comment 109 | hi def link crocExternal Include 110 | 111 | let b:current_syntax = "croc" 112 | 113 | " vim: ts=4 114 | -------------------------------------------------------------------------------- /libpcre.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JarrettBillingsley/Croc/1fa0693d17d0c565920dfa672104d92b4016d4bf/libpcre.dll -------------------------------------------------------------------------------- /samples/autoctor.croc: -------------------------------------------------------------------------------- 1 | module samples.autoctor 2 | 3 | local NOINIT = {} 4 | 5 | function autofield(name: string, type: string, init: any = NOINIT) = 6 | { name = name, type = type, init = init } 7 | 8 | function autoctor(c: class, fields: array, isOverride: bool = false) 9 | { 10 | local params, assignments = [], [] 11 | 12 | foreach(field; fields) 13 | { 14 | object.addField(c, field.name, field.init) 15 | 16 | if(field.init is NOINIT) 17 | params.append("{}: {}".format(field.name, field.type)) 18 | else 19 | params.append("{}: {} = {}".format(field.name, field.type, field.init)) 20 | 21 | assignments.append(":{0} = {0}".format(field.name)) 22 | } 23 | 24 | local ctorCode = "function constructor({}) {{ {} }".format(",".join(params), "; ".join(assignments)) 25 | local ctor = compiler.runString("return " ~ ctorCode) 26 | 27 | object.(isOverride ? 'addMethodOverride' : 'addMethod')(c, "constructor", ctor) 28 | return c 29 | } 30 | 31 | function autoctorOverride(c: class, fields: array) = 32 | autoctor(c, fields, true) 33 | 34 | // =================================================================== 35 | 36 | @autoctor( 37 | [ 38 | autofield('x', "int") 39 | autofield('y', "int", 2) 40 | autofield('z', "int", 3) 41 | ]) 42 | class Class 43 | {} 44 | 45 | function main() 46 | { 47 | local c = Class(4) 48 | writefln("{} {} {}", c.x, c.y, c.z) 49 | } -------------------------------------------------------------------------------- /samples/interfaces.croc: -------------------------------------------------------------------------------- 1 | module samples.interfaces 2 | 3 | class Method 4 | { 5 | _name 6 | _numParams 7 | 8 | this(name: string, numParams: int) 9 | { 10 | :_name = name 11 | :_numParams = numParams 12 | } 13 | 14 | function name() = 15 | :_name 16 | 17 | function implements(f: function) = 18 | f.numParams() == :_numParams 19 | 20 | function toString() = 21 | "{} ({} params)".format(:_name, :_numParams) 22 | } 23 | 24 | class Interface 25 | { 26 | _name 27 | _methods 28 | _implementors 29 | 30 | this(name: string, methods: array) 31 | { 32 | if(!methods.all(\m -> m as Method)) 33 | throw TypeError("All methods must be Methods") 34 | 35 | :_name = name 36 | :_methods = methods.dup() 37 | :_implementors = {} 38 | } 39 | 40 | function implement(T: class) 41 | { 42 | foreach(m; :_methods) 43 | { 44 | local name = m.name() 45 | 46 | if(!hasMethod(T, name) || !m.implements(T.(name))) 47 | throw TypeError("Class {} does not implement method '{}' from {}".format(nameOf(T), m, :_name)) 48 | } 49 | 50 | :_implementors[T] = true 51 | } 52 | 53 | function opCall(val: instance) 54 | { 55 | if(superOf(val) not in :_implementors) 56 | :implement(superOf(val)) 57 | 58 | return true 59 | } 60 | } 61 | 62 | function implements(T: class, vararg) 63 | { 64 | for(i; 0 .. #vararg) 65 | { 66 | local p = vararg[i] 67 | 68 | if(!(p as Interface)) 69 | throw TypeError("All varargs must be Interfaces") 70 | 71 | p.implement(T) 72 | } 73 | 74 | return T 75 | } 76 | 77 | local IStream = Interface("IStream", 78 | [ 79 | Method("read", 3) 80 | Method("write", 3) 81 | Method("seek", 2) 82 | ]) 83 | 84 | class DerpStream 85 | { 86 | function read(m, offset, size) {} 87 | function write(m, offset, size) {} 88 | function seek(offset, whence) {} 89 | } 90 | 91 | function streamSomething(s: @IStream) 92 | { 93 | s.read() 94 | writeln("yay!") 95 | } 96 | 97 | function main() 98 | { 99 | local d = DerpStream() 100 | streamSomething(d) 101 | } -------------------------------------------------------------------------------- /samples/nettest.croc: -------------------------------------------------------------------------------- 1 | module samples.nettest 2 | 3 | import net 4 | import stream: TextWriter, MemblockStream 5 | 6 | function httpGet(page: string) 7 | { 8 | if(page.startsWith("http://")) 9 | page = page[#"http://" ..] 10 | 11 | local slash = page.find("/") 12 | local server = page[.. slash] 13 | page = page[slash ..] 14 | 15 | if(#page == 0) 16 | page = "/" 17 | 18 | local sock = net.connect(server, 80) 19 | local writer = TextWriter(sock, "utf-8") 20 | writer.write("GET ", page, " HTTP/1.1\r\nHost: ", server, "\r\nConnection: close\r\n\r\n") 21 | 22 | local buf = MemblockStream() 23 | buf.copy(sock) 24 | sock.close() 25 | 26 | local ret = text.getCodec("utf-8").decode(buf.getBacking(), "ignore") 27 | 28 | local beginning = ret.find("\r\n\r\n") + 4 29 | 30 | if(beginning > #ret) 31 | return "" 32 | else 33 | return ret[beginning ..] 34 | } 35 | 36 | function main() 37 | { 38 | writeln(httpGet("http://www.iana.org/domains/example")) 39 | } -------------------------------------------------------------------------------- /samples/particles.croc: -------------------------------------------------------------------------------- 1 | module samples.particles 2 | 3 | import gl 4 | import sdl: key, event 5 | 6 | // Play! 7 | local Speed = 1.0 8 | local Life = 240 9 | 10 | function randColor() = 11 | math.frand(0.0, 1.0), math.frand(0.0, 1.0), math.frand(0.0, 1.0) 12 | 13 | class Particle 14 | { 15 | x; y 16 | dir; speed 17 | r; g; b; a 18 | origlife; life 19 | 20 | this(x, y, dir, speed, r, g, b, life) 21 | { 22 | :x = x 23 | :y = y 24 | :dir = dir 25 | :speed = speed 26 | :r = r 27 | :g = g 28 | :b = b 29 | :a = 1 30 | :origlife = life 31 | :origSpeed = speed 32 | :life = life 33 | } 34 | 35 | function update() 36 | { 37 | :life-- 38 | 39 | if(:life == 0) 40 | return false 41 | 42 | :a = (:life as float) / :origlife 43 | :speed = :origSpeed * :a 44 | :x += math.sin(:dir) * :speed 45 | :y += math.cos(:dir) * :speed 46 | 47 | return true 48 | } 49 | 50 | function draw() 51 | { 52 | gl.glColor4f(:r, :g, :b, :a) 53 | gl.glVertex2f(:x - 10, :y); gl.glVertex2f(:x + 10, :y) 54 | gl.glVertex2f(:x, :y - 10); gl.glVertex2f(:x, :y + 10) 55 | gl.glVertex2f(:x - 5, :y - 5); gl.glVertex2f(:x + 5, :y + 5) 56 | gl.glVertex2f(:x + 5, :y - 5); gl.glVertex2f(:x - 5, :y + 5) 57 | } 58 | } 59 | 60 | local particles = {} 61 | local freelist = {} 62 | local pooplist = {} 63 | 64 | function spawnParticle(x, y, dx, dy) 65 | { 66 | local p 67 | 68 | if(#freelist) 69 | { 70 | p = hash.take(freelist) 71 | freelist[p] = null 72 | } 73 | else 74 | p = Particle() 75 | 76 | local r, g, b = randColor() 77 | p.constructor(x as float, y as float, math.atan2(dx, dy), Speed, r, g, b, Life) 78 | particles[p] = true 79 | } 80 | 81 | function updateParticles() 82 | { 83 | hash.clear(pooplist) 84 | foreach(p, _; particles) 85 | { 86 | if(!p.update()) 87 | pooplist[p] = true 88 | } 89 | 90 | foreach(p, _; pooplist) 91 | { 92 | particles[p] = null 93 | freelist[p] = true 94 | } 95 | } 96 | 97 | function drawParticles() 98 | { 99 | gl.glBegin(gl.GL_LINES) 100 | foreach(p, _; particles) 101 | p.draw() 102 | gl.glEnd() 103 | } 104 | 105 | function main() 106 | { 107 | sdl.init(sdl.initEverything) 108 | scope(exit) sdl.quit() 109 | 110 | sdl.gl.setAttribute(sdl.gl.bufferSize, 32) 111 | sdl.gl.setAttribute(sdl.gl.depthSize, 16) 112 | sdl.gl.setAttribute(sdl.gl.doubleBuffer, 1) 113 | 114 | local w = 1152 115 | local h = 864 116 | 117 | if(!sdl.setVideoMode(w, h, 32, sdl.opengl | sdl.hwSurface)) 118 | if(!sdl.setVideoMode(w, h, 32, sdl.opengl | sdl.swSurface)) 119 | throw "Could not set video mode" 120 | 121 | sdl.caption("Particles") 122 | 123 | gl.load() 124 | gl.glViewport(0, 0, w, h) 125 | gl.glShadeModel(gl.GL_SMOOTH) 126 | gl.glClearColor(0, 0, 0, 1) 127 | gl.glClearDepth(1) 128 | gl.glEnable(gl.GL_BLEND) 129 | gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) 130 | 131 | gl.glMatrixMode(gl.GL_PROJECTION) 132 | gl.glLoadIdentity() 133 | gl.gluOrtho2D(0, w, 0, h) 134 | gl.glScalef(1, -1, 1) 135 | gl.glTranslatef(0, -h, 0) 136 | gl.glMatrixMode(gl.GL_MODELVIEW) 137 | 138 | local quitting = false 139 | local mx, my = 0, 0 140 | local mdx, mdy = 0, 0 141 | local mb = false 142 | 143 | while(!quitting) 144 | { 145 | foreach(e, a, b, c, d; event.poll) 146 | { 147 | switch(e) 148 | { 149 | case "keyDown": if(a == key.escape) event.push("quit"); break 150 | case "mouseDown": if(a == 1) mb = true; break 151 | case "mouseUp": if(a == 1) mb = false; break 152 | case "mouseMotion": mx, my, mdx, mdy = a, b, c, d; break 153 | case "quit": quitting = true; break 154 | default: break 155 | } 156 | } 157 | 158 | if(mb) 159 | spawnParticle(mx, my, mdx, mdy) 160 | 161 | updateParticles() 162 | 163 | gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) 164 | drawParticles() 165 | sdl.gl.swapBuffers() 166 | } 167 | } -------------------------------------------------------------------------------- /samples/sdltest.croc: -------------------------------------------------------------------------------- 1 | module samples.sdltest 2 | 3 | import sdl: event, key, niceKey, joystick as joy 4 | 5 | sdl.init(sdl.initEverything) 6 | 7 | scope(exit) 8 | sdl.quit() 9 | 10 | local w = 1152 11 | local h = 864 12 | 13 | sdl.setVideoMode(w, h, 32, sdl.hwSurface) 14 | 15 | for(i; 0 .. joy.count()) 16 | { 17 | joy.open(i) 18 | writefln("{}: {} axes, {} buttons, {} hats, {} trackballs", joy.info(i)) 19 | } 20 | 21 | joy.enableEvents(true) 22 | 23 | local quit = false 24 | 25 | while(!quit) 26 | { 27 | foreach(e, a, b, c, d; event.poll) 28 | { 29 | writefln("{}: {} {} {} {}", e, a ? a : "", b ? b : "", c ? c : "", d ? d : "") 30 | 31 | if(e == "quit" || (e == "keyDown" && a == key.escape)) 32 | quit = true 33 | 34 | // if(e.startsWith("joy")) 35 | // writefln("{}: {} {} {} {}", e, a, b, c, d) 36 | // 37 | // if(e == "active") 38 | // { 39 | // write(a ? "Gained " : "Lost ") 40 | // 41 | // if(b & event.mouseFocus) 42 | // write("mouse ") 43 | // 44 | // if(b & event.inputFocus) 45 | // write("input ") 46 | // 47 | // if(b & event.active) 48 | // write("active") 49 | // 50 | // writeln() 51 | // console.stdout.flush() 52 | // } 53 | } 54 | } -------------------------------------------------------------------------------- /samples/sieve.croc: -------------------------------------------------------------------------------- 1 | module samples.sieve 2 | 3 | function main() 4 | { 5 | local N = 1_000_000 6 | local start = time.microTime() 7 | local bits = array.new(math.iceil(N / ((math.intSize * 8) as float)), -1) 8 | 9 | // set 0 and 1 to false 10 | bits[0] &= ~3 11 | 12 | for(i; 2 .. math.sqrt(N) as int) 13 | { 14 | if(bits[i >> 6] & (1 << (i & 63))) 15 | { 16 | for(j; i * i .. N, i) 17 | bits[j >> 6] &= ~(1 << (j & 63)) 18 | } 19 | } 20 | 21 | local count = 0 22 | 23 | for(i; 0 .. #bits) 24 | { 25 | local x = bits[i] - ((bits[i] & 0xAAAAAAAAAAAAAAAA) >>> 1) 26 | x = (x & 0x3333333333333333) + ((x >>> 2) & 0x3333333333333333) 27 | x = (x + (x >>> 4)) & 0x0F0F0F0F0F0F0F0F 28 | count += (x * 0x0101010101010101) >>> 56 29 | } 30 | 31 | local end = time.microTime() 32 | writefln("Took {:.3} ms", (end - start) / 1000.0) 33 | writefln("There are {} primes below {}.", count, N) 34 | } -------------------------------------------------------------------------------- /samples/speed.croc: -------------------------------------------------------------------------------- 1 | module samples.speed 2 | 3 | /* 4 | Taken from the Io speed test. 5 | On my desktop. 6 | 7 | Python reflIntMath := 13.93 8 | Python reflFloatMath := 9.69 9 | 10 | Python intMath := 14.58 11 | Python floatMath := 11.42 12 | 13 | Python localAccesses := 26.74 14 | Python localSets := 29.07 15 | 16 | Python slotAccesses := 11.85 17 | Python slotSets := 10.68 18 | 19 | Python blockActivations := 3.55 20 | Python instantiations := 3.68 21 | Python version := "2.6.2 final 0" 22 | 23 | // values in millions per second 24 | 25 | Croc reflIntMath := 29.55 26 | Croc reflFloatMath := 27.54 27 | 28 | Croc intMath := 21.40 29 | Croc floatMath := 23.69 30 | 31 | Croc localAccesses := 34.16 32 | Croc localSets := 35.48 33 | 34 | Croc slotAccesses := 12.78 35 | Croc slotSets := 12.34 36 | 37 | Croc blockActivations := 3.33 38 | Croc instantiations := 1.61 39 | 40 | Croc version := "2.0" 41 | 42 | // values in millions per second 43 | */ 44 | 45 | local t1 46 | local oneMillion = 10_000_000 47 | 48 | local function foo() {} 49 | 50 | local class Tester 51 | { 52 | x 53 | 54 | function beginTimer() 55 | { 56 | t1 = time.microTime() 57 | } 58 | 59 | function endTimer(s) 60 | { 61 | t1 = time.microTime() - t1 62 | local mps = (oneMillion as float) / t1 63 | writefln("Croc {} := {:.2}", s, mps) 64 | } 65 | 66 | function testIntMath() 67 | { 68 | :beginTimer() 69 | local x = 0 70 | local y = 5 71 | local z = 10 72 | 73 | for(i; 0 .. oneMillion / 8) 74 | { 75 | x = y + z; x = y + z; x = y + z; x = y + z 76 | x = y + z; x = y + z; x = y + z; x = y + z 77 | } 78 | 79 | :endTimer("intMath\t\t") 80 | } 81 | 82 | function testFloatMath() 83 | { 84 | :beginTimer() 85 | local x = 0.0 86 | local y = 5.0 87 | local z = 10.0 88 | 89 | for(i; 0 .. oneMillion / 8) 90 | { 91 | x = y + z; x = y + z; x = y + z; x = y + z 92 | x = y + z; x = y + z; x = y + z; x = y + z 93 | } 94 | 95 | :endTimer("floatMath\t\t") 96 | } 97 | 98 | function testReflIntMath() 99 | { 100 | :beginTimer() 101 | local x = 0 102 | local y = 5 103 | 104 | for(i; 0 .. oneMillion / 8) 105 | { 106 | x += y; x += y; x += y; x += y 107 | x += y; x += y; x += y; x += y 108 | } 109 | 110 | :endTimer("reflIntMath\t") 111 | } 112 | 113 | function testReflFloatMath() 114 | { 115 | :beginTimer() 116 | local x = 0.0 117 | local y = 5.0 118 | 119 | for(i; 0 .. oneMillion / 8) 120 | { 121 | x += y; x += y; x += y; x += y 122 | x += y; x += y; x += y; x += y 123 | } 124 | 125 | :endTimer("reflFloatMath\t") 126 | } 127 | 128 | function testLocals() 129 | { 130 | :beginTimer() 131 | local v = 1 132 | local y 133 | 134 | for(i ; 0 .. oneMillion / 8) 135 | { 136 | y = v; y = v; y = v; y = v 137 | y = v; y = v; y = v; y = v 138 | } 139 | 140 | :endTimer("localAccesses\t") 141 | } 142 | 143 | function testSetLocals() 144 | { 145 | :beginTimer() 146 | local v = 1 147 | 148 | for(i ; 0 .. oneMillion / 8) 149 | { 150 | v = 1; v = 2; v = 3; v = 4 151 | v = 1; v = 2; v = 3; v = 4 152 | } 153 | 154 | :endTimer("localSets\t\t") 155 | } 156 | 157 | function testSlot() 158 | { 159 | :beginTimer() 160 | :x = 1 161 | local y 162 | 163 | for(i ; 0 .. oneMillion / 8) 164 | { 165 | y = :x; y = :x; y = :x; y = :x 166 | y = :x; y = :x; y = :x; y = :x 167 | } 168 | 169 | :endTimer("slotAccesses\t") 170 | } 171 | 172 | function testSetSlot() 173 | { 174 | :beginTimer() 175 | 176 | for(i ; 0 .. oneMillion / 8) 177 | { 178 | :x = 1; :x = 2; :x = 3; :x = 4 179 | :x = 1; :x = 2; :x = 3; :x = 4; 180 | } 181 | 182 | :endTimer("slotSets\t\t") 183 | } 184 | 185 | function testBlock() 186 | { 187 | :beginTimer() 188 | 189 | for(i ; 0 .. oneMillion / 8) 190 | { 191 | foo(); foo(); foo(); foo() 192 | foo(); foo(); foo(); foo() 193 | } 194 | 195 | :endTimer("blockActivations\t") 196 | } 197 | 198 | function testInstantiations() 199 | { 200 | :beginTimer() 201 | 202 | for(i ; 0 .. oneMillion / 8) 203 | { 204 | Tester(); Tester(); Tester(); Tester() 205 | Tester(); Tester(); Tester(); Tester() 206 | } 207 | 208 | :endTimer("instantiations\t") 209 | } 210 | 211 | function test() 212 | { 213 | :testReflIntMath() 214 | :testReflFloatMath() 215 | writeln() 216 | :testIntMath() 217 | :testFloatMath() 218 | writeln() 219 | :testLocals() 220 | :testSetLocals() 221 | writeln() 222 | :testSlot() 223 | :testSetSlot() 224 | writeln() 225 | :testBlock() 226 | :testInstantiations() 227 | 228 | writeln() 229 | writeln("Croc version\t\t := \"2.0\"") 230 | writeln() 231 | writeln("// values in millions per second") 232 | } 233 | } 234 | 235 | function main() 236 | { 237 | Tester.test() 238 | } -------------------------------------------------------------------------------- /samples/speed.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | oneMillion = 5000000 5 | 6 | class Tester: 7 | def foo(self): 8 | return 1 9 | 10 | def beginTimer(self): 11 | self.t1 = time.time() 12 | 13 | def endTimer(self, s): 14 | mps = (oneMillion/1000000)/(time.time() - self.t1) 15 | print "Python %s := %0.2f" % (s, mps) 16 | 17 | def testReflIntMath(self): 18 | self.beginTimer() 19 | x = 0; 20 | y = 5; 21 | 22 | for i in xrange(oneMillion/8): 23 | x += y; x += y; x += y; x += y; 24 | x += y; x += y; x += y; x += y; 25 | 26 | self.endTimer("reflIntMath\t") 27 | 28 | def testReflFloatMath(self): 29 | self.beginTimer() 30 | x = 0.0; 31 | y = 5.0; 32 | 33 | for i in xrange(oneMillion/8): 34 | x += y; x += y; x += y; x += y; 35 | x += y; x += y; x += y; x += y; 36 | 37 | self.endTimer("reflFloatMath\t") 38 | 39 | def testIntMath(self): 40 | self.beginTimer() 41 | x = 0; 42 | y = 5; 43 | z = 10; 44 | 45 | for i in xrange(oneMillion/8): 46 | x = y + z; x = y + z; x = y + z; x = y + z; 47 | x = y + z; x = y + z; x = y + z; x = y + z; 48 | 49 | self.endTimer("intMath\t\t") 50 | 51 | def testFloatMath(self): 52 | self.beginTimer() 53 | x = 0.0; 54 | y = 5.0; 55 | z = 10.0; 56 | 57 | for i in xrange(oneMillion/8): 58 | x = y + z; x = y + z; x = y + z; x = y + z; 59 | x = y + z; x = y + z; x = y + z; x = y + z; 60 | 61 | self.endTimer("floatMath\t") 62 | 63 | def testSlot(self): 64 | self.beginTimer() 65 | self.x = 1 66 | for i in xrange(oneMillion/8): 67 | self.x; self.x; self.x; self.x; 68 | self.x; self.x; self.x; self.x; 69 | self.endTimer("slotAccesses\t") 70 | 71 | def testSetSlot(self): 72 | self.beginTimer() 73 | self.x = 1 74 | for i in xrange(oneMillion/8): 75 | self.x = 1; self.x = 2; self.x = 3; self.x = 4; 76 | self.x = 1; self.x = 2; self.x = 3; self.x = 4; 77 | self.endTimer("slotSets\t\t") 78 | 79 | def testBlock(self): 80 | self.beginTimer() 81 | for i in xrange(oneMillion/8): 82 | self.foo(); self.foo(); self.foo(); self.foo(); 83 | self.foo(); self.foo(); self.foo(); self.foo() 84 | self.endTimer("blockActivations\t") 85 | 86 | def testInstantiations(self): 87 | self.beginTimer() 88 | for i in xrange(oneMillion/8): 89 | Tester(); Tester(); Tester(); Tester(); 90 | Tester(); Tester(); Tester(); Tester(); 91 | self.endTimer("instantiations\t") 92 | 93 | def testLocals(self): 94 | self.beginTimer() 95 | v = 1 96 | for i in xrange(oneMillion/8): 97 | v; v; v; v; 98 | v; v; v; v; 99 | self.endTimer("localAccesses\t") 100 | 101 | def testSetLocals(self): 102 | self.beginTimer() 103 | v = 1 104 | for i in xrange(oneMillion/8): 105 | v = 1; v = 2; v = 3; v = 4; 106 | v = 1; v = 2; v = 3; v = 4; 107 | self.endTimer("localSets\t") 108 | 109 | def test(self): 110 | print "" 111 | self.testReflIntMath() 112 | self.testReflFloatMath() 113 | print "" 114 | self.testIntMath() 115 | self.testFloatMath() 116 | print "" 117 | self.testLocals() 118 | self.testSetLocals() 119 | print "" 120 | self.testSlot() 121 | self.testSetSlot() 122 | print "" 123 | self.testBlock() 124 | self.testInstantiations() 125 | 126 | import sys 127 | print "Python version\t\t := ", sys.version 128 | print "" 129 | print "// values in millions per second" 130 | print "" 131 | 132 | 133 | Tester().test() 134 | 135 | -------------------------------------------------------------------------------- /samples/threadtest.croc: -------------------------------------------------------------------------------- 1 | module threadtest 2 | 3 | function Set(vararg) = {[vararg[i]] = vararg[i] for i in 0 .. #vararg} 4 | 5 | // A class which encapsulates a thread's body, a message queue, and timed wait 6 | // functionality. 7 | class Thread 8 | { 9 | mBody 10 | mWaitTime 11 | mNeedMessage = false 12 | mMessageHead 13 | mMessageTail 14 | 15 | // Construct a Thread with a thread body. 16 | this(body: thread) 17 | :mBody = body 18 | 19 | // Begin a timed wait. Duration is in seconds (int or float). This may 20 | // not work right for long durations. 21 | function beginWait(duration: int|float) 22 | { 23 | :mWaitTime = time.microTime() + ((duration * 1000000) as int) 24 | } 25 | 26 | // Add a message to this thread's message queue. 27 | function send(value) 28 | { 29 | local n = { value = value } 30 | 31 | if(:mMessageTail is null) 32 | :mMessageHead = n 33 | else 34 | :mMessageTail.next = n 35 | 36 | :mMessageTail = n 37 | } 38 | 39 | // Try to pop a message off this thread's message queue. Returns 'false' if there 40 | // was no available message, in which case it sets its status to "waiting for message". 41 | // Returns (true, message) if there was a message. 42 | function receive() 43 | { 44 | if(:mMessageHead is null) 45 | { 46 | :mNeedMessage = true 47 | return false 48 | } 49 | 50 | local item = :mMessageHead 51 | :mMessageHead = :mMessageHead.next 52 | 53 | if(:mMessageHead is null) 54 | :mMessageTail = null 55 | 56 | return true, item.value 57 | } 58 | 59 | // Resume the thread's body. This should only be called when isWaiting() returns 60 | // false. This will give the thread any message that it's waiting for, and if none 61 | // is waiting, will give it the varargs to this function instead. 62 | function resume(vararg) 63 | { 64 | if(:mNeedMessage) 65 | { 66 | assert(:mMessageHead is not null) 67 | :mNeedMessage = false 68 | local ok, value = :receive() 69 | assert(ok) 70 | return (:mBody)(value) 71 | } 72 | else 73 | return (:mBody)(vararg) 74 | } 75 | 76 | // Returns a bool, whether this thread is waiting, either on a message or on a timer. 77 | function isWaiting() 78 | { 79 | if(:mNeedMessage) 80 | return :mMessageHead is null 81 | else if(:mWaitTime is null) 82 | return false 83 | else 84 | return time.microTime() < :mWaitTime 85 | } 86 | 87 | // Returns whether the thread has completed or not. 88 | function isDead() = :mBody.isDead() 89 | } 90 | 91 | // Pass this function a set of Threads (i.e. a table where the keys == values). 92 | // This function only returns when all the threads have exited. 93 | function scheduler(threads) 94 | { 95 | // Very clever function. It handles "system calls", which are yielded values 96 | // from threads. It then can resume the threads and tailcall itself, making 97 | // itself into a sort of loop that will only break when the thread dies or enters 98 | // a waiting state. 99 | function handleCall(thread, type, vararg) 100 | { 101 | if(thread.isDead()) 102 | return true 103 | 104 | switch(type) 105 | { 106 | case "Yield": 107 | return false 108 | 109 | case "Wait": 110 | thread.beginWait(vararg) 111 | return false 112 | 113 | case "Send": 114 | local dest, value = vararg 115 | assert(dest in threads) 116 | dest.send(value) 117 | return handleCall(thread, thread.resume()) 118 | 119 | case "Receive": 120 | local ok, value = thread.receive() 121 | 122 | if(!ok) 123 | return false 124 | 125 | return handleCall(thread, thread.resume(value)) 126 | 127 | default: 128 | throw "Unknown call" 129 | } 130 | } 131 | 132 | while(#threads > 0) 133 | { 134 | foreach(thread; threads, "modify") 135 | { 136 | if(thread.isWaiting()) 137 | continue 138 | 139 | if(handleCall(thread, thread.resume())) 140 | threads[thread] = null 141 | } 142 | } 143 | } 144 | 145 | // Wait for 'duration' seconds. Can be float or int. 146 | function wait(duration) 147 | { 148 | yield("Wait", duration) 149 | } 150 | 151 | // Send the message 'value' to Thread 'dest'. 152 | function send(dest, value) 153 | { 154 | yield("Send", dest, value) 155 | } 156 | 157 | // Receive a message from any thread. 158 | function receive() = yield("Receive") 159 | 160 | // Example producer-consumer code with message-passing for synchronization. 161 | function main() 162 | { 163 | local N = 100 164 | 165 | local producer, consumer 166 | 167 | producer = Thread(thread.new(\ 168 | { 169 | local i = 0 170 | 171 | while(true) 172 | { 173 | wait(0.3 + math.rand(0, 100) / 100.0) 174 | 175 | local item = { type = "Item", value = i } 176 | i++ 177 | local msg = receive() 178 | assert(msg.type == "Empty") 179 | writefln("Producer produced {}.", item.value) 180 | send(consumer, item) 181 | } 182 | })) 183 | 184 | consumer = Thread(thread.new(\ 185 | { 186 | local empty = { type = "Empty" } 187 | 188 | for(i; 0 .. N) 189 | send(producer, empty) 190 | 191 | while(true) 192 | { 193 | local msg = receive() 194 | wait(0.3 + math.rand(0, 100) / 100.0) 195 | assert(msg.type == "Item") 196 | send(producer, empty) 197 | writefln("Consumer consumed {}.", msg.value) 198 | } 199 | })) 200 | 201 | scheduler(Set(producer, consumer)) 202 | writeln("Finished.") 203 | } -------------------------------------------------------------------------------- /src/croc/README.md: -------------------------------------------------------------------------------- 1 | Source Layout 2 | ============= 3 | 4 | croc/api[ex|funcs|types].h define the public C API of Croc. Everything else is internals/private. 5 | 6 | ext/ is for code that I didn't write (ext is short for "external") and for any utility programs (like for converting 7 | data files to header files). 8 | 9 | General structure of the source, from lowest-level to highest-level: 10 | 11 | * util/ contains useful code that doesn't actually depend on anything in the Croc library, like Unicode handling, array 12 | manipulation, string utilities etc. 13 | * base/ contains super low-level stuff like the GC, memory management, low-level data structures etc. 14 | * types/ contains the types used throughout the library. types/base holds most of the declarations, and the other files 15 | hold the definitions for the reference types. These are low-level operations, below the level of the interpreter. 16 | * internal/ contains what you might call the "interpreter", which includes the actual bytecode interpreter as well as 17 | all the functionality needed to support it. Much of this functionality is also used by the public API. 18 | * compiler/ contains... the compiler. 19 | * api/ contains the implementation of the functions defined in croc/apifuncs.h, that is, the basic API. 20 | * ex/ contains the implementation of the functions defined in croc/apiex.h, which is implemented on top of the public 21 | croc C API. Ostensibly. 22 | * stdlib/ contains the standard libraries. Again, these are ostensibly written entirely with the public C API, but in 23 | reality they dip down into the library internals for better performance. -------------------------------------------------------------------------------- /src/croc/addons/all.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_ADDONS_ALL_HPP 2 | #define CROC_ADDONS_ALL_HPP 3 | 4 | namespace croc 5 | { 6 | void initDevilLib(CrocThread* t); 7 | void initGlfwLib(CrocThread* t); 8 | void initImGuiLib(CrocThread* t); 9 | void initNetLib(CrocThread* t); 10 | void initOpenAlLib(CrocThread* t); 11 | void initPcreLib(CrocThread* t); 12 | } 13 | 14 | #endif -------------------------------------------------------------------------------- /src/croc/addons/gl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_ADDONS_GL_HPP 2 | #define CROC_ADDONS_GL_HPP 3 | 4 | #include "croc/ext/glad/glad.hpp" 5 | 6 | #include "croc/api.h" 7 | 8 | namespace croc 9 | { 10 | void loadOpenGL(CrocThread* t, GLADloadproc load); 11 | } 12 | 13 | #endif -------------------------------------------------------------------------------- /src/croc/api.h: -------------------------------------------------------------------------------- 1 | #ifndef CROC_API_H 2 | #define CROC_API_H 3 | 4 | /** @defgroup APITypes API Types 5 | The basic types and enumerations used by the basic API. */ 6 | 7 | /** @defgroup API API 8 | The basic Croc API. */ 9 | 10 | /** @defgroup Ex Extended API 11 | Useful functionality built on top of the basic API. */ 12 | 13 | #include "croc/apitypes.h" 14 | #include "croc/apifuncs.h" 15 | #include "croc/apiex.h" 16 | 17 | #endif -------------------------------------------------------------------------------- /src/croc/api/apichecks.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_API_APICHECKS_HPP 2 | #define CROC_API_APICHECKS_HPP 3 | 4 | #include "croc/internal/stack.hpp" 5 | 6 | #define API_CHECK_NUM_PARAMS(numParams)\ 7 | do {\ 8 | assert(t->stackIndex > t->stackBase);\ 9 | if((croc_getStackSize(*t) - 1) < (numParams))\ 10 | croc_eh_throwStd(*t, "ApiError",\ 11 | "%s - not enough parameters (expected %" CROC_SIZE_T_FORMAT ", only have %" CROC_SIZE_T_FORMAT " stack slots)",\ 12 | __FUNCTION__,\ 13 | cast(uword)(numParams),\ 14 | croc_getStackSize(*t) - 1);\ 15 | } while(false) 16 | 17 | #define API_CHECK_PARAM(name, idx, type, niceName)\ 18 | if(croc_type(*t, (idx)) != CrocType_##type)\ 19 | API_PARAM_TYPE_ERROR(idx, niceName, typeToString(CrocType_##type));\ 20 | auto name = getValue(t, (idx))->m##type; 21 | 22 | #define API_PARAM_TYPE_ERROR(idx, paramName, expected)\ 23 | do {\ 24 | croc_pushTypeString(*t, (idx));\ 25 | croc_eh_throwStd(*t, "TypeError", "%s - Expected type '%s' for %s, not '%s'",\ 26 | __FUNCTION__, (expected), (paramName), croc_getString(*t, -1));\ 27 | } while(false) 28 | 29 | #endif -------------------------------------------------------------------------------- /src/croc/api/array.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/stack.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | /** Creates a new array object and pushes it onto the stack. 12 | 13 | \param len is how long the new array should be. It will be filled with \c null. 14 | \returns the stack index of the pushed value. */ 15 | word_t croc_array_new(CrocThread* t_, uword_t len) 16 | { 17 | auto t = Thread::from(t_); 18 | croc_gc_maybeCollect(t_); 19 | return push(t, Value::from(Array::create(t->vm->mem, len))); 20 | } 21 | 22 | /** Creates a new array object using values on top of the stack to fill it. This pops the values, and pushes the new 23 | array. 24 | 25 | \param len is how long the array will be. There should be this many values sitting on top of the stack. 26 | \returns the stack index of the pushed value. */ 27 | word_t croc_array_newFromStack(CrocThread* t_, uword_t len) 28 | { 29 | auto t = Thread::from(t_); 30 | API_CHECK_NUM_PARAMS(len); 31 | croc_gc_maybeCollect(t_); 32 | auto a = Array::create(t->vm->mem, len); 33 | a->sliceAssign(t->vm->mem, 0, len, t->stack.slice(t->stackIndex - len, t->stackIndex)); 34 | 35 | if(len) 36 | croc_pop(t_, len); 37 | 38 | return push(t, Value::from(a)); 39 | } 40 | 41 | /** Fills every slot of the array at slot \c arr with the value on top of the stack, and then pops that value. */ 42 | void croc_array_fill(CrocThread* t_, word_t arr) 43 | { 44 | auto t = Thread::from(t_); 45 | API_CHECK_NUM_PARAMS(1); 46 | API_CHECK_PARAM(a, arr, Array, "arr"); 47 | a->fill(t->vm->mem, t->stack[t->stackIndex - 1]); 48 | croc_popTop(t_); 49 | } 50 | } -------------------------------------------------------------------------------- /src/croc/api/funcdef.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/stack.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | const char* croc_funcdef_getName(CrocThread* t_, word_t funcdef) 12 | { 13 | auto t = Thread::from(t_); 14 | API_CHECK_PARAM(fd, funcdef, Funcdef, "funcdef"); 15 | return fd->name->toCString(); 16 | } 17 | 18 | const char* croc_funcdef_getNamen(CrocThread* t_, word_t funcdef, uword_t* len) 19 | { 20 | auto t = Thread::from(t_); 21 | API_CHECK_PARAM(fd, funcdef, Funcdef, "funcdef"); 22 | *len = fd->name->length; 23 | return fd->name->toCString(); 24 | } 25 | } -------------------------------------------------------------------------------- /src/croc/api/mem.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/types/base.hpp" 4 | 5 | using namespace croc; 6 | 7 | extern "C" 8 | { 9 | /** Allocates a block of memory through the Croc memory allocator, using the allocator function that the thread's VM 10 | was created with. 11 | 12 | This is not garbage-collected; you are entirely responsible for managing this memory. It will, however, be 13 | tracked for memory leaks if the library was compiled with the CROC_LEAK_DETECTOR option. 14 | 15 | \param size is the number of bytes to allocate. 16 | \returns a pointer to the allocated memory. */ 17 | void* croc_mem_alloc(CrocThread* t_, uword_t size) 18 | { 19 | auto t = Thread::from(t_); 20 | return DArray::alloc(t->vm->mem, size).ptr; 21 | } 22 | 23 | /** Resizes a block of memory that was allocated with \ref croc_mem_alloc. 24 | 25 | \param[in,out] mem is the address of the pointer to the block to resize. This pointer may change as a result of 26 | resizing the block. 27 | \param[in,out] memSize is the address of the existing size of the memory block. It will be changed to \c newSize. 28 | \param newSize is the new size of the memory block. */ 29 | void croc_mem_resize(CrocThread* t_, void** mem, uword_t* memSize, uword_t newSize) 30 | { 31 | auto t = Thread::from(t_); 32 | auto arr = DArray::n(cast(uint8_t*)*mem, *memSize); 33 | arr.resize(t->vm->mem, newSize); 34 | *mem = arr.ptr; 35 | *memSize = arr.length; 36 | } 37 | 38 | /** Duplicates a block of memory that was allocated with \ref croc_mem_alloc. The new memory block is the same size 39 | and contains the same data. */ 40 | void* croc_mem_dup(CrocThread* t_, void* mem, uword_t memSize) 41 | { 42 | auto t = Thread::from(t_); 43 | auto arr = DArray::n(cast(uint8_t*)mem, memSize); 44 | return arr.dup(t->vm->mem).ptr; 45 | } 46 | 47 | /** Frees a block of memory that was allocated with \ref croc_mem_alloc. You must free every block that you 48 | allocated! */ 49 | void croc_mem_free(CrocThread* t_, void** mem, uword_t* memSize) 50 | { 51 | auto t = Thread::from(t_); 52 | auto arr = DArray::n(cast(uint8_t*)*mem, *memSize); 53 | arr.free(t->vm->mem); 54 | *mem = nullptr; 55 | *memSize = 0; 56 | } 57 | } -------------------------------------------------------------------------------- /src/croc/api/memblock.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/stack.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | /** Creates and pushes a new memblock object that is \c len bytes long. 12 | \returns the stack index of the pushed value. */ 13 | word_t croc_memblock_new(CrocThread* t_, uword_t len) 14 | { 15 | auto t = Thread::from(t_); 16 | croc_gc_maybeCollect(t_); 17 | return push(t, Value::from(Memblock::create(t->vm->mem, len))); 18 | } 19 | 20 | /** Creates and pushes a new memblock whose data is a \a copy of the given array. 21 | 22 | \param arr is the pointer to the array. 23 | \param arrLen is the length of the array, in bytes. 24 | \returns the stack index of the pushed value. */ 25 | word_t croc_memblock_fromNativeArray(CrocThread* t_, const void* arr, uword_t arrLen) 26 | { 27 | auto t = Thread::from(t_); 28 | auto ret = croc_memblock_new(t_, arrLen); 29 | auto data = getMemblock(t, ret)->data; 30 | data.slicea(DArray::n(cast(uint8_t*)arr, arrLen)); 31 | return ret; 32 | } 33 | 34 | /** Creates and pushes a new memblock object that is a view of the given array. The memblock will not own its data. 35 | 36 | \param arr is the pointer to the array. It is your responsibility to keep this array around as long as the 37 | memblock which views it exists. 38 | \param arrLen is the length of the array, in bytes. 39 | \returns the stack index of the pushed value. */ 40 | word_t croc_memblock_viewNativeArray(CrocThread* t_, void* arr, uword_t arrLen) 41 | { 42 | auto t = Thread::from(t_); 43 | return push(t, Value::from(Memblock::createView(t->vm->mem, DArray::n(cast(uint8_t*)arr, arrLen)))); 44 | } 45 | 46 | /** Same as \ref croc_memblock_viewNativeArray, except instead of creating a new memblock, it changes an existing 47 | memblock at \c slot so that its data points to the native array. If the memblock had any data (and owned it), that 48 | data will be freed. 49 | 50 | \param arr is the pointer to the array. It is your responsibility to keep this array around as long as the 51 | memblock which views it exists. 52 | \param arrLen is the length of the array, in bytes. */ 53 | void croc_memblock_reviewNativeArray(CrocThread* t_, word_t slot, void* arr, uword_t arrLen) 54 | { 55 | auto t = Thread::from(t_); 56 | API_CHECK_NUM_PARAMS(1); 57 | API_CHECK_PARAM(m, slot, Memblock, "slot"); 58 | m->view(t->vm->mem, DArray::n(cast(uint8_t*)arr, arrLen)); 59 | } 60 | 61 | /** Returns a pointer to the data of the memblock in the given \c slot. 62 | 63 | The pointer returned from this may point into Croc's memory (if the memblock owns its data). You are allowed to 64 | modify the data at this pointer, but don't store it unless you know the memblock won't be collected! */ 65 | char* croc_memblock_getData(CrocThread* t_, word_t slot) 66 | { 67 | auto t = Thread::from(t_); 68 | API_CHECK_NUM_PARAMS(1); 69 | API_CHECK_PARAM(m, slot, Memblock, "slot"); 70 | return cast(char*)m->data.ptr; 71 | } 72 | 73 | /** Same as \ref croc_memblock_getData, except it also returns the length of the memblock's data in bytes through 74 | the \c len parameter. 75 | 76 | The pointer returned from this may point into Croc's memory (if the memblock owns its data). You are allowed to 77 | modify the data at this pointer, but don't store it unless you know the memblock won't be collected! */ 78 | char* croc_memblock_getDatan(CrocThread* t_, word_t slot, uword_t* len) 79 | { 80 | auto t = Thread::from(t_); 81 | API_CHECK_NUM_PARAMS(1); 82 | API_CHECK_PARAM(m, slot, Memblock, "slot"); 83 | *len = m->data.length; 84 | return cast(char*)m->data.ptr; 85 | } 86 | 87 | /** \returns nonzero if the memblock at the given \c slot owns its data (it is allocated on the Croc heap). If this 88 | returns 0, it means the memblock is a view of a native array. */ 89 | int croc_memblock_ownData(CrocThread* t_, word_t slot) 90 | { 91 | auto t = Thread::from(t_); 92 | API_CHECK_NUM_PARAMS(1); 93 | API_CHECK_PARAM(m, slot, Memblock, "slot"); 94 | return m->ownData; 95 | } 96 | } -------------------------------------------------------------------------------- /src/croc/api/namespace.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/basic.hpp" 5 | #include "croc/internal/calls.hpp" 6 | #include "croc/internal/stack.hpp" 7 | #include "croc/types/base.hpp" 8 | 9 | using namespace croc; 10 | 11 | extern "C" 12 | { 13 | namespace 14 | { 15 | word_t newNamespaceInternal(Thread* t, const char* name, Namespace* parent) 16 | { 17 | croc_gc_maybeCollect(*t); 18 | return push(t, Value::from(Namespace::create(t->vm->mem, String::create(t->vm, atoda(name)), parent))); 19 | } 20 | } 21 | 22 | /** Creates and pushes a new namespace object whose parent namespace will be set to the current function's 23 | environment (or the global namespace if there is no current function). 24 | 25 | This is the same behavior as namespace name {} in Croc (though it doesn't actually declare the namespace; 26 | you'll have to store it somewhere, like in a global). 27 | 28 | \param name is the name that the namespace will be given. 29 | \returns the stack index of the pushed value. */ 30 | word_t croc_namespace_new(CrocThread* t_, const char* name) 31 | { 32 | auto t = Thread::from(t_); 33 | return newNamespaceInternal(t, name, getEnv(t)); 34 | } 35 | 36 | /** Creates and pushes a new namespace object whose parent is in slot \c parent. 37 | 38 | \param parent should be either a namespace or null, in which case the new namespace will have no parent. 39 | \param name is the name that the namespace will be given. 40 | \returns the stack index of the pushed value. */ 41 | word_t croc_namespace_newWithParent(CrocThread* t_, word_t parent, const char* name) 42 | { 43 | auto t = Thread::from(t_); 44 | 45 | if(croc_isNull(t_, parent)) 46 | return newNamespaceInternal(t, name, nullptr); 47 | else if(croc_isNamespace(t_, parent)) 48 | return newNamespaceInternal(t, name, getNamespace(t, parent)); 49 | else 50 | API_PARAM_TYPE_ERROR(parent, "parent", "null|namespace"); 51 | 52 | assert(false); 53 | return 0; // dummy 54 | } 55 | 56 | /** Creates and pushes a new namespace object without a parent. 57 | 58 | \param name is the name that the namespace will be given. 59 | \returns the stack index of the pushed value. */ 60 | word_t croc_namespace_newNoParent(CrocThread* t_, const char* name) 61 | { 62 | auto t = Thread::from(t_); 63 | return newNamespaceInternal(t, name, nullptr); 64 | } 65 | 66 | /** Removes all key-value pairs from the namespace in slot \c ns. */ 67 | void croc_namespace_clear(CrocThread* t_, word_t ns) 68 | { 69 | auto t = Thread::from(t_); 70 | API_CHECK_PARAM(n, ns, Namespace, "ns"); 71 | n->clear(t->vm->mem); 72 | } 73 | 74 | /** Pushes the "full name" of the namespace in slot \c ns, which is the name of the namespace and all its parents, 75 | separated by dots. 76 | 77 | \returns the stack index of the pushed value. */ 78 | word_t croc_namespace_pushFullName(CrocThread* t_, word_t ns) 79 | { 80 | auto t = Thread::from(t_); 81 | API_CHECK_PARAM(n, ns, Namespace, "ns"); 82 | return pushFullNamespaceName(t, n); 83 | } 84 | } -------------------------------------------------------------------------------- /src/croc/api/nativeref.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/stack.hpp" 4 | #include "croc/types/base.hpp" 5 | 6 | using namespace croc; 7 | 8 | extern "C" 9 | { 10 | /** Create a native reference to an object. 11 | 12 | Native references are a way for native code to keep a reference to a Croc object without having it get collected. 13 | You create a reference to the object, then use \ref croc_ref_push to get the object associated with that reference. 14 | When you no longer need to reference the object, use \ref croc_ref_remove to remove it. 15 | 16 | You can create multiple references to the same object. In this case, you must remove all the references separately. 17 | In this way it works something like a reference counting scheme. As long as at least one native reference to an 18 | object exists, it will never be collected. 19 | 20 | \param idx is the stack slot of the object to get a reference to. It must be a GC-able type. 21 | \returns the unique reference value to be passed to the other native reference functions. This is a 64-bit count, 22 | so you don't have to worry about a reference value that was removed with \ref croc_ref_remove ever being valid 23 | again (unless you generate millions of references per second for hundreds of thousands of years). */ 24 | crocref_t croc_ref_create(CrocThread* t_, word_t idx) 25 | { 26 | auto t = Thread::from(t_); 27 | auto v = *getValue(t, idx); 28 | 29 | if(!v.isGCObject()) 30 | { 31 | croc_pushTypeString(t_, idx); 32 | croc_eh_throwStd(t_, "ApiError", "%s - Can only get references to reference types, not '%s'", 33 | __FUNCTION__, 34 | croc_getString(t_, -1)); 35 | } 36 | 37 | auto ret = t->vm->currentRef++; 38 | *t->vm->refTab.insert(t->vm->mem, ret) = v.mGCObj; 39 | return ret; 40 | } 41 | 42 | /** Given a reference value that was returned from \ref croc_ref_create, pushes the object associated with that 43 | reference onto the stack. */ 44 | word_t croc_ref_push(CrocThread* t_, crocref_t r) 45 | { 46 | auto t = Thread::from(t_); 47 | 48 | auto v = t->vm->refTab.lookup(r); 49 | 50 | if(v == nullptr) 51 | croc_eh_throwStd(t_, "ApiError", "%s - Reference '%" CROC_UINTEGER_FORMAT "' does not exist", 52 | __FUNCTION__, r); 53 | 54 | return push(t, Value::from(*v)); 55 | } 56 | 57 | /** Given a reference value that was returned from \ref croc_ref_create, removes the reference to the object. The 58 | reference value will then be invalid for the rest of the life of the program. */ 59 | void croc_ref_remove(CrocThread* t_, crocref_t r) 60 | { 61 | auto t = Thread::from(t_); 62 | 63 | if(!t->vm->refTab.remove(r)) 64 | croc_eh_throwStd(t_, "ApiError", "%s - Reference '%" CROC_UINTEGER_FORMAT "' does not exist", 65 | __FUNCTION__, r); 66 | } 67 | } -------------------------------------------------------------------------------- /src/croc/api/table.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/stack.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | /** Creates a new empty table and pushes it onto the stack. 12 | 13 | \param size is available as a hint to the allocator to preallocate this many slots in the table. Use this if you 14 | know in advance how many things you'll be adding, or if you'll be adding a ton. 15 | 16 | \returns the stack index of the pushed value. */ 17 | word_t croc_table_new(CrocThread* t_, uword_t size) 18 | { 19 | auto t = Thread::from(t_); 20 | croc_gc_maybeCollect(t_); 21 | return push(t, Value::from(Table::create(t->vm->mem, size))); 22 | } 23 | 24 | /** Removes all key-value pairs from the table at slot \c idx. */ 25 | void croc_table_clear(CrocThread* t_, word_t tab) 26 | { 27 | auto t = Thread::from(t_); 28 | API_CHECK_PARAM(tabObj, tab, Table, "tab"); 29 | tabObj->clear(t->vm->mem); 30 | } 31 | } -------------------------------------------------------------------------------- /src/croc/api/thread.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/stack.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | /** Creates and pushes a new thread object in this VM, using the script function at \c func as its main function. 12 | The new thread will be in the initial state and can be started by calling it like a function. 13 | 14 | \returns the stack index of the pushed value. */ 15 | word_t croc_thread_new(CrocThread* t_, word_t func) 16 | { 17 | auto t = Thread::from(t_); 18 | API_CHECK_PARAM(f, func, Function, "thread function"); 19 | 20 | if(f->isNative) 21 | croc_eh_throwStd(t_, "ValueError", "%s - Native functions may not be used as the body of a thread", 22 | __FUNCTION__); 23 | 24 | croc_gc_maybeCollect(t_); 25 | auto nt = Thread::create(t->vm, f); 26 | nt->setHookFunc(t->vm->mem, t->hookFunc); 27 | nt->hooks = t->hooks; 28 | nt->hookDelay = t->hookDelay; 29 | nt->hookCounter = t->hookCounter; 30 | return croc_pushThread(t_, *nt); 31 | } 32 | 33 | /** \returns the execution state of the given thread. */ 34 | CrocThreadState croc_thread_getState(CrocThread* t_) 35 | { 36 | return Thread::from(t_)->state; 37 | } 38 | 39 | /** \returns a string representation of the execution state of the given thread. This is a constant string, so it's 40 | safe to store a pointer to it. */ 41 | const char* croc_thread_getStateString(CrocThread* t_) 42 | { 43 | return ThreadStateStrings[Thread::from(t_)->state]; 44 | } 45 | 46 | /** \returns the call depth of the given thread. This is how many function calls are on the call stack. Note that 47 | there can be more calls than are "actually" on it, since this function also counts tailcalls. */ 48 | uword_t croc_thread_getCallDepth(CrocThread* t_) 49 | { 50 | auto t = Thread::from(t_); 51 | 52 | uword depth = 0; 53 | 54 | for(uword i = 0; i < t->arIndex; i++) 55 | depth += t->actRecs[i].numTailcalls + 1; 56 | 57 | return depth; 58 | } 59 | 60 | /** Resets a dead thread at \c slot to the initial state, keeping the same main function. */ 61 | void croc_thread_reset(CrocThread* t_, word_t slot) 62 | { 63 | auto t = Thread::from(t_); 64 | API_CHECK_PARAM(other, slot, Thread, "slot"); 65 | 66 | // This shouldn't be possible, but it can't hurt to check 67 | if(t->vm != other->vm) 68 | croc_eh_throwStd(t_, "ValueError", "%s - Attempting to reset a thread that belongs to a different VM", 69 | __FUNCTION__); 70 | 71 | if(other->state != CrocThreadState_Dead) 72 | croc_eh_throwStd(t_, "StateError", "%s - Attempting to reset a %s thread (must be dead)", 73 | __FUNCTION__, ThreadStateStrings[other->state]); 74 | 75 | other->reset(); 76 | } 77 | 78 | /** Resets a dead thread at \c slot to the initial state, but changes its main function to the script function that 79 | is on top of the stack. The function is popped. */ 80 | void croc_thread_resetWithFunc(CrocThread* t_, word_t slot) 81 | { 82 | auto t = Thread::from(t_); 83 | API_CHECK_NUM_PARAMS(1); 84 | API_CHECK_PARAM(other, slot, Thread, "slot"); 85 | API_CHECK_PARAM(f, -1, Function, "thread function"); 86 | 87 | // This shouldn't be possible, but it can't hurt to check 88 | if(t->vm != other->vm) 89 | croc_eh_throwStd(t_, "ValueError", "%s - Attempting to reset a thread that belongs to a different VM", 90 | __FUNCTION__); 91 | 92 | if(other->state != CrocThreadState_Dead) 93 | croc_eh_throwStd(t_, "StateError", "%s - Attempting to reset a %s thread (must be dead)", 94 | __FUNCTION__, ThreadStateStrings[other->state]); 95 | 96 | if(f->isNative) 97 | croc_eh_throwStd(t_, "ValueError", "%s - Native functions may not be used as the body of a thread", 98 | __FUNCTION__); 99 | 100 | other->setCoroFunc(t->vm->mem, f); 101 | croc_popTop(t_); 102 | other->reset(); 103 | } 104 | 105 | /** Halts the given thread. If the thread is currently running, immediately throws a \c HaltException on it. 106 | Otherwise, it places a pending halt on the thread (see \ref croc_thread_pendingHalt). */ 107 | void croc_thread_halt(CrocThread* t_) 108 | { 109 | auto t = Thread::from(t_); 110 | 111 | if(t->state == CrocThreadState_Running) 112 | croc_eh_throwStd(t_, "HaltException", "Thread halted"); 113 | else 114 | croc_thread_pendingHalt(t_); 115 | } 116 | 117 | /** Places a pending halt on the thread. The thread will not halt immediately, but as soon as it begins executing 118 | script code, it will. */ 119 | void croc_thread_pendingHalt(CrocThread* t_) 120 | { 121 | auto t = Thread::from(t_); 122 | 123 | if(t->state != CrocThreadState_Dead && t->arIndex > 0) 124 | t->shouldHalt = true; 125 | } 126 | 127 | /** \returns nonzero if there is a pending halt on the given thread. */ 128 | int croc_thread_hasPendingHalt(CrocThread* t_) 129 | { 130 | return Thread::from(t_)->shouldHalt; 131 | } 132 | } -------------------------------------------------------------------------------- /src/croc/api/typequeries.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/basic.hpp" 5 | #include "croc/internal/stack.hpp" 6 | #include "croc/types/base.hpp" 7 | 8 | using namespace croc; 9 | 10 | extern "C" 11 | { 12 | /** \returns a member of the \ref CrocType enumeration telling what type of value is in the given stack slot. */ 13 | CrocType croc_type(CrocThread* t, word_t slot) 14 | { 15 | return getValue(Thread::from(t), slot)->type; 16 | } 17 | 18 | /** \returns nonzero if the given stack slot holds either an int or a float, and 0 otherwise. */ 19 | int croc_isNum(CrocThread* t, word_t slot) 20 | { 21 | auto v = getValue(Thread::from(t), slot); 22 | return v->type == CrocType_Int || v->type == CrocType_Float; 23 | } 24 | 25 | /** \returns nonzero if the given stack slot holds a string that is exactly one codepoint long, and 0 otherwise. */ 26 | int croc_isChar(CrocThread* t, word_t slot) 27 | { 28 | auto v = getValue(Thread::from(t), slot); 29 | return v->type == CrocType_String && v->mString->cpLength == 1; 30 | } 31 | 32 | /** Pushes a nice string representation of the type of the value in \c slot, which is useful for error messages. 33 | This is equivalent to Croc's \c niceTypeof library function. */ 34 | word_t croc_pushTypeString(CrocThread* t_, word_t slot) 35 | { 36 | auto t = Thread::from(t_); 37 | return pushTypeStringImpl(t, *getValue(t, slot)); 38 | } 39 | } -------------------------------------------------------------------------------- /src/croc/api/variables.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/calls.hpp" 5 | #include "croc/internal/stack.hpp" 6 | #include "croc/internal/variables.hpp" 7 | #include "croc/types/base.hpp" 8 | 9 | using namespace croc; 10 | 11 | extern "C" 12 | { 13 | /** Pushes the environment namespace of the function at the given call stack depth. A \c depth of 0 means the 14 | currently-executing function; 1 means the function which called this one; and so on. If you pass a depth greater 15 | than the call stack depth, pushes the global namespace instead. 16 | 17 | It is an error to get the environment of a call stack index which was overwritten by a tailcall. 18 | 19 | \returns the stack slot of the pushed value. */ 20 | word_t croc_pushEnvironment(CrocThread* t_, uword_t depth) 21 | { 22 | auto t = Thread::from(t_); 23 | return push(t, Value::from(getEnv(t, depth))); 24 | } 25 | 26 | /** Inside a native function which had upvalues associated with it at creation, you can use this to set the upvalue 27 | with the given index. 28 | 29 | This expects one value on top of the stack. That value is set as the new value of upvalue \c idx, and then the value 30 | is popped. */ 31 | void croc_setUpval(CrocThread* t_, uword_t idx) 32 | { 33 | auto t = Thread::from(t_); 34 | 35 | if(t->arIndex == 0) 36 | croc_eh_throwStd(t_, "ApiError", "%s - No function to set upvalue (can't call this function at top level)", 37 | __FUNCTION__); 38 | 39 | API_CHECK_NUM_PARAMS(1); 40 | 41 | auto func = t->currentAR->func; 42 | 43 | if(idx >= func->nativeUpvals().length) 44 | croc_eh_throwStd(t_, "BoundsError", 45 | "%s - Invalid upvalue index %" CROC_SIZE_T_FORMAT " (only have %" CROC_SIZE_T_FORMAT ")", 46 | __FUNCTION__, idx, func->nativeUpvals().length); 47 | 48 | func->setNativeUpval(t->vm->mem, idx, *getValue(t, -1)); 49 | croc_popTop(t_); 50 | } 51 | 52 | /** Inside a native function which had upvalues associated with it at creation, pushes the upvalue with the given 53 | index. 54 | 55 | \returns the stack slot of the pushed value. */ 56 | word_t croc_pushUpval(CrocThread* t_, uword_t idx) 57 | { 58 | auto t = Thread::from(t_); 59 | 60 | if(t->arIndex == 0) 61 | croc_eh_throwStd(t_, "ApiError", "%s - No function to get upvalue (can't call this function at top level)", 62 | __FUNCTION__); 63 | 64 | // It is impossible for this function to be called from script code, because the only way that could happen is 65 | // if the interpreter itself called it. 66 | assert(t->currentAR->func->isNative); 67 | 68 | auto upvals = t->currentAR->func->nativeUpvals(); 69 | 70 | if(idx >= upvals.length) 71 | croc_eh_throwStd(t_, "BoundsError", 72 | "%s - Invalid upvalue index %" CROC_SIZE_T_FORMAT " (only have %" CROC_SIZE_T_FORMAT ")", 73 | __FUNCTION__, idx, upvals.length); 74 | 75 | return push(t, upvals[idx]); 76 | } 77 | 78 | /** Expects a value on top of the stack. Pops the value and creates a new global named \c name in the current 79 | function's environment, just like declaring a global in Croc. */ 80 | void croc_newGlobal(CrocThread* t_, const char* name) 81 | { 82 | auto t = Thread::from(t_); 83 | API_CHECK_NUM_PARAMS(1); 84 | croc_pushString(t_, name); 85 | croc_swapTop(t_); 86 | croc_newGlobalStk(t_); 87 | } 88 | 89 | /** Expects two values on top of the stack: the value on top, and the name of the global to create below that. 90 | Creates the global and pops both values. */ 91 | void croc_newGlobalStk(CrocThread* t_) 92 | { 93 | auto t = Thread::from(t_); 94 | API_CHECK_NUM_PARAMS(2); 95 | API_CHECK_PARAM(name, -2, String, "global name"); 96 | newGlobalImpl(t, name, getEnv(t), t->stack[t->stackIndex - 1]); 97 | croc_pop(t_, 2); 98 | } 99 | 100 | /** Pushes the value of the global variable named \c name, just like accessing a global in Croc. 101 | 102 | \returns the stack slot of the pushed value. */ 103 | word_t croc_pushGlobal(CrocThread* t_, const char* name) 104 | { 105 | croc_pushString(t_, name); 106 | return croc_pushGlobalStk(t_); 107 | } 108 | 109 | /** Expects a string on top of the stack as the name of the global to get. Replaces the top of the stack with the 110 | value of the global. 111 | 112 | \returns the stack slot of the pushed value. */ 113 | word_t croc_pushGlobalStk(CrocThread* t_) 114 | { 115 | auto t = Thread::from(t_); 116 | API_CHECK_NUM_PARAMS(1); 117 | API_CHECK_PARAM(name, -1, String, "global name"); 118 | t->stack[t->stackIndex - 1] = getGlobalImpl(t, name, getEnv(t)); 119 | return croc_getStackSize(t_) - 1; 120 | } 121 | 122 | /** Expects a value on top of the stack. Pops the value and assigns it into the global named \c name in the current 123 | function's environment, just like setting a global in Croc. */ 124 | void croc_setGlobal(CrocThread* t_, const char* name) 125 | { 126 | auto t = Thread::from(t_); 127 | API_CHECK_NUM_PARAMS(1); 128 | croc_pushString(t_, name); 129 | croc_swapTop(t_); 130 | croc_setGlobalStk(t_); 131 | } 132 | 133 | /** Expects two values on top of the stack: the value on top, and the name of the global to set below that. Sets 134 | the global and pops both values. */ 135 | void croc_setGlobalStk(CrocThread* t_) 136 | { 137 | auto t = Thread::from(t_); 138 | API_CHECK_NUM_PARAMS(2); 139 | API_CHECK_PARAM(name, -2, String, "global name"); 140 | setGlobalImpl(t, name, getEnv(t), t->stack[t->stackIndex - 1]); 141 | croc_pop(t_, 2); 142 | } 143 | } -------------------------------------------------------------------------------- /src/croc/api/weakref.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/stack.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | /** Creates and pushes a weakref from the value at slot \c idx. If the value is a value type or quasi-value type, 12 | the value will just be duplicated. Otherwise, a weakref object will be created (if one hasn't been created for that 13 | object already) and pushed. This mirrors the behavior of the Croc stdlib \c weakref function. 14 | 15 | \returns the stack index of the pushed value. */ 16 | word_t croc_weakref_push(CrocThread* t_, word_t idx) 17 | { 18 | auto t = Thread::from(t_); 19 | return push(t, Weakref::makeref(t->vm, *getValue(t, idx))); 20 | } 21 | 22 | /** Given a weakref (or a value of any value or quasi-value type) at slot \c idx, pushes the referenced object. If 23 | the value is a value or quasi-value type, just duplicates the value. Otherwise, it must be a weakref object, and the 24 | weakref's referent will be pushed (or null if it was collected). This mirrors the behavior of the Croc stdlib \c 25 | deref function. 26 | 27 | \returns the stack index of the pushed value. */ 28 | word_t croc_weakref_deref(CrocThread* t_, word_t idx) 29 | { 30 | auto t = Thread::from(t_); 31 | 32 | switch(croc_type(t_, idx)) 33 | { 34 | case CrocType_Null: 35 | case CrocType_Bool: 36 | case CrocType_Int: 37 | case CrocType_Float: 38 | case CrocType_String: 39 | case CrocType_Nativeobj: 40 | case CrocType_Upval: 41 | return croc_dup(t_, idx); 42 | 43 | case CrocType_Weakref: 44 | if(auto o = getValue(t, idx)->mWeakref->obj) 45 | return push(t, Value::from(o)); 46 | else 47 | return croc_pushNull(t_); 48 | 49 | default: 50 | API_PARAM_TYPE_ERROR(idx, "value", "null|bool|int|float|string|nativeobj|weakref"); 51 | assert(false); 52 | return 0; // dummy 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/croc/base/deque.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_BASE_DEQUE_HPP 2 | #define CROC_BASE_DEQUE_HPP 3 | 4 | #include 5 | 6 | #include "croc/base/gcobject.hpp" 7 | // #include "croc/base/memory.hpp" 8 | #include "croc/base/sanity.hpp" 9 | 10 | namespace croc 11 | { 12 | struct Memory; 13 | 14 | struct Deque 15 | { 16 | private: 17 | GCObject** mDataPtr; 18 | size_t mDataLen; 19 | size_t mStart; 20 | size_t mEnd; 21 | size_t mSize; 22 | 23 | public: 24 | void init(); 25 | void prealloc(Memory& mem, size_t size); 26 | void add(Memory& mem, GCObject* t); 27 | GCObject* remove(); 28 | void append(Memory& mem, GCObject** ptr, size_t len); 29 | void append(Memory& mem, Deque& other); 30 | 31 | inline bool isEmpty() const { return mSize == 0; } 32 | inline bool isFull() const { return mSize == mDataLen; } 33 | inline size_t length() const { return mSize; } 34 | inline size_t capacity() const { return mDataLen; } 35 | 36 | void reset(); 37 | void clear(Memory& mem); 38 | void minimize(Memory& mem); 39 | 40 | void foreach(std::function dg); 41 | 42 | struct Iterator 43 | { 44 | friend class Deque; 45 | 46 | private: 47 | Deque* mDeque; 48 | size_t mIdx; 49 | bool mDead; 50 | #ifndef NDEBUG 51 | size_t mStartSize, mStartLength; 52 | #endif 53 | Iterator(Deque* d); 54 | 55 | public: 56 | inline bool hasNext() const { return !mDead; } 57 | 58 | GCObject* next(); 59 | void removeCurrent(); 60 | }; 61 | 62 | Iterator iterator(); 63 | 64 | private: 65 | void enlargeArray(Memory& mem); 66 | void resizeArray(Memory& mem, size_t newSize); 67 | }; 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/croc/base/gc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_BASE_GC_HPP 2 | #define CROC_BASE_GC_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | typedef enum GCCycleType 9 | { 10 | GCCycleType_Normal, 11 | GCCycleType_Full, 12 | GCCycleType_NoRoots 13 | } GCCycleType; 14 | 15 | void gcCycle(VM* vm, GCCycleType cycleType); 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/croc/base/gcobject.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_BASE_GCOBJECT_HPP 2 | #define CROC_BASE_GCOBJECT_HPP 3 | 4 | #include "croc/apitypes.h" 5 | #include "croc/base/sanity.hpp" 6 | 7 | #ifdef CROC_LEAK_DETECTOR 8 | # define ALLOC_OBJ(mem, type) cast(type*)mem.allocate(sizeof(type), false, typeid(type)) 9 | # define ALLOC_OBJ_ACYC(mem, type) cast(type*)mem.allocate(sizeof(type), true, typeid(type)) 10 | # define ALLOC_OBJSZ(mem, type, extra) cast(type*)mem.allocate(sizeof(type) + (extra), false, typeid(type)) 11 | # define ALLOC_OBJSZ_ACYC(mem, type, extra) cast(type*)mem.allocate(sizeof(type) + (extra), true, typeid(type)) 12 | # define ALLOC_OBJ_FINAL(mem, type) cast(type*)mem.allocateFinalizable(sizeof(type), typeid(type)) 13 | # define ALLOC_OBJSZ_FINAL(mem, type, extra) cast(type*)mem.allocateFinalizable(sizeof(type) + (extra), typeid(type)) 14 | # define FREE_OBJ(mem, type, ptr) mem.free((ptr), typeid(type)) 15 | #else 16 | # define ALLOC_OBJ(mem, type) cast(type*)mem.allocate(sizeof(type), false) 17 | # define ALLOC_OBJ_ACYC(mem, type) cast(type*)mem.allocate(sizeof(type), true) 18 | # define ALLOC_OBJSZ(mem, type, extra) cast(type*)mem.allocate(sizeof(type) + (extra), false) 19 | # define ALLOC_OBJSZ_ACYC(mem, type, extra) cast(type*)mem.allocate(sizeof(type) + (extra), true) 20 | # define ALLOC_OBJ_FINAL(mem, type) cast(type*)mem.allocateFinalizable(sizeof(type) ) 21 | # define ALLOC_OBJSZ_FINAL(mem, type, extra) cast(type*)mem.allocateFinalizable(sizeof(type) + (extra)) 22 | # define FREE_OBJ(mem, type, ptr) mem.free((ptr)) 23 | #endif 24 | 25 | #define GCOBJ_UNLOGGED(o) TEST_FLAG((o)->gcflags, GCFlags_Unlogged) 26 | #define GCOBJ_LOG(o) CLEAR_FLAG((o)->gcflags, GCFlags_Unlogged) 27 | #define GCOBJ_UNLOG(o) SET_FLAG((o)->gcflags, GCFlags_Unlogged) 28 | 29 | #define GCOBJ_INRC(o) TEST_FLAG((o)->gcflags, GCFlags_InRC) 30 | #define GCOBJ_TORC(o) SET_FLAG((o)->gcflags, GCFlags_InRC | GCFlags_JustMoved | GCFlags_Unlogged) 31 | #define GCOBJ_JUSTMOVED(o) TEST_FLAG((o)->gcflags, GCFlags_JustMoved) 32 | #define GCOBJ_CLEARJUSTMOVED(o) CLEAR_FLAG((o)->gcflags, GCFlags_JustMoved) 33 | 34 | #define GCOBJ_COLOR(o) ((o)->gcflags & GCFlags_ColorMask) 35 | #define GCOBJ_SETCOLOR(o, c) ((o)->gcflags = ((o)->gcflags & ~GCFlags_ColorMask) | (c)) 36 | 37 | #define GCOBJ_CYCLELOGGED(o) TEST_FLAG((o)->gcflags, GCFlags_CycleLogged) 38 | #define GCOBJ_CYCLELOG(o) SET_FLAG((o)->gcflags, GCFlags_CycleLogged) 39 | #define GCOBJ_CYCLEUNLOG(o) CLEAR_FLAG((o)->gcflags, GCFlags_CycleLogged) 40 | 41 | #define GCOBJ_FINALIZABLE(o) TEST_FLAG((o)->gcflags, GCFlags_Finalizable) 42 | #define GCOBJ_FINALIZED(o) TEST_FLAG((o)->gcflags, GCFlags_Finalized) 43 | #define GCOBJ_SETFINALIZED(o) SET_FLAG((o)->gcflags, GCFlags_Finalized) 44 | 45 | namespace croc 46 | { 47 | enum GCFlags 48 | { 49 | GCFlags_Unlogged = (1 << 0), // 0b0_00000001 50 | GCFlags_InRC = (1 << 1), // 0b0_00000010 51 | 52 | GCFlags_Black = (0 << 2), // 0b0_00000000 53 | GCFlags_Grey = (1 << 2), // 0b0_00000100 54 | GCFlags_White = (2 << 2), // 0b0_00001000 55 | GCFlags_Purple = (3 << 2), // 0b0_00001100 56 | GCFlags_Green = (4 << 2), // 0b0_00010000 57 | GCFlags_ColorMask = (7 << 2), // 0b0_00011100 58 | 59 | GCFlags_CycleLogged = (1 << 5), // 0b0_00100000 60 | 61 | GCFlags_Finalizable = (1 << 6), // 0b0_01000000 62 | GCFlags_Finalized = (1 << 7), // 0b0_10000000 63 | 64 | GCFlags_JustMoved = (1 << 8) // 0b1_00000000 65 | }; 66 | 67 | struct GCObject 68 | { 69 | uint32_t gcflags; 70 | uint32_t refCount; 71 | size_t memSize; 72 | CrocType type; 73 | }; 74 | } 75 | 76 | #endif -------------------------------------------------------------------------------- /src/croc/base/leakdetector.cpp: -------------------------------------------------------------------------------- 1 | #ifdef CROC_LEAK_DETECTOR 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __GNUC__ 7 | # include 8 | #endif 9 | 10 | #include "croc/base/leakdetector.hpp" 11 | #include "croc/base/sanity.hpp" 12 | #include "croc/base/gcobject.hpp" 13 | 14 | namespace croc 15 | { 16 | namespace 17 | { 18 | void dumpTypeName(const std::type_info* ti) 19 | { 20 | #ifdef __GNUC__ 21 | // gcc returns a mangled name which we have to demangle so it doesn't look like ass! 22 | int status; 23 | char* realname = abi::__cxa_demangle(ti->name(), 0, 0, &status); 24 | 25 | if(status < 0) 26 | fprintf(stderr, "\n"); 27 | else 28 | fprintf(stderr, "%s", realname); 29 | 30 | free(realname); 31 | #else 32 | fprintf(stderr, "%s", ti->name()); 33 | #endif 34 | } 35 | 36 | void invalidFree(const std::type_info& ti) 37 | { 38 | fprintf(stderr, "AWFUL: You're trying to free something that wasn't allocated on the Croc Heap, or are" 39 | " performing a double free! It's of type "); 40 | 41 | dumpTypeName(&ti); 42 | fprintf(stderr, "\n"); 43 | assert(false); 44 | } 45 | 46 | void invalidResize(const std::type_info& ti) 47 | { 48 | fprintf(stderr, 49 | "AWFUL: You're trying to resize an array that wasn't allocated on the Croc Heap! It's of type "); 50 | 51 | dumpTypeName(&ti); 52 | fprintf(stderr, "\n"); 53 | assert(false); 54 | } 55 | 56 | void dumpBlock(void* ptr, LeakMemBlock& block, bool raw) 57 | { 58 | fprintf(stderr, " address %p, ", ptr); 59 | 60 | if(!raw) 61 | { 62 | GCObject* obj = cast(GCObject*)ptr; 63 | fprintf(stderr, "refcount %u, flags %03x, ", obj->refCount, obj->gcflags); 64 | } 65 | 66 | fprintf(stderr, "length %" CROC_SIZE_T_FORMAT " bytes, type ", block.len); 67 | dumpTypeName(block.ti); 68 | fprintf(stderr, "\n"); 69 | } 70 | 71 | void dumpList(LeakDetector::BlockMap& blocks, const char* name, bool raw) 72 | { 73 | if(blocks.size() > 0) 74 | { 75 | fprintf(stderr, "Unfreed %s blocks:\n", name); 76 | 77 | for(LeakDetector::iter i = blocks.begin(); i != blocks.end(); i++) 78 | dumpBlock((*i).first, (*i).second, raw); 79 | } 80 | } 81 | } 82 | 83 | void LeakDetector::init() 84 | { 85 | nurseryBlocks = BlockMap(); 86 | rcBlocks = BlockMap(); 87 | rawBlocks = BlockMap(); 88 | } 89 | 90 | void LeakDetector::cleanup() 91 | { 92 | nurseryBlocks.clear(); 93 | rcBlocks.clear(); 94 | rawBlocks.clear(); 95 | } 96 | 97 | void LeakDetector::newRaw(void* ptr, size_t size TYPEID_PARAM) 98 | { 99 | LeakMemBlock& n = rawBlocks[ptr]; 100 | n.len = size; 101 | n.ti = &ti; 102 | } 103 | 104 | void LeakDetector::newNursery(void* ptr, size_t size TYPEID_PARAM) 105 | { 106 | LeakMemBlock& n = nurseryBlocks[ptr]; 107 | n.len = size; 108 | n.ti = &ti; 109 | } 110 | 111 | void LeakDetector::newRC(void* ptr, size_t size TYPEID_PARAM) 112 | { 113 | LeakMemBlock& n = rcBlocks[ptr]; 114 | n.len = size; 115 | n.ti = &ti; 116 | } 117 | 118 | void LeakDetector::freeRaw(void* ptr TYPEID_PARAM) 119 | { 120 | if(rawBlocks.erase(ptr) == 0) 121 | invalidFree(ti); 122 | } 123 | 124 | void LeakDetector::freeNursery(void* obj TYPEID_PARAM) 125 | { 126 | if(nurseryBlocks.erase(obj) == 0) 127 | invalidFree(ti); 128 | } 129 | 130 | void LeakDetector::freeRC(void* obj TYPEID_PARAM) 131 | { 132 | if(rcBlocks.erase(obj) == 0) 133 | invalidFree(ti); 134 | } 135 | 136 | void LeakDetector::checkRawExists(void* ptr TYPEID_PARAM) 137 | { 138 | LeakDetector::iter i = rawBlocks.find(ptr); 139 | 140 | if(i == rawBlocks.end()) 141 | invalidResize(ti); 142 | } 143 | 144 | void LeakDetector::relocateRaw(void* oldPtr, void* newPtr, size_t newSize TYPEID_PARAM) 145 | { 146 | if(oldPtr == newPtr) 147 | rawBlocks[oldPtr].len = newSize; 148 | else 149 | { 150 | rawBlocks.erase(oldPtr); 151 | newRaw(newPtr, newSize, ti); 152 | } 153 | } 154 | 155 | void LeakDetector::clearNursery() 156 | { 157 | nurseryBlocks.clear(); 158 | } 159 | 160 | void LeakDetector::makeRC(void* obj) 161 | { 162 | LeakDetector::iter i = nurseryBlocks.find(obj); 163 | assert(i != nurseryBlocks.end()); 164 | rcBlocks[obj] = (*i).second; 165 | } 166 | 167 | void LeakDetector::dumpBlocks() 168 | { 169 | dumpList(nurseryBlocks, "nursery", false); 170 | dumpList(rcBlocks, "RC", false); 171 | dumpList(rawBlocks, "raw", true); 172 | } 173 | } 174 | 175 | #endif -------------------------------------------------------------------------------- /src/croc/base/leakdetector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_BASE_LEAKDETECTOR_HPP 2 | #define CROC_BASE_LEAKDETECTOR_HPP 3 | 4 | #ifndef CROC_LEAK_DETECTOR 5 | # define TYPEID_PARAM 6 | # define TYPEID_ARG 7 | # define LEAK_DETECT(x) 8 | #else // else, rest of module! 9 | 10 | #include 11 | #include 12 | 13 | #include "croc/base/sanity.hpp" 14 | 15 | #define TYPEID_PARAM ,const std::type_info& ti 16 | #define TYPEID_ARG ,ti 17 | #define LEAK_DETECT(x) x 18 | 19 | namespace croc 20 | { 21 | struct LeakMemBlock 22 | { 23 | size_t len; 24 | const std::type_info* ti; 25 | }; 26 | 27 | struct LeakDetector 28 | { 29 | typedef std::map BlockMap; 30 | typedef BlockMap::iterator iter; 31 | 32 | BlockMap nurseryBlocks; 33 | BlockMap rcBlocks; 34 | BlockMap rawBlocks; 35 | 36 | void init(); 37 | void cleanup(); 38 | 39 | void newRaw(void* ptr, size_t size TYPEID_PARAM); 40 | void newNursery(void* ptr, size_t size TYPEID_PARAM); 41 | void newRC(void* ptr, size_t size TYPEID_PARAM); 42 | 43 | void freeRaw(void* ptr TYPEID_PARAM); 44 | void freeNursery(void* obj TYPEID_PARAM); 45 | void freeRC(void* obj TYPEID_PARAM); 46 | 47 | void checkRawExists(void* ptr TYPEID_PARAM); 48 | void relocateRaw(void* oldPtr, void* newPtr, size_t newSize TYPEID_PARAM); 49 | void clearNursery(); 50 | void makeRC(void* obj); 51 | 52 | void dumpBlocks(); 53 | }; 54 | } 55 | 56 | #endif 57 | #endif -------------------------------------------------------------------------------- /src/croc/base/memory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_BASE_MEMORY_HPP 2 | #define CROC_BASE_MEMORY_HPP 3 | 4 | #include "croc/apitypes.h" 5 | #include "croc/base/deque.hpp" 6 | #include "croc/base/gcobject.hpp" 7 | #include "croc/base/leakdetector.hpp" 8 | #include "croc/base/sanity.hpp" 9 | 10 | namespace croc 11 | { 12 | struct Memory 13 | { 14 | CrocMemFunc memFunc; 15 | void* ctx; 16 | 17 | Deque modBuffer; 18 | Deque decBuffer; 19 | Deque nursery; 20 | 21 | // 0 for enabled, positive for disabled 22 | size_t gcDisabled; 23 | size_t totalBytes; 24 | size_t nurseryBytes; 25 | size_t nurseryLimit; 26 | size_t metadataLimit; 27 | size_t nurserySizeCutoff; 28 | size_t cycleCollectCountdown; 29 | size_t nextCycleCollect; 30 | size_t cycleMetadataLimit; 31 | LEAK_DETECT(LeakDetector leaks;) 32 | 33 | void init(CrocMemFunc func, void* context); 34 | 35 | inline bool couldUseGC() const 36 | { 37 | return 38 | nurseryBytes >= nurseryLimit || 39 | (modBuffer.length() + decBuffer.length()) * sizeof(GCObject*) >= metadataLimit; 40 | } 41 | 42 | void resizeNurserySpace(size_t newSize); 43 | void clearNurserySpace(); 44 | void cleanup(); 45 | 46 | GCObject* allocate(size_t size, bool acyclic TYPEID_PARAM); 47 | GCObject* allocateFinalizable(size_t size TYPEID_PARAM); 48 | void makeRC(GCObject* obj); 49 | void free(GCObject* o TYPEID_PARAM); 50 | 51 | void* allocRaw(size_t size TYPEID_PARAM); 52 | void resizeRaw(void*& ptr, size_t& len, size_t newLen TYPEID_PARAM); 53 | void freeRaw(void*& ptr, size_t& len TYPEID_PARAM); 54 | 55 | private: 56 | GCObject* allocateRC(size_t size, bool acyclic TYPEID_PARAM); 57 | GCObject* allocateGCObject(size_t size, bool acyclic, uint32_t gcflags); 58 | void* realloc(void* p, size_t oldSize, size_t newSize); 59 | }; 60 | } 61 | 62 | #endif -------------------------------------------------------------------------------- /src/croc/base/metamethods.cpp: -------------------------------------------------------------------------------- 1 | #include "croc/base/metamethods.hpp" 2 | 3 | namespace croc 4 | { 5 | #define POOP(_, x) x 6 | const char* MetaNames[] = 7 | { 8 | METAMETHOD_LIST(POOP) 9 | }; 10 | #undef POOP 11 | } -------------------------------------------------------------------------------- /src/croc/base/metamethods.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_BASE_METAMETHODS_HPP 2 | #define CROC_BASE_METAMETHODS_HPP 3 | 4 | #include "croc/base/sanity.hpp" 5 | 6 | namespace croc 7 | { 8 | #define METAMETHOD_LIST(X)\ 9 | X(MM_Cat, "opCat" ),\ 10 | X(MM_CatEq, "opCatAssign" ),\ 11 | X(MM_Cat_r, "opCat_r" ),\ 12 | X(MM_Index, "opIndex" ),\ 13 | X(MM_IndexAssign, "opIndexAssign" ),\ 14 | X(MM_Slice, "opSlice" ),\ 15 | X(MM_SliceAssign, "opSliceAssign" ),\ 16 | X(MM_Field, "opField" ),\ 17 | X(MM_FieldAssign, "opFieldAssign" ),\ 18 | X(MM_Length, "opLength" ),\ 19 | X(MM_LengthAssign, "opLengthAssign"),\ 20 | X(MM_Cmp, "opCmp" ),\ 21 | X(MM_Equals, "opEquals" ),\ 22 | X(MM_Call, "opCall" ),\ 23 | X(MM_Method, "opMethod" ),\ 24 | X(MM_Apply, "opApply" ),\ 25 | X(MM_In, "opIn" ),\ 26 | X(MM_ToString, "toString" ) 27 | 28 | #define POOP(x, _) x 29 | typedef enum Metamethod 30 | { 31 | METAMETHOD_LIST(POOP), 32 | 33 | MM_NUMMETAMETHODS 34 | } Metamethod; 35 | #undef POOP 36 | 37 | extern const char* MetaNames[]; 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /src/croc/base/opcodes.cpp: -------------------------------------------------------------------------------- 1 | #include "croc/base/opcodes.hpp" 2 | 3 | namespace croc 4 | { 5 | #define POOP(x) #x 6 | 7 | const char* OpNames[] = 8 | { 9 | INSTRUCTION_LIST(POOP) 10 | }; 11 | #undef POOP 12 | } 13 | -------------------------------------------------------------------------------- /src/croc/base/sanity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_BASE_SANITY_HPP 2 | #define CROC_BASE_SANITY_HPP 3 | 4 | // Really basic stdlibs used everywhere. 5 | #include 6 | #include 7 | #include 8 | 9 | #define cast(x) (x) 10 | #define TEST_FLAG(o, f) (((o) & (f)) != 0) 11 | #define SET_FLAG(o, f) ((o) |= (f)) 12 | #define CLEAR_FLAG(o, f) ((o) &= ~(f)) 13 | 14 | #ifndef NDEBUG 15 | #include 16 | #define DBGPRINT(...) printf(__VA_ARGS__) 17 | #else 18 | #define DBGPRINT(...) 19 | #endif 20 | 21 | // TODO: make a config header file, like Lua 22 | #ifdef _WIN32 23 | #define CROC_INTEGER_FORMAT "I64d" 24 | #define CROC_UINTEGER_FORMAT "I64u" 25 | #define CROC_HEX64_FORMAT "I64x" 26 | 27 | #ifdef _WIN64 28 | # define CROC_SIZE_T_FORMAT "I64u" 29 | # define CROC_SSIZE_T_FORMAT "I64d" 30 | #else 31 | # define CROC_SIZE_T_FORMAT "u" 32 | # define CROC_SSIZE_T_FORMAT "d" 33 | #endif 34 | 35 | #else 36 | // Who thought "yeah let's make the same integer type use different print specifiers depending on your platform" made 37 | // any 38 | // fucking 39 | // sense 40 | // whatsoever 41 | #if CROC_BUILD_BITS == 64 42 | #define CROC_INTEGER_FORMAT "ld" 43 | #define CROC_UINTEGER_FORMAT "lu" 44 | #define CROC_HEX64_FORMAT "lx" 45 | #else 46 | #define CROC_INTEGER_FORMAT "lld" 47 | #define CROC_UINTEGER_FORMAT "llu" 48 | #define CROC_HEX64_FORMAT "llx" 49 | #endif 50 | 51 | #define CROC_SIZE_T_FORMAT "zu" 52 | #define CROC_SSIZE_T_FORMAT "zd" 53 | // #define CROC_SIZE_T_FORMAT "u" 54 | // #define CROC_SSIZE_T_FORMAT "d" 55 | #endif 56 | 57 | #define CROC_FORMAT_BUF_SIZE 256 58 | 59 | #endif -------------------------------------------------------------------------------- /src/croc/base/writebarrier.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_WRITEBARRIER_HPP 2 | #define CROC_WRITEBARRIER_HPP 3 | 4 | // #ifndef NDEBUG 5 | // #include 6 | // #endif 7 | 8 | #include 9 | 10 | #include "croc/base/memory.hpp" 11 | #include "croc/base/gcobject.hpp" 12 | #include "croc/types/base.hpp" 13 | 14 | #define WRITE_BARRIER(mem, srcObj)\ 15 | assert((srcObj)->type != CrocType_Array && (srcObj)->type != CrocType_Table);\ 16 | if(GCOBJ_UNLOGGED((srcObj)))\ 17 | writeBarrierSlow((mem), (srcObj)); 18 | 19 | #define CONTAINER_WRITE_BARRIER(mem, srcObj)\ 20 | if(GCOBJ_UNLOGGED((srcObj)))\ 21 | {\ 22 | (mem).modBuffer.add((mem), (srcObj));\ 23 | GCOBJ_LOG((srcObj));\ 24 | } 25 | 26 | namespace croc 27 | { 28 | typedef std::function WBCallback; 29 | 30 | void writeBarrierSlow(Memory& mem, GCObject* srcObj); 31 | void visitRoots(VM* vm, WBCallback callback); 32 | void visitObj(GCObject* o, bool isModifyPhase, WBCallback callback); 33 | } 34 | 35 | #endif -------------------------------------------------------------------------------- /src/croc/compiler/ast.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/compiler/ast.hpp" 3 | #include "croc/compiler/types.hpp" 4 | 5 | namespace croc 6 | { 7 | const char* AstTagNames[AstTag_NUMBER] = 8 | { 9 | #define POOP(Tag, _, __) #Tag, 10 | AST_LIST(POOP) 11 | #undef POOP 12 | }; 13 | 14 | const char* NiceAstTagNames[AstTag_NUMBER] = 15 | { 16 | #define POOP(_, NiceName, __) NiceName, 17 | AST_LIST(POOP) 18 | #undef POOP 19 | }; 20 | 21 | bool Expression::hasSideEffects() 22 | { 23 | switch(type) 24 | { 25 | case AstTag_CondExp: { 26 | auto c = cast(CondExp*)this; 27 | return c->cond->hasSideEffects() || c->op1->hasSideEffects() || c->op2->hasSideEffects(); 28 | } 29 | case AstTag_OrOrExp: { 30 | auto o = cast(OrOrExp*)this; 31 | return o->op1->hasSideEffects() || o->op2->hasSideEffects(); 32 | } 33 | case AstTag_AndAndExp: { 34 | auto a = cast(AndAndExp*) this; 35 | return a->op1->hasSideEffects() || a->op2->hasSideEffects(); 36 | } 37 | 38 | case AstTag_CallExp: 39 | case AstTag_MethodCallExp: 40 | case AstTag_YieldExp: return true; 41 | default: return false; 42 | } 43 | } 44 | 45 | bool Expression::isMultRet() 46 | { 47 | switch(type) 48 | { 49 | case AstTag_CallExp: 50 | case AstTag_MethodCallExp: 51 | case AstTag_VarargExp: 52 | case AstTag_YieldExp: return true; 53 | default: return false; 54 | } 55 | } 56 | 57 | bool Expression::isLHS() 58 | { 59 | switch(type) 60 | { 61 | case AstTag_LenExp: 62 | case AstTag_DotExp: 63 | case AstTag_IndexExp: 64 | case AstTag_SliceExp: 65 | case AstTag_IdentExp: 66 | case AstTag_VargIndexExp: return true; 67 | default: return false; 68 | } 69 | } 70 | 71 | bool Expression::isConstant() 72 | { 73 | switch(type) 74 | { 75 | case AstTag_NullExp: 76 | case AstTag_BoolExp: 77 | case AstTag_IntExp: 78 | case AstTag_FloatExp: 79 | case AstTag_StringExp: return true; 80 | default: return false; 81 | } 82 | } 83 | 84 | bool Expression::isTrue() 85 | { 86 | switch(type) 87 | { 88 | case AstTag_NullExp: return false; 89 | case AstTag_BoolExp: return (cast(BoolExp*)this)->value; 90 | case AstTag_IntExp: return (cast(IntExp*)this)->value != 0; 91 | case AstTag_FloatExp: return (cast(FloatExp*)this)->value != 0.0; 92 | case AstTag_StringExp: return true; 93 | default: return false; 94 | } 95 | } 96 | 97 | bool Expression::isNull() 98 | { 99 | return type == AstTag_NullExp; 100 | } 101 | 102 | bool Expression::isBool() 103 | { 104 | return type == AstTag_BoolExp; 105 | } 106 | 107 | bool Expression::isInt() 108 | { 109 | return type == AstTag_IntExp; 110 | } 111 | 112 | bool Expression::isFloat() 113 | { 114 | return type == AstTag_FloatExp; 115 | } 116 | 117 | bool Expression::isNum() 118 | { 119 | return type == AstTag_IntExp || type == AstTag_FloatExp; 120 | } 121 | 122 | bool Expression::isString() 123 | { 124 | return type == AstTag_StringExp; 125 | } 126 | 127 | bool Expression::asBool() 128 | { 129 | if(auto b = AST_AS(BoolExp, this)) 130 | return b->value; 131 | 132 | assert(false); 133 | return false; // dummy 134 | } 135 | 136 | crocint Expression::asInt() 137 | { 138 | if(auto i = AST_AS(IntExp, this)) 139 | return i->value; 140 | 141 | assert(false); 142 | return 0; // dummy 143 | } 144 | 145 | crocfloat Expression::asFloat() 146 | { 147 | if(auto i = AST_AS(IntExp, this)) 148 | return i->value; 149 | else if(auto f = AST_AS(FloatExp, this)) 150 | return f->value; 151 | 152 | assert(false); 153 | return 0; // dummy 154 | } 155 | 156 | crocstr Expression::asString() 157 | { 158 | if(auto s = AST_AS(StringExp, this)) 159 | return s->value; 160 | 161 | assert(false); 162 | return crocstr(); // dummy 163 | } 164 | 165 | CrocType Expression::crocType() 166 | { 167 | switch(type) 168 | { 169 | case AstTag_NullExp: return CrocType_Null; 170 | case AstTag_BoolExp: return CrocType_Bool; 171 | case AstTag_IntExp: return CrocType_Int; 172 | case AstTag_FloatExp: return CrocType_Float; 173 | case AstTag_StringExp: return CrocType_String; 174 | 175 | default: 176 | assert(false); return CrocType_Null; // dummy 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /src/croc/compiler/astvisitor.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "croc/compiler/ast.hpp" 5 | #include "croc/compiler/astvisitor.hpp" 6 | #include "croc/compiler/types.hpp" 7 | 8 | namespace croc 9 | { 10 | // Visit methods 11 | #define POOP(Tag, _, BaseType)\ 12 | BaseType* AstVisitor::visit(Tag* node)\ 13 | {\ 14 | (void)node;\ 15 | fprintf(stderr, "no visit method implemented for AST node '" #Tag "'");\ 16 | abort();\ 17 | } 18 | AST_LIST(POOP) 19 | #undef POOP 20 | 21 | AstNode* AstVisitor::visit(AstNode* n) 22 | { 23 | switch(n->type) 24 | { 25 | #define POOP(Tag, _, __)\ 26 | case AstTag_##Tag: return visit(cast(Tag*)n); 27 | AST_LIST(POOP) 28 | #undef poop 29 | 30 | default: assert(false); return nullptr; // dummy 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/croc/compiler/astvisitor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_COMPILER_ASTVISITOR_HPP 2 | #define CROC_COMPILER_ASTVISITOR_HPP 3 | 4 | #include 5 | 6 | #include "croc/compiler/ast.hpp" 7 | #include "croc/compiler/types.hpp" 8 | 9 | namespace croc 10 | { 11 | struct AstVisitor 12 | { 13 | protected: 14 | Compiler& c; 15 | 16 | public: 17 | AstVisitor(Compiler& c) : c(c) {} 18 | 19 | inline Statement* visit(Statement* n) 20 | { 21 | return cast(Statement*)cast(void*)visit(cast(AstNode*)n); 22 | } 23 | 24 | inline Expression* visit(Expression* n) 25 | { 26 | return cast(Expression*)cast(void*)visit(cast(AstNode*)n); 27 | } 28 | 29 | #define POOP(Tag, _, BaseType)\ 30 | virtual BaseType* visit(Tag* node); 31 | AST_LIST(POOP) 32 | #undef POOP 33 | 34 | protected: 35 | AstNode* visit(AstNode* n); 36 | }; 37 | 38 | struct IdentityVisitor : public AstVisitor 39 | { 40 | public: 41 | IdentityVisitor(Compiler& c) : AstVisitor(c) {} 42 | 43 | #define POOP(Tag, _, BaseType)\ 44 | virtual BaseType* visit(Tag* node) override { return node; } 45 | AST_LIST(POOP) 46 | #undef POOP 47 | }; 48 | } 49 | 50 | #endif -------------------------------------------------------------------------------- /src/croc/compiler/docgen.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_COMPILER_DOCGEN_HPP 2 | #define CROC_COMPILER_DOCGEN_HPP 3 | 4 | #include "croc/compiler/astvisitor.hpp" 5 | #include "croc/compiler/types.hpp" 6 | 7 | namespace croc 8 | { 9 | class DocGen : public IdentityVisitor 10 | { 11 | private: 12 | struct DocTableDesc 13 | { 14 | DocTableDesc* prev; 15 | word docTable; 16 | word childIndex; 17 | 18 | DocTableDesc() : 19 | prev(), 20 | docTable(), 21 | childIndex() 22 | {} 23 | }; 24 | 25 | DocTableDesc* mDocTableDesc; 26 | CrocThread* t; 27 | uword mDocTable; 28 | word mDittoDepth; 29 | 30 | public: 31 | DocGen(Compiler& c) : 32 | IdentityVisitor(c), 33 | mDocTableDesc(), 34 | t(*c.thread()), 35 | mDocTable(), 36 | mDittoDepth() 37 | {} 38 | 39 | using AstVisitor::visit; 40 | 41 | FuncDef* visitStatements(FuncDef* d); 42 | virtual Module* visit(Module* m) override; 43 | virtual FuncDecl* visit(FuncDecl* d) override; 44 | virtual FuncDef* visit(FuncDef* d) override; 45 | virtual ClassDecl* visit(ClassDecl* d) override; 46 | virtual NamespaceDecl* visit(NamespaceDecl* d) override; 47 | virtual VarDecl* visit(VarDecl* d) override; 48 | virtual ScopeStmt* visit(ScopeStmt* s) override; 49 | virtual BlockStmt* visit(BlockStmt* s) override; 50 | virtual FuncLiteralExp* visit(FuncLiteralExp* e) override; 51 | 52 | private: 53 | void addComments(CompileLoc docsLoc, crocstr docs); 54 | void pushDocTable(DocTableDesc& desc, CompileLoc loc, CompileLoc docsLoc, crocstr kind, crocstr name, crocstr docs); 55 | void popDocTable(DocTableDesc& desc, const char* parentField = "children"); 56 | void ensureChildren(const char* parentField = "children"); 57 | void unpopTable(); 58 | void doProtection(Protection p); 59 | Expression* docTableToAST(CompileLoc loc); 60 | Identifier* docIdent(CompileLoc loc); 61 | Identifier* doctableIdent(CompileLoc loc); 62 | Decorator* makeDeco(CompileLoc loc, Decorator* existing, bool lastIndex = true); 63 | Expression* makeDocCall(Expression* init); 64 | void pushTrimmedString(crocstr str); 65 | 66 | template 67 | void doFields(DArray fields) 68 | { 69 | for(auto &f: fields) 70 | { 71 | if(f.docs.length == 0) 72 | continue; 73 | 74 | if(auto method = f.func) 75 | { 76 | visit(method); 77 | 78 | if(c.docDecorators()) 79 | f.initializer = makeDocCall(f.initializer); 80 | } 81 | else 82 | { 83 | // TODO: this location might not be on exactly the same line as the field itself.. huge deal? 84 | DocTableDesc desc; 85 | pushDocTable(desc, f.initializer->location, f.docsLoc, ATODA("field"), f.name, f.docs); 86 | 87 | if(f.initializer->sourceStr.length) 88 | { 89 | pushTrimmedString(f.initializer->sourceStr); 90 | croc_fielda(t, mDocTable, "value"); 91 | } 92 | 93 | popDocTable(desc); 94 | } 95 | } 96 | } 97 | }; 98 | } 99 | 100 | #endif -------------------------------------------------------------------------------- /src/croc/compiler/docparser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_COMPILER_DOCPARSER_HPP 2 | #define CROC_COMPILER_DOCPARSER_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | void processComment(CrocThread* t, crocstr comment); 9 | word parseCommentText(CrocThread* t, crocstr comment); 10 | } 11 | 12 | #endif -------------------------------------------------------------------------------- /src/croc/compiler/parser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_COMPILER_PARSER_HPP 2 | #define CROC_COMPILER_PARSER_HPP 3 | 4 | #include 5 | 6 | #include "croc/compiler/ast.hpp" 7 | #include "croc/compiler/lexer.hpp" 8 | #include "croc/compiler/types.hpp" 9 | #include "croc/types/base.hpp" 10 | 11 | namespace croc 12 | { 13 | struct Parser 14 | { 15 | private: 16 | Compiler& c; 17 | Lexer& l; 18 | uword mDummyNameCounter; 19 | crocstr mCurrentClassName; 20 | 21 | public: 22 | Parser(Compiler& compiler, Lexer& lexer) : 23 | c(compiler), 24 | l(lexer), 25 | mDummyNameCounter(0), 26 | mCurrentClassName() 27 | {} 28 | 29 | crocstr capture(std::function dg); 30 | crocstr parseName(); 31 | Expression* parseDottedName(); 32 | Identifier* parseIdentifier(); 33 | DArray parseArguments(); 34 | Module* parseModule(); 35 | FuncDef* parseStatements(crocstr name); 36 | FuncDef* parseExpressionFunc(crocstr name); 37 | Statement* parseStatement(bool needScope = true); 38 | Statement* parseExpressionStmt(); 39 | Decorator* parseDecorator(); 40 | Decorator* parseDecorators(); 41 | Statement* parseDeclStmt(); 42 | VarDecl* parseVarDecl(); 43 | FuncDecl* parseFuncDecl(Decorator* deco); 44 | FuncDef* parseFuncBody(CompileLoc location, Identifier* name); 45 | DArray parseFuncParams(bool& isVararg); 46 | DArray parseFuncReturns(bool& isVarret); 47 | uint32_t parseType(const char* kind, DArray& classTypes, crocstr& typeString, Expression*& customConstraint); 48 | uint32_t parseParamType(DArray& classTypes, crocstr& typeString, Expression*& customConstraint); 49 | uint32_t parseReturnType(DArray& classTypes, crocstr& typeString, Expression*& customConstraint); 50 | FuncDef* parseSimpleFuncDef(); 51 | FuncDef* parseFuncLiteral(); 52 | FuncDef* parseHaskellFuncLiteral(); 53 | ClassDecl* parseClassDecl(Decorator* deco); 54 | NamespaceDecl* parseNamespaceDecl(Decorator* deco); 55 | BlockStmt* parseBlockStmt(); 56 | AssertStmt* parseAssertStmt(); 57 | BreakStmt* parseBreakStmt(); 58 | ContinueStmt* parseContinueStmt(); 59 | DoWhileStmt* parseDoWhileStmt(); 60 | Statement* parseForStmt(); 61 | ForeachStmt* parseForeachStmt(); 62 | IfStmt* parseIfStmt(); 63 | ImportStmt* parseImportStmt(); 64 | ReturnStmt* parseReturnStmt(); 65 | SwitchStmt* parseSwitchStmt(); 66 | CaseStmt* parseCaseStmt(); 67 | DefaultStmt* parseDefaultStmt(); 68 | ThrowStmt* parseThrowStmt(); 69 | ScopeActionStmt* parseScopeActionStmt(); 70 | Statement* parseTryStmt(); 71 | WhileStmt* parseWhileStmt(); 72 | Statement* parseStatementExpr(); 73 | AssignStmt* parseAssignStmt(Expression* firstLHS); 74 | Statement* parseOpAssignStmt(Expression* exp1); 75 | Expression* parseExpression(); 76 | Expression* parseCondExp(Expression* exp1 = nullptr); 77 | Expression* parseLogicalCondExp(); 78 | Expression* parseOrOrExp(Expression* exp1 = nullptr); 79 | Expression* parseAndAndExp(Expression* exp1 = nullptr); 80 | Expression* parseOrExp(); 81 | Expression* parseXorExp(); 82 | Expression* parseAndExp(); 83 | Expression* parseCmpExp(); 84 | Expression* parseShiftExp(); 85 | Expression* parseAddExp(); 86 | Expression* parseAsExp(); 87 | Expression* parseMulExp(); 88 | Expression* parseUnExp(); 89 | Expression* parsePrimaryExp(); 90 | IdentExp* parseIdentExp(); 91 | ThisExp* parseThisExp(); 92 | NullExp* parseNullExp(); 93 | BoolExp* parseBoolExp(); 94 | VarargExp* parseVarargExp(); 95 | IntExp* parseIntExp(); 96 | FloatExp* parseFloatExp(); 97 | StringExp* parseStringExp(); 98 | FuncLiteralExp* parseFuncLiteralExp(); 99 | FuncLiteralExp* parseHaskellFuncLiteralExp(); 100 | Expression* parseParenExp(); 101 | Expression* parseTableCtorExp(); 102 | PrimaryExp* parseArrayCtorExp(); 103 | YieldExp* parseYieldExp(); 104 | Expression* parseMemberExp(); 105 | Expression* parsePostfixExp(Expression* exp); 106 | ForComprehension* parseForComprehension(); 107 | IfComprehension* parseIfComprehension(); 108 | void propagateFuncLiteralNames(DArray lhs, DArray rhs); 109 | void propagateFuncLiteralName(AstNode* lhs, FuncLiteralExp* fl); 110 | Identifier* dummyForeachIndex(CompileLoc loc); 111 | Identifier* dummyFuncLiteralName(CompileLoc loc); 112 | bool isPrivateFieldName(crocstr name); 113 | crocstr checkPrivateFieldName(crocstr fieldName); 114 | Expression* decoToExp(Decorator* dec, Expression* exp); 115 | 116 | template 117 | void attachDocs(T& t, crocstr preDocs, CompileLoc preDocsLoc) 118 | { 119 | if(!c.docComments()) 120 | return; 121 | 122 | if(preDocs.length != 0) 123 | { 124 | if(l.tok().postComment.length != 0) 125 | c.synException(preDocsLoc, "Cannot have two doc comments on one declaration"); 126 | else 127 | { 128 | t.docs = preDocs; 129 | t.docsLoc = preDocsLoc; 130 | } 131 | } 132 | else if(l.tok().postComment.length != 0) 133 | { 134 | t.docs = l.tok().postComment; 135 | t.docsLoc = l.tok().postCommentLoc; 136 | } 137 | } 138 | }; 139 | } 140 | 141 | #endif -------------------------------------------------------------------------------- /src/croc/ex/compilation.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/types/base.hpp" 5 | #include "croc/util/str.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | /** Expects an environment namespace on top of the stack and a string of code containing zero or more statements 12 | under it. Compiles the code and instantiates the funcdef. Pops the environment and source, and replaces them with 13 | the resulting function closure. 14 | 15 | \returns the stack index of the resulting closure. */ 16 | word_t croc_ex_loadStringWithEnvStk(CrocThread* t_, const char* name) 17 | { 18 | auto t = Thread::from(t_); 19 | API_CHECK_NUM_PARAMS(2); 20 | API_CHECK_PARAM(_, -2, String, "code"); 21 | API_CHECK_PARAM(__, -1, Namespace, "environment"); 22 | (void)_; 23 | (void)__; 24 | croc_dup(t_, -2); 25 | croc_compiler_compileStmtsEx(t_, name ? name : ""); 26 | croc_swapTop(t_); 27 | croc_function_newScriptWithEnv(t_, -2); 28 | croc_insertAndPop(t_, -3); 29 | return croc_getStackSize(t_) - 1; 30 | } 31 | 32 | /** Like \ref croc_ex_loadStringWithEnvStk but also calls the resulting function, leaving nothing on the stack. */ 33 | void croc_ex_runStringWithEnvStk(CrocThread* t, const char* name) 34 | { 35 | croc_ex_loadStringWithEnvStk(t, name); 36 | croc_pushNull(t); 37 | croc_call(t, -2, 0); 38 | } 39 | 40 | /** Expects an environment namespace on top of the stack and a string of code containing an expression under it. 41 | Compiles and runs the expression, returning \c numReturns values which replace the code and environment. 42 | 43 | \returns the number of values that were returned from the expression. */ 44 | uword_t croc_ex_evalWithEnvStk(CrocThread* t_, word_t numReturns) 45 | { 46 | auto t = Thread::from(t_); 47 | API_CHECK_NUM_PARAMS(2); 48 | API_CHECK_PARAM(_, -2, String, "code"); 49 | API_CHECK_PARAM(__, -1, Namespace, "environment"); 50 | (void)_; 51 | (void)__; 52 | croc_dup(t_, -2); 53 | croc_compiler_compileExprEx(t_, ""); 54 | croc_swapTop(t_); 55 | croc_function_newScriptWithEnv(t_, -2); 56 | croc_insertAndPop(t_, -3); 57 | croc_pushNull(t_); 58 | return croc_call(t_, -2, numReturns); 59 | } 60 | 61 | /** Imports the module \c moduleName, and then calls the Croc \c modules.runMain function on the resulting module. 62 | 63 | \param numParams is how many parameters you want to pass to the module's \c main function. There should be this many 64 | values on the stack, and they will be popped. */ 65 | void croc_ex_runModule(CrocThread* t_, const char* moduleName, uword_t numParams) 66 | { 67 | auto t = Thread::from(t_); 68 | API_CHECK_NUM_PARAMS(numParams); 69 | croc_ex_importNS(t_, moduleName); 70 | croc_pushNull(t_); 71 | croc_ex_lookup(t_, "modules.runMain"); 72 | croc_swapTopWith(t_, -3); 73 | croc_rotate(t_, numParams + 3, 3); 74 | croc_call(t_, -3 - numParams, 0); 75 | } 76 | } -------------------------------------------------------------------------------- /src/croc/ex/imports.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/api/apichecks.hpp" 4 | #include "croc/internal/stack.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | using namespace croc; 8 | 9 | extern "C" 10 | { 11 | /** Like \ref croc_ex_import, but leaves the namespace of the imported module on the stack. 12 | 13 | \returns the stack index of the pushed namespace. */ 14 | word_t croc_ex_importNS(CrocThread* t, const char* name) 15 | { 16 | croc_pushString(t, name); 17 | croc_ex_importNSStk(t, -1); 18 | croc_insertAndPop(t, -2); 19 | return croc_getStackSize(t) - 1; 20 | } 21 | 22 | /** Like \ref croc_ex_importStk, but leaves the namespace of the imported module on the stack. 23 | 24 | \returns the stack index of the pushed namespace. */ 25 | word_t croc_ex_importNSStk(CrocThread* t_, word_t name) 26 | { 27 | auto t = Thread::from(t_); 28 | name = croc_absIndex(t_, name); 29 | API_CHECK_PARAM(_, name, String, "module name"); 30 | (void)_; 31 | croc_ex_lookup(t_, "modules.load"); 32 | croc_pushNull(t_); 33 | croc_dup(t_, name); 34 | croc_call(t_, -3, 1); 35 | return croc_getStackSize(t_) - 1; 36 | } 37 | 38 | /** Like \ref croc_ex_importFromStringStk, but leaves the namespace of the imported module on the stack. 39 | 40 | \returns the stack index of the pushed namespace. */ 41 | word_t croc_ex_importFromStringNSStk(CrocThread* t_, const char* name, const char* srcName) 42 | { 43 | auto t = Thread::from(t_); 44 | API_CHECK_PARAM(_, -1, String, "source"); 45 | (void)_; 46 | 47 | if(name == nullptr) 48 | croc_eh_throwStd(t_, "ApiError", "'name' is null"); 49 | 50 | if(srcName == nullptr) 51 | srcName = name; 52 | 53 | croc_ex_lookup(t_, "modules.customLoaders"); 54 | croc_insert(t_, -2); 55 | const char* modName; 56 | croc_compiler_compileModuleEx(t_, srcName, &modName); 57 | 58 | if(strcmp(name, modName) != 0) 59 | croc_eh_throwStd(t_, "ImportException", 60 | "Import name (%s) does not match name given in module statement (%s)", name, modName); 61 | 62 | croc_fielda(t_, -2, modName); 63 | croc_popTop(t_); 64 | return croc_ex_importNS(t_, modName); 65 | } 66 | } -------------------------------------------------------------------------------- /src/croc/ex/library.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/types/base.hpp" 4 | 5 | using namespace croc; 6 | 7 | extern "C" 8 | { 9 | namespace 10 | { 11 | void pushRegisterFunc(CrocThread* t, CrocRegisterFunc& f, uword_t numUpvals) 12 | { 13 | croc_function_new(t, f.name, f.maxParams, f.func, numUpvals); 14 | } 15 | } 16 | 17 | /** Given a \c name and a \c loader native function which will act as the top-level function for the module, inserts 18 | an entry into the Croc \c modules.customLoaders table which will call the \c loader when \c name is imported. */ 19 | void croc_ex_makeModule(CrocThread* t, const char* name, CrocNativeFunc loader) 20 | { 21 | croc_pushGlobal(t, "modules"); 22 | croc_field(t, -1, "customLoaders"); 23 | 24 | if(croc_hasField(t, -1, name)) 25 | croc_eh_throwStd(t, "LookupError", 26 | "%s - Module '%s' already has a loader set for it in modules.customLoaders", __FUNCTION__, name); 27 | 28 | croc_function_new(t, name, 1, loader, 0); 29 | croc_fielda(t, -2, name); 30 | croc_pop(t, 2); 31 | } 32 | 33 | /** Like \ref croc_ex_registerGlobal, but expects \c numUpvals values on top of the stack, and creates the function 34 | with them as its upvalues. */ 35 | void croc_ex_registerGlobalUV(CrocThread* t, CrocRegisterFunc f, uword_t numUpvals) 36 | { 37 | pushRegisterFunc(t, f, numUpvals); 38 | croc_newGlobal(t, f.name); 39 | } 40 | 41 | /** Like \ref croc_ex_registerField, but expects \c numUpvals values on top of the stack, and creates the function 42 | with them as its upvalues. The object that will be given the field should be below the upvalues. */ 43 | void croc_ex_registerFieldUV(CrocThread* t, CrocRegisterFunc f, uword_t numUpvals) 44 | { 45 | pushRegisterFunc(t, f, numUpvals); 46 | croc_fielda(t, -2, f.name); 47 | } 48 | 49 | /** Like \ref croc_ex_registerMethod, but expects \c numUpvals values on top of the stack, and creates the function 50 | with them as its upvalues. The object that will be given the method should be below the upvalues. */ 51 | void croc_ex_registerMethodUV(CrocThread* t, CrocRegisterFunc f, uword_t numUpvals) 52 | { 53 | pushRegisterFunc(t, f, numUpvals); 54 | croc_class_addMethod(t, -2, f.name); 55 | } 56 | 57 | /** Takes an array of \ref CrocRegisterFunc structs, terminated by a struct whose \c name member is \c NULL, and 58 | registers them all as globals in the current environment. 59 | 60 | For example: 61 | 62 | \code{.c} 63 | const CrocRegisterFunc funcs[] = 64 | { 65 | { "func1", 0, &func1 }, 66 | { "func2", 1, &func2 }, 67 | { NULL, 0, NULL } 68 | }; 69 | 70 | // Later, perhaps in the loader function that was set using croc_ex_makeModule... 71 | croc_ex_registerGlobals(t, funcs); 72 | \endcode */ 73 | void croc_ex_registerGlobals(CrocThread* t, const CrocRegisterFunc* funcs) 74 | { 75 | for(auto f = funcs; f->name != nullptr; f++) 76 | croc_ex_registerGlobal(t, *f); 77 | } 78 | 79 | /** Like \ref croc_ex_registerGlobals, but field-assigns them into the value on top of the stack. */ 80 | void croc_ex_registerFields(CrocThread* t, const CrocRegisterFunc* funcs) 81 | { 82 | for(auto f = funcs; f->name != nullptr; f++) 83 | croc_ex_registerField(t, *f); 84 | } 85 | 86 | /** Like \ref croc_ex_registerGlobals, but adds them as methods into the class on top of the stack. */ 87 | void croc_ex_registerMethods(CrocThread* t, const CrocRegisterFunc* funcs) 88 | { 89 | for(auto f = funcs; f->name != nullptr; f++) 90 | croc_ex_registerMethod(t, *f); 91 | } 92 | } -------------------------------------------------------------------------------- /src/croc/ext/convert.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char** argv) 7 | { 8 | if(argc < 2) 9 | exit(EXIT_FAILURE); 10 | 11 | auto slash = strrchr(argv[1], '/'); 12 | 13 | if(slash) 14 | slash++; 15 | else 16 | { 17 | slash = strrchr(argv[1], '\\'); 18 | 19 | if(slash) 20 | slash++; 21 | else 22 | slash = argv[1]; 23 | } 24 | 25 | auto name = (char*)malloc(strlen(slash) + 1); 26 | strcpy(name, slash); 27 | 28 | for(auto pos = strchr(name, '.'); pos != nullptr; pos = strchr(pos + 1, '.')) 29 | *pos = '_'; 30 | 31 | auto fp = fopen(argv[1], "rb"); 32 | 33 | if(fp == nullptr) 34 | exit(EXIT_FAILURE); 35 | 36 | printf("const char %s_text[] =\n{", name); 37 | 38 | int i = 0; 39 | char c; 40 | for( ; (c = fgetc(fp)) != EOF; i++) 41 | { 42 | if((i % 20) == 0) 43 | printf("\n\t"); 44 | 45 | printf("%#04x, ", c); 46 | } 47 | 48 | printf("\n\t0\n};\n\nconst size_t %s_length = %d;\n", name, i); 49 | fclose(fp); 50 | free(name); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /src/croc/ext/imgui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(imgui_SRC 4 | imconfig.h 5 | imgui.cpp 6 | imgui.h 7 | imgui_user.inl 8 | stb_textedit.h 9 | ) 10 | 11 | add_library(imgui ${imgui_SRC}) -------------------------------------------------------------------------------- /src/croc/ext/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // USER IMPLEMENTATION 3 | // This file contains compile-time options for ImGui. 4 | // Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). 5 | //----------------------------------------------------------------------------- 6 | 7 | #pragma once 8 | 9 | //---- Define your own ImVector<> type if you don't want to use the provided implementation defined in imgui.h 10 | //#include 11 | //#define ImVector std::vector 12 | //#define ImVector MyVector 13 | 14 | //---- Define assertion handler. Defaults to calling assert(). 15 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 16 | 17 | //---- Don't implement default clipboard handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) 18 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS 19 | 20 | //---- Include imgui_user.inl at the end of imgui.cpp so you can include code that extends ImGui using its private data/functions. 21 | #define IMGUI_INCLUDE_IMGUI_USER_INL 22 | 23 | //---- Define implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. 24 | /* 25 | #define IM_VEC2_CLASS_EXTRA \ 26 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 27 | operator MyVec2() const { return MyVec2(x,y); } 28 | 29 | #define IM_VEC4_CLASS_EXTRA \ 30 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 31 | operator MyVec4() const { return MyVec4(x,y,z,w); } 32 | */ 33 | 34 | //---- Freely implement extra functions within the ImGui:: namespace. 35 | //---- Declare helpers or widgets implemented in imgui_user.inl or elsewhere, so end-user doesn't need to include multiple files. 36 | //---- e.g. you can create variants of the ImGui::Value() helper for your low-level math types. 37 | /* 38 | namespace ImGui 39 | { 40 | void Value(const char* prefix, const MyVec2& v, const char* float_format = NULL); 41 | void Value(const char* prefix, const MyVec4& v, const char* float_format = NULL); 42 | } 43 | */ 44 | 45 | -------------------------------------------------------------------------------- /src/croc/ext/imgui/imgui_user.inl: -------------------------------------------------------------------------------- 1 | 2 | namespace ImGui 3 | { 4 | 5 | int WindowStackDepth() 6 | { 7 | return GImGui.CurrentWindowStack.size(); 8 | } 9 | 10 | bool IsInitialized() 11 | { 12 | return GImGui.Initialized; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/croc/ext/jhash.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_EXT_JHASH_HPP 2 | #define CROC_EXT_JHASH_HPP 3 | 4 | #include 5 | #include /* defines uint32_t etc */ 6 | 7 | uint32_t hashword( 8 | const uint32_t *k, /* the key, an array of uint32_t values */ 9 | size_t length, /* the length of the key, in uint32_ts */ 10 | uint32_t initval); /* the previous hash, or an arbitrary value */ 11 | 12 | void hashword2 ( 13 | const uint32_t *k, /* the key, an array of uint32_t values */ 14 | size_t length, /* the length of the key, in uint32_ts */ 15 | uint32_t *pc, /* IN: seed OUT: primary hash value */ 16 | uint32_t *pb); /* IN: more seed OUT: secondary hash value */ 17 | 18 | uint32_t hashlittle( const void *key, size_t length, uint32_t initval); 19 | 20 | void hashlittle2( 21 | const void *key, /* the key to hash */ 22 | size_t length, /* length of the key */ 23 | uint32_t *pc, /* IN: primary initval, OUT: primary hash */ 24 | uint32_t *pb); /* IN: secondary initval, OUT: secondary hash */ 25 | 26 | uint32_t hashbig( const void *key, size_t length, uint32_t initval); 27 | 28 | #endif -------------------------------------------------------------------------------- /src/croc/internal/basic.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_BASIC_HPP 2 | #define CROC_INTERNAL_BASIC_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | bool validIndices(crocint lo, crocint hi, uword len); 9 | bool correctIndices(crocint& loIndex, crocint& hiIndex, Value lo, Value hi, uword len); 10 | word toStringImpl(Thread* t, Value v, bool raw); 11 | word pushFullNamespaceName(Thread* t, Namespace* ns); 12 | word pushTypeStringImpl(Thread* t, Value v); 13 | bool inImpl(Thread* t, Value item, Value container); 14 | crocint cmpImpl(Thread* t, Value a, Value b); 15 | bool switchCmpImpl(Thread* t, Value a, Value b); 16 | bool equalsImpl(Thread* t, Value a, Value b); 17 | void idxImpl(Thread* t, AbsStack dest, Value container, Value key); 18 | void tableIdxImpl(Thread* t, AbsStack dest, Table* container, Value key); 19 | void idxaImpl(Thread* t, AbsStack container, Value key, Value value); 20 | void tableIdxaImpl(Thread* t, Table* container, Value key, Value value); 21 | void sliceImpl(Thread* t, AbsStack dest, Value src, Value lo, Value hi); 22 | void sliceaImpl(Thread* t, Value container, Value lo, Value hi, Value value); 23 | void fieldImpl(Thread* t, AbsStack dest, Value container, String* name, bool raw); 24 | void fieldaImpl(Thread* t, AbsStack container, String* name, Value value, bool raw); 25 | void lenImpl(Thread* t, AbsStack dest, Value src); 26 | void lenaImpl(Thread* t, Value dest, Value len); 27 | void catImpl(Thread* t, AbsStack dest, AbsStack firstSlot, uword num); 28 | void arrayConcat(Thread* t, DArray vals, uword len); 29 | void stringConcat(Thread* t, Value first, DArray vals, uword len, uword cpLen); 30 | void catEqImpl(Thread* t, AbsStack dest, AbsStack firstSlot, uword num); 31 | void arrayAppend(Thread* t, Array* a, DArray vals); 32 | Value superOfImpl(Thread* t, Value v); 33 | } 34 | 35 | #endif -------------------------------------------------------------------------------- /src/croc/internal/calls.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_CALLS_HPP 2 | #define CROC_INTERNAL_CALLS_HPP 3 | 4 | #include "croc/base/metamethods.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | namespace croc 8 | { 9 | Namespace* getEnv(Thread* t, uword depth = 0); 10 | Value lookupMethod(Thread* t, Value v, String* name); 11 | Value getInstanceMethod(Thread* t, Instance* inst, String* name); 12 | Value getGlobalMetamethod(Thread* t, CrocType type, String* name); 13 | Function* getMM(Thread* t, Value obj, Metamethod method); 14 | Namespace* getMetatable(Thread* t, CrocType type); 15 | void closeUpvals(Thread* t, AbsStack index); 16 | Upval* findUpval(Thread* t, uword num); 17 | ActRecord* pushAR(Thread* t); 18 | // void popAR(Thread* t); 19 | void popARTo(Thread* t, uword removeTo); 20 | void callEpilogue(Thread* t); 21 | void saveResults(Thread* t, Thread* from, AbsStack first, uword num); 22 | DArray loadResults(Thread* t); 23 | bool callPrologue(Thread* t, AbsStack slot, word expectedResults, uword numParams, bool isTailcall = false); 24 | bool funcCallPrologue(Thread* t, Function* func, AbsStack returnSlot, word expectedResults, AbsStack paramSlot, 25 | uword numParams, bool isTailcall = false); 26 | uword commonCall(Thread* t, AbsStack slot, word numReturns, bool isScript); 27 | bool methodCallPrologue(Thread* t, AbsStack slot, Value self, String* methodName, word numReturns, uword numParams, 28 | bool isTailcall = false); 29 | bool tryMMDest(Thread* t, Metamethod mm, AbsStack dest, Value src1); 30 | bool tryMMDest(Thread* t, Metamethod mm, AbsStack dest, Value src1, Value src2); 31 | bool tryMMDest(Thread* t, Metamethod mm, AbsStack dest, Value src1, Value src2, Value src3); 32 | bool tryMM(Thread* t, Metamethod mm, Value src1, Value src2); 33 | bool tryMM(Thread* t, Metamethod mm, Value src1, Value src2, Value src3); 34 | bool tryMM(Thread* t, Metamethod mm, Value src1, Value src2, Value src3, Value src4); 35 | } 36 | 37 | #endif -------------------------------------------------------------------------------- /src/croc/internal/class.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/basic.hpp" 4 | #include "croc/internal/class.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | namespace croc 8 | { 9 | void classDeriveImpl(Thread* t, Class* c, Class* base) 10 | { 11 | // This probably shouldn't happen under normal circumstances but maybe if it's made a library function? 12 | if(c->isFrozen) 13 | croc_eh_throwStd(*t, "StateError", "Attempting to derive classes into a frozen class"); 14 | 15 | freezeImpl(t, base); 16 | 17 | if(base->finalizer) 18 | croc_eh_throwStd(*t, "ValueError", "Attempting to derive from class '%s' which has a finalizer", 19 | base->name->toCString()); 20 | 21 | const char* which; 22 | 23 | if(auto conflict = Class::derive(t->vm->mem, c, base, which)) 24 | { 25 | croc_eh_throwStd(*t, "ValueError", 26 | "Attempting to derive %s '%s' from class '%s', but it already exists in the new class '%s'", 27 | which, conflict->key->toCString(), base->name->toCString(), c->name->toCString()); 28 | } 29 | } 30 | 31 | void freezeImpl(Thread* t, Class* c) 32 | { 33 | if(c->isFrozen) 34 | return; 35 | 36 | if(auto ctor = c->getMethod(t->vm->ctorString)) 37 | { 38 | if(ctor->type != CrocType_Function) 39 | { 40 | pushTypeStringImpl(t, *ctor); 41 | croc_eh_throwStd(*t, "TypeError", "Class constructor must be of type 'function', not '%s'", 42 | croc_getString(*t, -1)); 43 | } 44 | 45 | c->constructor = ctor; 46 | } 47 | 48 | if(auto finalizer = c->getMethod(t->vm->finalizerString)) 49 | { 50 | if(finalizer->type != CrocType_Function) 51 | { 52 | pushTypeStringImpl(t, *finalizer); 53 | croc_eh_throwStd(*t, "TypeError", "Class finalizer must be of type 'function', not '%s'", 54 | croc_getString(*t, -1)); 55 | } 56 | 57 | c->finalizer = finalizer; 58 | } 59 | 60 | c->freeze(t->vm->mem); 61 | } 62 | } -------------------------------------------------------------------------------- /src/croc/internal/class.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_CLASS_HPP 2 | #define CROC_INTERNAL_CLASS_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | void classDeriveImpl(Thread* t, Class* c, Class* base); 9 | void freezeImpl(Thread* t, Class* c); 10 | } 11 | 12 | #endif -------------------------------------------------------------------------------- /src/croc/internal/debug.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/basic.hpp" 4 | #include "croc/internal/calls.hpp" 5 | #include "croc/internal/debug.hpp" 6 | #include "croc/internal/eh.hpp" 7 | #include "croc/internal/stack.hpp" 8 | #include "croc/types/base.hpp" 9 | 10 | namespace croc 11 | { 12 | // don't call this if t.calldepth == 0 or with depth >= t.calldepth 13 | // returns null if the given index is a tailcall or if depth is deeper than the current call depth 14 | ActRecord* getActRec(Thread* t, uword depth) 15 | { 16 | assert(t->arIndex != 0); 17 | 18 | if(depth == 0) 19 | return t->currentAR; 20 | 21 | for(word idx = t->arIndex - 1; idx >= 0; idx--) 22 | { 23 | if(depth == 0) 24 | return &t->actRecs[cast(uword)idx]; 25 | else if(depth <= t->actRecs[cast(uword)idx].numTailcalls) 26 | return nullptr; 27 | 28 | depth -= (t->actRecs[cast(uword)idx].numTailcalls + 1); 29 | } 30 | 31 | return nullptr; 32 | } 33 | 34 | word pcToLine(ActRecord* ar, Instruction* pc) 35 | { 36 | int line = 0; 37 | 38 | auto def = ar->func->scriptFunc; 39 | uword instructionIndex = (pc > def->code.ptr) ? (pc - def->code.ptr - 1) : 0; 40 | 41 | if(instructionIndex < def->lineInfo.length) 42 | line = def->lineInfo[instructionIndex]; 43 | 44 | return line; 45 | } 46 | 47 | word getDebugLine(Thread* t, uword depth) 48 | { 49 | if(t->currentAR == nullptr) 50 | return 0; 51 | 52 | auto ar = getActRec(t, depth); 53 | 54 | if(ar == nullptr || ar->func == nullptr || ar->func->isNative) 55 | return 0; 56 | 57 | return pcToLine(ar, ar->pc); 58 | } 59 | 60 | word pushDebugLoc(Thread* t, ActRecord* ar) 61 | { 62 | if(ar == nullptr) 63 | ar = t->currentAR; 64 | 65 | if(ar == nullptr || ar->func == nullptr) 66 | return croc_eh_pushLocationObject(*t, "", 0, CrocLocation_Unknown); 67 | else 68 | { 69 | pushFullNamespaceName(t, ar->func->environment); 70 | 71 | if(croc_len(*t, -1) == 0) 72 | croc_dupTop(*t); 73 | else 74 | croc_pushString(*t, "."); 75 | 76 | push(t, Value::from(ar->func->name)); 77 | 78 | auto slot = t->stackIndex - 3; 79 | catImpl(t, slot, slot, 3); 80 | auto s = croc_getString(*t, -3); 81 | croc_pop(*t, 3); 82 | 83 | if(ar->func->isNative) 84 | return croc_eh_pushLocationObject(*t, s, 0, CrocLocation_Native); 85 | else 86 | return croc_eh_pushLocationObject(*t, s, pcToLine(ar, ar->pc), CrocLocation_Script); 87 | } 88 | } 89 | 90 | void callHook(Thread* t, CrocThreadHook hook) 91 | { 92 | if(!t->hooksEnabled || !t->hookFunc) 93 | return; 94 | 95 | auto savedTop = t->stackIndex; 96 | t->hooksEnabled = false; 97 | 98 | auto slot = push(t, Value::from(t->hookFunc)) + t->stackBase; 99 | push(t, Value::from(t)); 100 | 101 | switch(hook) 102 | { 103 | case CrocThreadHook_Call: croc_pushString(*t, "call"); break; 104 | case CrocThreadHook_TailCall: croc_pushString(*t, "tailcall"); break; 105 | case CrocThreadHook_Ret: croc_pushString(*t, "ret"); break; 106 | case CrocThreadHook_Delay: croc_pushString(*t, "delay"); break; 107 | case CrocThreadHook_Line: croc_pushString(*t, "line"); break; 108 | default: assert(false); 109 | } 110 | 111 | t->nativeCallDepth++; 112 | 113 | auto failed = tryCode(t, slot - t->stackBase, [&] 114 | { 115 | commonCall(t, slot, 0, callPrologue(t, slot, 0, 2)); 116 | }); 117 | 118 | t->nativeCallDepth--; 119 | t->hooksEnabled = true; 120 | 121 | if(failed) 122 | croc_eh_rethrow(*t); 123 | else 124 | t->stackIndex = savedTop; 125 | } 126 | } -------------------------------------------------------------------------------- /src/croc/internal/debug.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_DEBUG_HPP 2 | #define CROC_INTERNAL_DEBUG_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | ActRecord* getActRec(Thread* t, uword depth); 9 | word pcToLine(ActRecord* ar, Instruction* pc); 10 | word getDebugLine(Thread* t, uword depth = 0); 11 | word pushDebugLoc(Thread* t, ActRecord* ar = nullptr); 12 | void callHook(Thread* t, CrocThreadHook hook); 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /src/croc/internal/eh.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_EH_HPP 2 | #define CROC_INTERNAL_EH_HPP 3 | 4 | #include 5 | 6 | #include "croc/types/base.hpp" 7 | 8 | namespace croc 9 | { 10 | word defaultUnhandledEx(CrocThread* t); 11 | void pushNativeEHFrame(Thread* t, RelStack slot, jmp_buf& buf); 12 | void pushExecEHFrame(Thread* t, jmp_buf& buf); 13 | void pushScriptEHFrame(Thread* t, bool isCatch, RelStack slot, Instruction* pc); 14 | void popNativeEHFrame(Thread* t); 15 | void popScriptEHFrame(Thread* t); 16 | void unwindThisFramesEH(Thread* t); 17 | bool tryCode(Thread* t, RelStack slot, std::function dg); 18 | word pushTraceback(Thread* t); 19 | void continueTraceback(Thread* t, Value ex); 20 | void addLocationInfo(Thread* t, Value ex); 21 | void throwImpl(Thread* t, Value ex, bool rethrowing); 22 | void unwind(Thread* t); 23 | } 24 | 25 | #endif -------------------------------------------------------------------------------- /src/croc/internal/gc.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/base/gc.hpp" 4 | #include "croc/internal/calls.hpp" 5 | #include "croc/internal/eh.hpp" 6 | #include "croc/internal/gc.hpp" 7 | #include "croc/internal/stack.hpp" 8 | #include "croc/types/base.hpp" 9 | 10 | namespace croc 11 | { 12 | void runFinalizers(Thread* t) 13 | { 14 | auto &mem = t->vm->mem; 15 | auto &decBuffer = mem.decBuffer; 16 | 17 | t->vm->disableGC(); 18 | auto hooksEnabled = t->hooksEnabled; 19 | t->hooksEnabled = false; 20 | 21 | // FINALIZE. Go through the finalize buffer, running the finalizer, and setting it to finalized. At this point, 22 | // the object may have been resurrected but we can't really tell unless we make the write barrier more 23 | // complicated. Or something. So we just queue a decrement for it. It'll get deallocated the next time around. 24 | t->vm->toFinalize.foreach([&](GCObject* i_) 25 | { 26 | auto i = cast(Instance*)i_; 27 | // debug Stdout.formatln("Taking {} off toFinalize", i).flush; 28 | 29 | auto slot = push(t, *i->parent->finalizer); 30 | push(t, Value::from(i)); 31 | 32 | auto failed = tryCode(t, slot, [&] 33 | { 34 | auto absSlot = slot + t->stackBase; 35 | commonCall(t, absSlot, 0, callPrologue(t, absSlot, 0, 1)); 36 | }); 37 | 38 | if(failed) 39 | { 40 | croc_eh_pushStd(*t, "FinalizerError"); 41 | croc_pushNull(*t); 42 | croc_pushFormat(*t, "Error finalizing instance of class '%s'", i->parent->name->toCString()); 43 | croc_call(*t, -3, 1); 44 | croc_swapTop(*t); 45 | croc_fielda(*t, -2, "cause"); 46 | t->hooksEnabled = hooksEnabled; 47 | croc_eh_throw(*t); 48 | } 49 | 50 | GCOBJ_SETFINALIZED(i); 51 | decBuffer.add(mem, cast(GCObject*)i); 52 | }); 53 | 54 | t->hooksEnabled = hooksEnabled; 55 | t->vm->enableGC(); 56 | t->vm->toFinalize.reset(); 57 | } 58 | } -------------------------------------------------------------------------------- /src/croc/internal/gc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_GC_HPP 2 | #define CROC_INTERNAL_GC_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | void runFinalizers(Thread* t); 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /src/croc/internal/interpreter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_INTERPRETER_HPP 2 | #define CROC_INTERNAL_INTERPRETER_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | void execute(Thread* t, uword startARIndex); 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /src/croc/internal/stack.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/types/base.hpp" 4 | 5 | namespace croc 6 | { 7 | void checkStack(Thread* t, AbsStack idx) 8 | { 9 | if(idx >= t->stack.length) 10 | { 11 | uword size = idx * 2; 12 | auto oldBase = t->stack.ptr; 13 | t->stack.resize(t->vm->mem, size); 14 | auto newBase = t->stack.ptr; 15 | 16 | if(newBase != oldBase) 17 | { 18 | for(auto uv = t->upvalHead; uv != nullptr; uv = uv->nextuv) 19 | uv->value = (uv->value - oldBase) + newBase; 20 | } 21 | } 22 | } 23 | 24 | RelStack fakeToRel(Thread* t, word fake) 25 | { 26 | assert(t->stackIndex > t->stackBase); 27 | 28 | auto size = croc_getStackSize(*t); 29 | 30 | if(fake < 0) 31 | fake += size; 32 | 33 | if(fake < 0 || fake >= cast(word)size) 34 | croc_eh_throwStd(*t, "ApiError", 35 | "Invalid stack index %" CROC_SSIZE_T_FORMAT " (stack size = %" CROC_SIZE_T_FORMAT ")", fake, size); 36 | 37 | return cast(RelStack)fake; 38 | } 39 | 40 | AbsStack fakeToAbs(Thread* t, word fake) 41 | { 42 | return fakeToRel(t, fake) + t->stackBase; 43 | } 44 | 45 | word push(Thread* t, Value val) 46 | { 47 | checkStack(t, t->stackIndex); 48 | t->stack[t->stackIndex] = val; 49 | t->stackIndex++; 50 | return cast(word)(t->stackIndex - 1 - t->stackBase); 51 | } 52 | 53 | word pushCrocstr(CrocThread* t, crocstr s) 54 | { 55 | return croc_pushStringn(t, cast(const char*)s.ptr, s.length); 56 | } 57 | 58 | Value* getValue(Thread* t, word slot) 59 | { 60 | return &t->stack[fakeToAbs(t, slot)]; 61 | } 62 | 63 | String* getStringObj(Thread* t, word slot) 64 | { 65 | auto v = &t->stack[fakeToAbs(t, slot)]; 66 | 67 | if(v->type == CrocType_String) 68 | return v->mString; 69 | else 70 | return nullptr; 71 | } 72 | 73 | crocstr getCrocstr(Thread* t, word slot) 74 | { 75 | auto v = &t->stack[fakeToAbs(t, slot)]; 76 | 77 | if(v->type == CrocType_String) 78 | return v->mString->toDArray(); 79 | else 80 | return crocstr(); 81 | } 82 | 83 | crocstr getCrocstr(CrocThread* t, word slot) 84 | { 85 | return getCrocstr(Thread::from(t), slot); 86 | } 87 | 88 | #define MAKE_GET(Type)\ 89 | Type* get##Type(Thread* t, word slot)\ 90 | {\ 91 | auto v = &t->stack[fakeToAbs(t, slot)];\ 92 | \ 93 | if(v->type == CrocType_##Type)\ 94 | return v->m##Type;\ 95 | else\ 96 | return nullptr;\ 97 | } 98 | 99 | MAKE_GET(Weakref) 100 | MAKE_GET(Table) 101 | MAKE_GET(Namespace) 102 | MAKE_GET(Array) 103 | MAKE_GET(Memblock) 104 | MAKE_GET(Function) 105 | MAKE_GET(Funcdef) 106 | MAKE_GET(Class) 107 | MAKE_GET(Instance) 108 | MAKE_GET(Thread) 109 | 110 | } -------------------------------------------------------------------------------- /src/croc/internal/stack.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_STACK_HPP 2 | #define CROC_INTERNAL_STACK_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | void checkStack (Thread* t, AbsStack idx); 9 | RelStack fakeToRel (Thread* t, word fake); 10 | AbsStack fakeToAbs (Thread* t, word fake); 11 | word push (Thread* t, Value val); 12 | word pushCrocstr (CrocThread* t, crocstr s); 13 | Value* getValue (Thread* t, word slot); 14 | String* getStringObj (Thread* t, word slot); 15 | crocstr getCrocstr (Thread* t, word slot); 16 | crocstr getCrocstr (CrocThread* t, word slot); 17 | Weakref* getWeakref (Thread* t, word slot); 18 | Table* getTable (Thread* t, word slot); 19 | Namespace* getNamespace (Thread* t, word slot); 20 | Array* getArray (Thread* t, word slot); 21 | Memblock* getMemblock (Thread* t, word slot); 22 | Function* getFunction (Thread* t, word slot); 23 | Funcdef* getFuncdef (Thread* t, word slot); 24 | Class* getClass (Thread* t, word slot); 25 | Instance* getInstance (Thread* t, word slot); 26 | Thread* getThread (Thread* t, word slot); 27 | } 28 | 29 | #endif -------------------------------------------------------------------------------- /src/croc/internal/thread.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/calls.hpp" 4 | #include "croc/internal/eh.hpp" 5 | #include "croc/internal/interpreter.hpp" 6 | #include "croc/internal/stack.hpp" 7 | #include "croc/internal/thread.hpp" 8 | #include "croc/types/base.hpp" 9 | 10 | namespace croc 11 | { 12 | void yieldImpl(Thread* t, AbsStack firstValue, word numValues, word expectedResults) 13 | { 14 | auto ar = pushAR(t); 15 | 16 | assert(t->arIndex > 1); 17 | *ar = t->actRecs[t->arIndex - 2]; 18 | 19 | ar->func = nullptr; 20 | 21 | assert(firstValue > t->stackBase); 22 | 23 | ar->returnSlot = firstValue; 24 | ar->expectedResults = expectedResults; 25 | ar->firstResult = 0; 26 | ar->numResults = 0; 27 | 28 | if(numValues == -1) 29 | t->numYields = t->stackIndex - firstValue; 30 | else 31 | { 32 | t->stackIndex = firstValue + numValues; 33 | t->numYields = numValues; 34 | } 35 | 36 | t->state = CrocThreadState_Suspended; 37 | } 38 | 39 | void resume(Thread* t, Thread* from, AbsStack slot, uword expectedResults, uword numParams) 40 | { 41 | // Set up AR on the calling thread, which is used to get yielded values from the resumed thread 42 | auto ar = pushAR(from); 43 | ar->base = slot; 44 | ar->savedTop = from->stackIndex; 45 | ar->vargBase = slot; 46 | ar->returnSlot = slot; 47 | ar->func = nullptr; 48 | ar->pc = nullptr; 49 | ar->expectedResults = expectedResults; 50 | ar->numTailcalls = 0; 51 | ar->firstResult = 0; 52 | ar->numResults = 0; 53 | ar->unwindCounter = 0; 54 | ar->unwindReturn = nullptr; 55 | from->stackBase = slot; 56 | from->stackIndex = slot + 1; 57 | 58 | auto savedState = from->state; 59 | from->state = CrocThreadState_Waiting; 60 | t->threadThatResumedThis = from; 61 | 62 | auto failed = tryCode(from, 0, [&] 63 | { 64 | if(t->state == CrocThreadState_Initial) 65 | { 66 | checkStack(t, cast(AbsStack)(numParams + 2)); 67 | t->stack[1] = Value::from(t->coroFunc); 68 | t->stack.slicea(2, 2 + numParams, from->stack.slice(slot + 1, slot + 1 + numParams)); 69 | t->stackIndex += numParams; 70 | 71 | auto result = callPrologue(t, cast(AbsStack)1, -1, numParams); 72 | assert(result); 73 | #ifdef NDEBUG 74 | (void)result; 75 | #endif 76 | execute(t, t->arIndex); 77 | } 78 | else 79 | { 80 | // Get rid of 'this' 81 | numParams--; 82 | saveResults(t, from, slot + 2, numParams); 83 | callEpilogue(t); 84 | execute(t, t->savedStartARIndex); 85 | } 86 | }); 87 | 88 | from->state = savedState; 89 | from->vm->curThread = from; 90 | 91 | if(failed) 92 | { 93 | assert(t->state == CrocThreadState_Dead); 94 | croc_eh_pushStd(*from, "HaltException"); 95 | bool isHalt = croc_isInstanceOf(*from, -2, -1); 96 | croc_popTop(*from); 97 | 98 | if(isHalt) 99 | { 100 | assert(t->arIndex == 0); 101 | assert(t->upvalHead == nullptr); 102 | assert(t->resultIndex == 0); 103 | assert(t->nativeCallDepth == 0); 104 | } 105 | else 106 | { 107 | saveResults(from, from, from->stackIndex - 1, 1); 108 | callEpilogue(from); // get rid of the resume AR 109 | from->stackIndex = slot + 1; 110 | continueTraceback(from, *getValue(from, -1)); 111 | croc_eh_rethrow(*from); 112 | } 113 | } 114 | 115 | // Move the values from the yielded thread's stack to the calling thread's stack 116 | saveResults(from, t, t->stackIndex - t->numYields, t->numYields); 117 | t->stackIndex -= t->numYields; 118 | callEpilogue(from); 119 | } 120 | } -------------------------------------------------------------------------------- /src/croc/internal/thread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_THREAD_HPP 2 | #define CROC_INTERNAL_THREAD_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | void yieldImpl(Thread* t, AbsStack firstValue, word numValues, word expectedResults); 9 | void resume(Thread* t, Thread* from, AbsStack slot, uword expectedResults, uword numParams); 10 | } 11 | 12 | #endif -------------------------------------------------------------------------------- /src/croc/internal/variables.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/variables.hpp" 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | Value getGlobalImpl(Thread* t, String* name, Namespace* env) 9 | { 10 | if(auto glob = env->get(name)) 11 | return *glob; 12 | 13 | if(env->root) 14 | { 15 | if(auto glob = env->root->get(name)) 16 | return *glob; 17 | } 18 | 19 | croc_eh_throwStd(*t, "NameError", "Attempting to get a nonexistent global '%s'", name->toCString()); 20 | assert(false); 21 | return Value::nullValue; // dummy 22 | } 23 | 24 | void setGlobalImpl(Thread* t, String* name, Namespace* env, Value val) 25 | { 26 | if(env->setIfExists(t->vm->mem, name, val)) 27 | return; 28 | 29 | if(env->root && env->root->setIfExists(t->vm->mem, name, val)) 30 | return; 31 | 32 | croc_eh_throwStd(*t, "NameError", "Attempting to set a nonexistent global '%s'", name->toCString()); 33 | assert(false); 34 | } 35 | 36 | void newGlobalImpl(Thread* t, String* name, Namespace* env, Value val) 37 | { 38 | if(env->contains(name)) 39 | croc_eh_throwStd(*t, "NameError", "Attempting to create global '%s' that already exists", 40 | name->toCString()); 41 | 42 | env->set(t->vm->mem, name, val); 43 | } 44 | } -------------------------------------------------------------------------------- /src/croc/internal/variables.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_INTERNAL_VARIABLES_HPP 2 | #define CROC_INTERNAL_VARIABLES_HPP 3 | 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | Value getGlobalImpl(Thread* t, String* name, Namespace* env); 9 | void setGlobalImpl(Thread* t, String* name, Namespace* env, Value val); 10 | void newGlobalImpl(Thread* t, String* name, Namespace* env, Value val); 11 | } 12 | 13 | #endif -------------------------------------------------------------------------------- /src/croc/mainpage.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | \mainpage 3 | 4 | \section Welcome 5 | 6 | This is the documentation for the Croc native API. To get started, click the "API" button above to browse the docs by 7 | section, or the "Globals" button to browse all API functions, types etc. by name. 8 | */ -------------------------------------------------------------------------------- /src/croc/stdlib/all.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_STDLIB_ALL_HPP 2 | #define CROC_STDLIB_ALL_HPP 3 | 4 | namespace croc 5 | { 6 | void initArrayLib(CrocThread* t); 7 | void initAsciiLib(CrocThread* t); 8 | void initCompilerLib(CrocThread* t); 9 | void initConsoleLib(CrocThread* t); 10 | void initDebugLib(CrocThread* t); 11 | void initDocsLib(CrocThread* t); 12 | void initDoctoolsLibs(CrocThread* t); 13 | void initEnvLib(CrocThread* t); 14 | void initExceptionsLib(CrocThread* t); 15 | void initFileLib(CrocThread* t); 16 | void initGCLib(CrocThread* t); 17 | void initHashLib(CrocThread* t); 18 | void initJSONLib(CrocThread* t); 19 | void initMathLib(CrocThread* t); 20 | void initMemblockLib(CrocThread* t); 21 | void initMiscLib(CrocThread* t); 22 | void initMiscLib_Vector(CrocThread* t); 23 | void initModulesLib(CrocThread* t); 24 | void initObjectLib(CrocThread* t); 25 | void initOSLib(CrocThread* t); 26 | void initPathLib(CrocThread* t); 27 | void initReplLib(CrocThread* t); 28 | void initSerializationLib(CrocThread* t); 29 | void initStreamLib(CrocThread* t); 30 | void initStringLib(CrocThread* t); 31 | void initStringLib_StringBuffer(CrocThread* t); 32 | void initTextLib(CrocThread* t); 33 | void initThreadLib(CrocThread* t); 34 | void initTimeLib(CrocThread* t); 35 | 36 | #ifdef CROC_BUILTIN_DOCS 37 | void docExceptionsLib(CrocThread* t); 38 | void docGCLib(CrocThread* t); 39 | void docMiscLib(CrocThread* t); 40 | void docMiscLib_Vector(CrocThread* t, CrocDoc* doc); 41 | void docStringLib(CrocThread* t); 42 | void docStringLib_StringBuffer(CrocThread* t, CrocDoc* doc); 43 | #endif 44 | } 45 | 46 | #endif -------------------------------------------------------------------------------- /src/croc/stdlib/console.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/stack.hpp" 4 | #include "croc/stdlib/helpers/oscompat.hpp" 5 | #include "croc/stdlib/helpers/register.hpp" 6 | #include "croc/types/base.hpp" 7 | 8 | namespace croc 9 | { 10 | namespace 11 | { 12 | #include "croc/stdlib/console.croc.hpp" 13 | } 14 | 15 | void initConsoleLib(CrocThread* t) 16 | { 17 | croc_table_new(t, 0); 18 | auto in = oscompat::getStdin(t); 19 | if(in == oscompat::InvalidHandle) 20 | oscompat::throwIOEx(t); 21 | 22 | auto out = oscompat::getStdout(t); 23 | if(out == oscompat::InvalidHandle) 24 | oscompat::throwIOEx(t); 25 | 26 | auto err = oscompat::getStderr(t); 27 | if(err == oscompat::InvalidHandle) 28 | oscompat::throwIOEx(t); 29 | 30 | croc_pushNativeobj(t, cast(void*)cast(uword)in); croc_fielda(t, -2, "stdin"); 31 | croc_pushNativeobj(t, cast(void*)cast(uword)out); croc_fielda(t, -2, "stdout"); 32 | croc_pushNativeobj(t, cast(void*)cast(uword)err); croc_fielda(t, -2, "stderr"); 33 | croc_newGlobal(t, "_consoletmp"); 34 | 35 | registerModuleFromString(t, "console", console_croc_text, "console.croc"); 36 | 37 | croc_vm_pushGlobals(t); 38 | croc_pushString(t, "_consoletmp"); 39 | croc_removeKey(t, -2); 40 | croc_popTop(t); 41 | } 42 | } -------------------------------------------------------------------------------- /src/croc/stdlib/console.croc: -------------------------------------------------------------------------------- 1 | /** 2 | The console library provides basic console IO by wrapping the standard input, output, and error streams in 3 | \link{stream.Stream} objects. This is a safe library. It also exports some functions into the global namespace for 4 | convenience. 5 | */ 6 | module console 7 | 8 | local TextReader, TextWriter, NativeStream = stream.TextReader, stream.TextWriter, stream.NativeStream 9 | 10 | /** 11 | These are the \link{stream.Stream} objects that wrap the standard input, output, and error streams. You can, however, 12 | reassign these at will, which makes redirecting Croc's input and output trivial. For instance, if you wanted to 13 | change the standard input stream to use a file instead of the console, you could simply do it like this: 14 | 15 | \code 16 | // Good idea to hold onto the old stream in case you want to set it back 17 | local oldStream = console.stdin 18 | console.stdin = TextReader(file.inFile("somefile.txt")) 19 | 20 | // Now any use of stdin (including the global readln() function) will read from "somefile.txt" instead. 21 | \endcode 22 | */ 23 | global stdin, stdout, stderr 24 | 25 | stdin = TextReader(NativeStream(_consoletmp.stdin, "r"), "utf-8", "replace") 26 | stdout = TextWriter(NativeStream(_consoletmp.stdout, "w"), "utf-8", "replace") 27 | stderr = TextWriter(NativeStream(_consoletmp.stderr, "w"), "utf-8", "replace") 28 | 29 | /** 30 | This is a shortcut for calling \tt{stdout.write} with the given arguments. 31 | 32 | Also mirrored in the global namespace so you can access it unqualified. 33 | 34 | \see \link{stream.TextWriter.write} 35 | */ 36 | function write(vararg) 37 | { 38 | stdout.write(vararg) 39 | } 40 | 41 | /** 42 | This is a shortcut for calling \tt{stdout.writeln} with the given arguments. 43 | 44 | Also mirrored in the global namespace so you can access it unqualified. 45 | 46 | \see \link{stream.TextWriter.writeln} 47 | */ 48 | function writeln(vararg) 49 | { 50 | stdout.writeln(vararg) 51 | } 52 | 53 | /** 54 | This is a shortcut for calling \tt{stdout.writef} with the given arguments. 55 | 56 | Also mirrored in the global namespace so you can access it unqualified. 57 | 58 | \see \link{stream.TextWriter.writef} 59 | */ 60 | function writef(fmt: string, vararg) 61 | { 62 | stdout.writef(fmt, vararg) 63 | } 64 | 65 | /** 66 | This is a shortcut for calling \tt{stdout.writefln} with the given arguments. 67 | 68 | Also mirrored in the global namespace so you can access it unqualified. 69 | 70 | \see \link{stream.TextWriter.writefln} 71 | */ 72 | function writefln(fmt: string, vararg) 73 | { 74 | stdout.writefln(fmt, vararg) 75 | } 76 | 77 | /** 78 | This is a shortcut for calling \tt{stdin.readln}. 79 | 80 | Also mirrored in the global namespace so you can access it unqualified. 81 | 82 | \see \link{stream.TextReader.readln} 83 | */ 84 | function readln() = 85 | stdin.readln() 86 | 87 | // Export write[f][ln] and readln to the global namespace 88 | _G.write = write 89 | _G.writeln = writeln 90 | _G.writef = writef 91 | _G.writefln = writefln 92 | _G.readln = readln -------------------------------------------------------------------------------- /src/croc/stdlib/docs.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/stack.hpp" 4 | #include "croc/stdlib/helpers/register.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | namespace croc 8 | { 9 | namespace 10 | { 11 | #include "croc/stdlib/docs.croc.hpp" 12 | 13 | word_t _processComment(CrocThread* t) 14 | { 15 | croc_ex_checkStringParam(t, 1); 16 | croc_ex_checkParam(t, 2, CrocType_Table); 17 | croc_dup(t, 2); 18 | croc_dup(t, 1); 19 | croc_compiler_processDocComment(t); 20 | return 1; 21 | } 22 | 23 | word_t _parseCommentText(CrocThread* t) 24 | { 25 | croc_ex_checkStringParam(t, 1); 26 | croc_dup(t, 1); 27 | croc_compiler_parseDocCommentText(t); 28 | return 1; 29 | } 30 | 31 | word_t _getMetatable(CrocThread* t) 32 | { 33 | auto type = CrocType_Null; 34 | croc_ex_checkParam(t, 1, CrocType_String); 35 | auto s = getCrocstr(t, 1); 36 | 37 | if(s == ATODA("null")) type = CrocType_Null; 38 | if(s == ATODA("bool")) type = CrocType_Bool; else 39 | if(s == ATODA("int")) type = CrocType_Int; else 40 | if(s == ATODA("float")) type = CrocType_Float; else 41 | if(s == ATODA("nativeobj")) type = CrocType_Nativeobj; else 42 | if(s == ATODA("string")) type = CrocType_String; else 43 | if(s == ATODA("weakref")) type = CrocType_Weakref; else 44 | if(s == ATODA("table")) type = CrocType_Table; else 45 | if(s == ATODA("namespace")) type = CrocType_Namespace; else 46 | if(s == ATODA("array")) type = CrocType_Array; else 47 | if(s == ATODA("memblock")) type = CrocType_Memblock; else 48 | if(s == ATODA("function")) type = CrocType_Function; else 49 | if(s == ATODA("funcdef")) type = CrocType_Funcdef; else 50 | if(s == ATODA("class")) type = CrocType_Class; else 51 | if(s == ATODA("instance")) type = CrocType_Instance; else 52 | if(s == ATODA("thread")) type = CrocType_Thread; else 53 | { 54 | croc_pushBool(t, false); 55 | return 1; 56 | } 57 | 58 | croc_pushBool(t, true); 59 | croc_vm_pushTypeMT(t, type); 60 | return 2; 61 | } 62 | 63 | const CrocRegisterFunc _globalFuncs[] = 64 | { 65 | {"processComment", 2, &_processComment }, 66 | {"parseCommentText", 2, &_parseCommentText}, 67 | {"getMetatable", 1, &_getMetatable }, 68 | {nullptr, 0, nullptr} 69 | }; 70 | } 71 | 72 | void initDocsLib(CrocThread* t) 73 | { 74 | croc_table_new(t, 0); 75 | croc_ex_registerFields(t, _globalFuncs); 76 | croc_newGlobal(t, "_docstmp"); 77 | 78 | registerModuleFromString(t, "docs", docs_croc_text, "docs.croc"); 79 | 80 | croc_vm_pushGlobals(t); 81 | croc_pushString(t, "_docstmp"); 82 | croc_removeKey(t, -2); 83 | croc_popTop(t); 84 | } 85 | } -------------------------------------------------------------------------------- /src/croc/stdlib/doctools.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/stack.hpp" 4 | #include "croc/stdlib/helpers/register.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | namespace croc 8 | { 9 | namespace 10 | { 11 | #include "croc/stdlib/doctools_output.croc.hpp" 12 | #include "croc/stdlib/doctools_console.croc.hpp" 13 | #include "croc/stdlib/doctools_trac.croc.hpp" 14 | } 15 | 16 | void initDoctoolsLibs(CrocThread* t) 17 | { 18 | registerModuleFromString(t, "doctools.output", doctools_output_croc_text, "doctools/output.croc"); 19 | registerModuleFromString(t, "doctools.console", doctools_console_croc_text, "doctools/console.croc"); 20 | registerModuleFromString(t, "doctools.trac", doctools_trac_croc_text, "doctools/trac.croc"); 21 | } 22 | } -------------------------------------------------------------------------------- /src/croc/stdlib/env.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "croc/api.h" 5 | #include "croc/internal/stack.hpp" 6 | #include "croc/stdlib/helpers/oscompat.hpp" 7 | #include "croc/stdlib/helpers/register.hpp" 8 | #include "croc/types/base.hpp" 9 | 10 | namespace croc 11 | { 12 | namespace 13 | { 14 | const StdlibRegisterInfo _getVar_info = 15 | { 16 | Docstr(DFunc("getVar") DParam("name", "string") DParamD("defaultVal", "string", "null") 17 | R"(Get the value of the environment variable named \tt{name}. 18 | 19 | \param[name] is the name of the environment variable to retrieve. 20 | \param[defaultVal] is an optional value that will be returned in the case that there is no environment variable 21 | named \tt{name}. 22 | 23 | \returns the value of the environment variable, if any. If no variable named \tt{name} exists, it will return 24 | \tt{defaultVal} instead (which defaults to \tt{null}).)"), 25 | 26 | "getVar", 2 27 | }; 28 | 29 | word_t _getVar(CrocThread* t) 30 | { 31 | croc_ex_checkStringParam(t, 1); 32 | auto haveDef = croc_ex_optParam(t, 2, CrocType_String); 33 | auto name = getCrocstr(t, 1); 34 | 35 | if(!oscompat::getEnv(t, name)) 36 | { 37 | if(haveDef) 38 | croc_dup(t, 2); 39 | else 40 | croc_pushNull(t); 41 | } 42 | 43 | return 1; 44 | } 45 | 46 | const StdlibRegisterInfo _getAllVars_info = 47 | { 48 | Docstr(DFunc("getAllVars") 49 | R"(\returns a table containing all variables in this process's environment. The keys are the names and the values 50 | are... the values.)"), 51 | 52 | "getAllVars", 0 53 | }; 54 | 55 | word_t _getAllVars(CrocThread* t) 56 | { 57 | oscompat::getAllEnvVars(t); 58 | return 1; 59 | } 60 | 61 | const StdlibRegisterInfo _setVar_info = 62 | { 63 | Docstr(DFunc("setVar") DParam("name", "string") DParamD("val", "string", "null") 64 | R"(Set or unset a variable in this process's environment. 65 | 66 | \param[name] is the name of the variable to set or unset. 67 | \param[val] is the value to set the variable to. If you set the variable to the empty string or \tt{null}, the 68 | variable will be unset (removed from the process's environment).)"), 69 | 70 | "setVar", 2 71 | }; 72 | 73 | word_t _setVar(CrocThread* t) 74 | { 75 | croc_ex_checkStringParam(t, 1); 76 | auto haveVal = croc_ex_optParam(t, 2, CrocType_String); 77 | auto name = getCrocstr(t, 1); 78 | 79 | if(haveVal) 80 | oscompat::setEnv(t, name, getCrocstr(t, 2)); 81 | else 82 | oscompat::setEnv(t, name, crocstr()); 83 | 84 | return 0; 85 | } 86 | 87 | const StdlibRegister _globalFuncs[] = 88 | { 89 | _DListItem(_getVar), 90 | _DListItem(_getAllVars), 91 | _DListItem(_setVar), 92 | _DListEnd 93 | }; 94 | 95 | word loader(CrocThread* t) 96 | { 97 | registerGlobals(t, _globalFuncs); 98 | return 0; 99 | } 100 | } 101 | 102 | void initEnvLib(CrocThread* t) 103 | { 104 | registerModule(t, "env", &loader); 105 | croc_pushGlobal(t, "env"); 106 | #ifdef CROC_BUILTIN_DOCS 107 | CrocDoc doc; 108 | croc_ex_doc_init(t, &doc, __FILE__); 109 | croc_ex_doc_push(&doc, 110 | DModule("env") 111 | R"(This small module allows you to get and set process environment variables.)"); 112 | docFields(&doc, _globalFuncs); 113 | croc_ex_doc_pop(&doc, -1); 114 | croc_ex_doc_finish(&doc); 115 | #endif 116 | croc_popTop(t); 117 | } 118 | } -------------------------------------------------------------------------------- /src/croc/stdlib/helpers/format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_STDLIB_HELPERS_FORMAT_HPP 2 | #define CROC_STDLIB_HELPERS_FORMAT_HPP 3 | 4 | #include 5 | 6 | #include "croc/types/base.hpp" 7 | 8 | namespace croc 9 | { 10 | uword formatImpl(CrocThread* t, uword numParams); 11 | uword formatImpl(CrocThread* t, uword startIndex, uword numParams); 12 | } 13 | 14 | #endif -------------------------------------------------------------------------------- /src/croc/stdlib/helpers/json.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_STDLIB_HELPERS_JSON_HPP 2 | #define CROC_STDLIB_HELPERS_JSON_HPP 3 | 4 | #include 5 | 6 | #include "croc/api.h" 7 | #include "croc/types/base.hpp" 8 | 9 | namespace croc 10 | { 11 | word_t fromJSON(CrocThread* t, crocstr source); 12 | void toJSON(CrocThread* t, word_t root, bool pretty, std::function output, std::function nl); 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /src/croc/stdlib/helpers/oscompat.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_STDLIB_HELPERS_OSCOMPAT_HPP 2 | #define CROC_STDLIB_HELPERS_OSCOMPAT_HPP 3 | 4 | #include 5 | 6 | #ifdef _WIN32 7 | #include "windows.h" 8 | #else 9 | #include "unistd.h" 10 | #include "fcntl.h" 11 | #endif 12 | 13 | #include "croc/types/base.hpp" 14 | 15 | namespace croc 16 | { 17 | namespace oscompat 18 | { 19 | #ifdef _WIN32 20 | typedef HANDLE FileHandle; 21 | const FileHandle InvalidHandle = INVALID_HANDLE_VALUE; 22 | 23 | enum class FileAccess : DWORD 24 | { 25 | Read = GENERIC_READ, 26 | Write = GENERIC_WRITE, 27 | ReadWrite = GENERIC_READ | GENERIC_WRITE 28 | }; 29 | 30 | enum class FileCreate : DWORD 31 | { 32 | OpenExisting = OPEN_EXISTING, 33 | CreateIfNeeded = CREATE_ALWAYS, 34 | Append = OPEN_ALWAYS, 35 | MustNotExist = CREATE_NEW 36 | }; 37 | 38 | enum class Whence : DWORD 39 | { 40 | Begin = FILE_BEGIN, 41 | Current = FILE_CURRENT, 42 | End = FILE_END 43 | }; 44 | 45 | typedef HMODULE LibraryHandle; 46 | typedef FARPROC LibraryProc; 47 | #else 48 | typedef int FileHandle; 49 | const FileHandle InvalidHandle = -1; 50 | 51 | enum class FileAccess 52 | { 53 | Read = O_RDONLY, 54 | Write = O_WRONLY, 55 | ReadWrite = O_RDWR 56 | }; 57 | 58 | enum class FileCreate 59 | { 60 | OpenExisting = 0, 61 | CreateIfNeeded = O_CREAT | O_TRUNC, 62 | Append = O_CREAT, 63 | MustNotExist = O_CREAT | O_EXCL 64 | }; 65 | 66 | enum class Whence 67 | { 68 | Begin = SEEK_SET, 69 | Current = SEEK_CUR, 70 | End = SEEK_END 71 | }; 72 | 73 | typedef void* LibraryHandle; 74 | typedef void* LibraryProc; 75 | #endif 76 | 77 | template 78 | union LibraryProcPun 79 | { 80 | LibraryProc lp; 81 | T fp; 82 | }; 83 | 84 | static_assert(sizeof(FileHandle) <= sizeof(void*), "Can't fit file handle into a nativeobj"); 85 | 86 | enum class FileType 87 | { 88 | File, 89 | Dir, 90 | Link, 91 | Other 92 | }; 93 | 94 | typedef int64_t Time; 95 | 96 | struct DateTime 97 | { 98 | uint16_t year, month, day, hour, min, sec, msec; 99 | }; 100 | 101 | struct FileInfo 102 | { 103 | FileType type; 104 | uint64_t size; 105 | Time created; 106 | Time modified; 107 | Time accessed; 108 | }; 109 | 110 | typedef FILE* ProcessHandle; 111 | 112 | // Most of these functions have some kind of "invalid" return value. If that's returned, then the error message will 113 | // be sitting on top of the thread's stack. 114 | 115 | // Error handling 116 | void pushSystemErrorMsg(CrocThread* t); 117 | void throwIOEx(CrocThread* t); 118 | void throwOSEx(CrocThread* t); 119 | 120 | // File streams 121 | FileHandle openFile(CrocThread* t, crocstr name, FileAccess access, FileCreate create); 122 | bool truncate(CrocThread* t, FileHandle f); 123 | FileHandle fromCFile(CrocThread* t, FILE* f); 124 | 125 | // Console streams 126 | FileHandle getStdin(CrocThread* t); 127 | FileHandle getStdout(CrocThread* t); 128 | FileHandle getStderr(CrocThread* t); 129 | 130 | // General-purpose streams 131 | bool isValidHandle(FileHandle f); 132 | int64_t read(CrocThread* t, FileHandle f, DArray data); 133 | int64_t write(CrocThread* t, FileHandle f, DArray data); 134 | uint64_t seek(CrocThread* t, FileHandle f, uint64_t pos, Whence whence); 135 | bool flush(CrocThread* t, FileHandle f); 136 | bool close(CrocThread* t, FileHandle f); 137 | 138 | // Environment variables 139 | bool getEnv(CrocThread* t, crocstr name); 140 | void setEnv(CrocThread* t, crocstr name, crocstr val); 141 | void getAllEnvVars(CrocThread* t); 142 | 143 | // FS stuff 144 | bool listDir(CrocThread* t, crocstr path, bool includeHidden, std::function dg); 145 | bool pushCurrentDir(CrocThread* t); 146 | bool changeDir(CrocThread* t, crocstr path); 147 | bool makeDir(CrocThread* t, crocstr path); 148 | bool removeDir(CrocThread* t, crocstr path); 149 | bool getInfo(CrocThread* t, crocstr name, FileInfo* info); 150 | bool copyFromTo(CrocThread* t, crocstr from, crocstr to, bool force); 151 | bool moveFromTo(CrocThread* t, crocstr from, crocstr to, bool force); 152 | bool remove(CrocThread* t, crocstr path); 153 | 154 | // Time 155 | void initTime(); 156 | uint64_t microTime(); 157 | Time sysTime(); 158 | DateTime timeToDateTime(Time t, bool isLocal); 159 | Time dateTimeToTime(DateTime t, bool isLocal); 160 | 161 | // Threading 162 | void sleep(uword msec); 163 | 164 | // Processes 165 | ProcessHandle openProcess(CrocThread* t, crocstr cmd, FileAccess access); 166 | FileHandle getProcessStream(CrocThread* t, ProcessHandle p); 167 | int closeProcess(CrocThread* t, ProcessHandle p); 168 | 169 | // Shared libraries 170 | LibraryHandle openLibrary(CrocThread* t, const char* name); 171 | LibraryHandle openLibraryMulti(CrocThread* t, const char** names); 172 | void closeLibrary(CrocThread* t, LibraryHandle lib); 173 | 174 | #ifdef _WIN32 175 | template 176 | void getProc(CrocThread* t, LibraryHandle lib, const char* name, T& proc) 177 | { 178 | LibraryProcPun pun; 179 | 180 | if((pun.lp = GetProcAddress(lib, name))) 181 | proc = pun.fp; 182 | else 183 | { 184 | pushSystemErrorMsg(t); 185 | croc_pushFormat(t, "Could not get procedure '%s': ", name); 186 | croc_swapTop(t); 187 | croc_cat(t, 2); 188 | throwOSEx(t); 189 | } 190 | } 191 | #else 192 | 193 | #endif 194 | } 195 | } 196 | 197 | #endif -------------------------------------------------------------------------------- /src/croc/stdlib/helpers/register.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/stdlib/helpers/register.hpp" 4 | #include "croc/types/base.hpp" 5 | #include "croc/util/str.hpp" 6 | 7 | namespace croc 8 | { 9 | namespace 10 | { 11 | void makeModuleNamespace(CrocThread* t, const char* name) 12 | { 13 | croc_pushGlobal(t, "_G"); 14 | 15 | delimiters(atoda(name), ATODA("."), [&](crocstr piece) 16 | { 17 | croc_pushStringn(t, cast(const char*)piece.ptr, piece.length); 18 | 19 | if(croc_hasFieldStk(t, -2, -1)) 20 | { 21 | croc_fieldStk(t, -2); 22 | croc_insertAndPop(t, -2); 23 | } 24 | else 25 | { 26 | croc_namespace_new(t, croc_getString(t, -1)); 27 | croc_insert(t, -3); 28 | croc_dup(t, -3); 29 | croc_fieldaStk(t, -3); 30 | croc_popTop(t); 31 | } 32 | }); 33 | } 34 | } 35 | 36 | void registerModule(CrocThread* t, const char* name, CrocNativeFunc loader) 37 | { 38 | makeModuleNamespace(t, name); 39 | croc_dupTop(t); 40 | croc_function_newWithEnv(t, name, 0, loader, 0); 41 | croc_swapTop(t); 42 | croc_call(t, -2, 0); 43 | } 44 | 45 | void registerModuleFromString(CrocThread* t, const char* name, const char* source, const char* sourceName) 46 | { 47 | makeModuleNamespace(t, name); 48 | 49 | croc_pushString(t, source); 50 | const char* modName; 51 | croc_compiler_compileModuleEx(t, sourceName, &modName); 52 | 53 | if(strcmp(name, modName) != 0) 54 | croc_eh_throwStd(t, "ImportException", 55 | "Import name (%s) does not match name given in module statement (%s)", name, modName); 56 | 57 | croc_swapTop(t); 58 | croc_dupTop(t); 59 | croc_function_newScriptWithEnv(t, -3); 60 | croc_swapTop(t); 61 | croc_call(t, -2, 0); 62 | croc_popTop(t); 63 | } 64 | 65 | #define MAKE_REGISTER_MULTI(Type)\ 66 | void register##Type##s(CrocThread* t, const StdlibRegister* funcs)\ 67 | {\ 68 | for(auto f = funcs; f->info.name != nullptr; f++)\ 69 | croc_ex_register##Type(t, (CrocRegisterFunc { f->info.name, f->info.maxParams, f->func}));\ 70 | } 71 | 72 | MAKE_REGISTER_MULTI(Global) 73 | MAKE_REGISTER_MULTI(Field) 74 | MAKE_REGISTER_MULTI(Method) 75 | 76 | #define MAKE_REGISTER_UV(Type)\ 77 | void register##Type##UV(CrocThread* t, const StdlibRegister* func)\ 78 | {\ 79 | uword numUVs = 0;\ 80 | for(auto f = func; f->info.name != nullptr; f++)\ 81 | {\ 82 | if(f[1].info.name == nullptr)\ 83 | {\ 84 | register##Type(t, *f, numUVs);\ 85 | break;\ 86 | }\ 87 | else\ 88 | {\ 89 | croc_function_new(t, f->info.name, f->info.maxParams, f->func, 0);\ 90 | numUVs++;\ 91 | }\ 92 | }\ 93 | } 94 | 95 | MAKE_REGISTER_UV(Global) 96 | MAKE_REGISTER_UV(Field) 97 | MAKE_REGISTER_UV(Method) 98 | 99 | #define MAKE_REGISTER(Type)\ 100 | void register##Type(CrocThread* t, const StdlibRegister& func, uword numUVs)\ 101 | {\ 102 | croc_ex_register##Type##UV(t, (CrocRegisterFunc { func.info.name, func.info.maxParams, func.func }), numUVs);\ 103 | } 104 | 105 | MAKE_REGISTER(Global) 106 | MAKE_REGISTER(Field) 107 | MAKE_REGISTER(Method) 108 | 109 | #ifdef CROC_BUILTIN_DOCS 110 | #define MAKE_DOC_MULTI(Type)\ 111 | void doc##Type##s(CrocDoc* d, const StdlibRegister* funcs)\ 112 | {\ 113 | for(auto f = funcs; f->info.docs != nullptr; f++)\ 114 | croc_ex_doc##Type(d, f->info.docs);\ 115 | } 116 | 117 | MAKE_DOC_MULTI(Global) 118 | MAKE_DOC_MULTI(Field) 119 | 120 | #define MAKE_DOC_UV(Type)\ 121 | void doc##Type##UV(CrocDoc* d, const StdlibRegister* func)\ 122 | {\ 123 | for(auto f = func; f->info.name != nullptr; f++)\ 124 | {\ 125 | if(f[1].info.name == nullptr)\ 126 | {\ 127 | croc_ex_doc##Type(d, f->info.docs);\ 128 | break;\ 129 | }\ 130 | }\ 131 | } 132 | 133 | MAKE_DOC_UV(Global) 134 | MAKE_DOC_UV(Field) 135 | 136 | #define MAKE_DOC(Type)\ 137 | void doc##Type(CrocDoc* d, const StdlibRegister& func)\ 138 | {\ 139 | croc_ex_doc##Type(d, func.info.docs);\ 140 | } 141 | 142 | MAKE_DOC(Global) 143 | MAKE_DOC(Field) 144 | #endif 145 | } -------------------------------------------------------------------------------- /src/croc/stdlib/helpers/register.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_STDLIB_HELPERS_REGISTER_HPP 2 | #define CROC_STDLIB_HELPERS_REGISTER_HPP 3 | 4 | #include "croc/api.h" 5 | #include "croc/types/base.hpp" 6 | 7 | namespace croc 8 | { 9 | #define DModule CROC_DOC_MODULE 10 | #define DFunc CROC_DOC_FUNC 11 | #define DClass CROC_DOC_CLASS 12 | #define DNs CROC_DOC_NS 13 | #define DField CROC_DOC_FIELD 14 | #define DFieldV CROC_DOC_FIELDV 15 | #define DVar CROC_DOC_VAR 16 | #define DVarV CROC_DOC_VARV 17 | #define DBase CROC_DOC_BASE 18 | #define DParamAny CROC_DOC_PARAMANY 19 | #define DParamAnyD CROC_DOC_PARAMANYD 20 | #define DParam CROC_DOC_PARAM 21 | #define DParamD CROC_DOC_PARAMD 22 | #define DVararg CROC_DOC_VARARG 23 | 24 | #define _DListItem(name) {name##_info, &name} 25 | #define _DListEnd {{nullptr, nullptr, 0}, nullptr} 26 | 27 | constexpr const char* Docstr(const char* s) 28 | { 29 | #ifdef CROC_BUILTIN_DOCS 30 | return s; 31 | #else 32 | return s - s; // teehee sneaky way to return nullptr AND use s 33 | #endif 34 | } 35 | 36 | struct StdlibRegisterInfo 37 | { 38 | const char* docs; 39 | const char* name; 40 | word maxParams; 41 | }; 42 | 43 | struct StdlibRegister 44 | { 45 | StdlibRegisterInfo info; 46 | CrocNativeFunc func; 47 | }; 48 | 49 | void registerModule(CrocThread* t, const char* name, CrocNativeFunc loader); 50 | void registerModuleFromString(CrocThread* t, const char* name, const char* source, const char* sourceName); 51 | void registerGlobals(CrocThread* t, const StdlibRegister* funcs); 52 | void registerFields(CrocThread* t, const StdlibRegister* funcs); 53 | void registerMethods(CrocThread* t, const StdlibRegister* funcs); 54 | void registerGlobalUV(CrocThread* t, const StdlibRegister* func); 55 | void registerFieldUV(CrocThread* t, const StdlibRegister* func); 56 | void registerMethodUV(CrocThread* t, const StdlibRegister* func); 57 | void registerGlobal(CrocThread* t, const StdlibRegister& func, uword numUVs); 58 | void registerField(CrocThread* t, const StdlibRegister& func, uword numUVs); 59 | void registerMethod(CrocThread* t, const StdlibRegister& func, uword numUVs); 60 | #ifdef CROC_BUILTIN_DOCS 61 | void docGlobals(CrocDoc* d, const StdlibRegister* funcs); 62 | void docFields(CrocDoc* d, const StdlibRegister* funcs); 63 | void docGlobalUV(CrocDoc* d, const StdlibRegister* func); 64 | void docFieldUV(CrocDoc* d, const StdlibRegister* func); 65 | void docGlobal(CrocDoc* d, const StdlibRegister& func); 66 | void docField(CrocDoc* d, const StdlibRegister& func); 67 | #endif 68 | 69 | } 70 | 71 | #endif -------------------------------------------------------------------------------- /src/croc/stdlib/json.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "croc/api.h" 5 | #include "croc/internal/stack.hpp" 6 | #include "croc/stdlib/helpers/json.hpp" 7 | #include "croc/stdlib/helpers/register.hpp" 8 | #include "croc/types/base.hpp" 9 | 10 | namespace croc 11 | { 12 | namespace 13 | { 14 | const StdlibRegisterInfo _fromJSON_info = 15 | { 16 | Docstr(DFunc("fromJSON") DParam("j", "string") 17 | R"(Parses the JSON \tt{j} into a Croc representation using null, bool, int, float, string, table, and array. 18 | 19 | The string must contain well-formed JSON, and the root object must be a JSON object or array. There must be no extra 20 | data after the root object either. 21 | 22 | This parser is strictly conforming; all object keys must be quoted and there can be no spurious commas. Sadly. 23 | 24 | \returns the root object. 25 | 26 | \throws[LexicalException] if there are lexical errors in \tt{j}. 27 | \throws[SyntaxException] if there are syntactic errors in \tt{j}.)"), 28 | 29 | "fromJSON", 1 30 | }; 31 | 32 | word_t _fromJSON(CrocThread* t) 33 | { 34 | croc_ex_checkParam(t, 1, CrocType_String); 35 | fromJSON(t, getCrocstr(t, 1)); 36 | return 1; 37 | } 38 | 39 | const StdlibRegisterInfo _toJSON_info = 40 | { 41 | Docstr(DFunc("toJSON") DParamAny("root") DParamD("pretty", "bool", "false") 42 | R"(Converts \tt{root} to a JSON string representation and returns that string. 43 | 44 | If you want to send JSON to a stream of some kind, have a look at \link{writeJSON}. 45 | 46 | \param[root] must (currently) be a table or array, and must recursively contain only null, bool, int, float, string, 47 | table, and array values. Any circularly-referenced objects will be detected and throw an exception. 48 | \param[pretty] controls whether or not whitespace and newlines are inserted into the output. If it's \tt{false}, 49 | there will be as little whitespace as possible and no newlines. If it's \tt{true}, newlines, whitespace, and 50 | indentation will be inserted to make it at least somewhat human-readable. 51 | 52 | \throws[TypeError] if \tt{root} is not a table or array, or if any non-convertible types are found during 53 | conversion. 54 | \throws[ValueError] if the object graph is invalid somehow.)"), 55 | 56 | "toJSON", 2 57 | }; 58 | 59 | word_t _toJSON(CrocThread* t) 60 | { 61 | croc_ex_checkAnyParam(t, 1); 62 | auto pretty = croc_ex_optBoolParam(t, 2, false); 63 | 64 | CrocStrBuffer buf; 65 | croc_ex_buffer_init(t, &buf); 66 | 67 | auto output = [&](crocstr s) { croc_ex_buffer_addStringn(&buf, cast(const char*)s.ptr, s.length); }; 68 | auto newline = [&]() { croc_ex_buffer_addString(&buf, "\n"); }; 69 | 70 | toJSON(t, 1, pretty, output, newline); 71 | croc_ex_buffer_finish(&buf); 72 | return 1; 73 | } 74 | 75 | const StdlibRegisterInfo _writeJSON_info = 76 | { 77 | Docstr(DFunc("writeJSON") DParamAny("dest") DParamAny("root") DParamD("pretty", "bool", "false") 78 | R"(Like \link{toJSON}, but instead of returning a string, it calls methods of \tt{dest} to output the JSON. 79 | 80 | \tt{dest} can be any type which satisfies this interface: 81 | 82 | \blist 83 | \li It must have a method \tt{write} which will always be called with a single string parameter. 84 | \li It must have a method \tt{writeln} which will always be called without any parameters. This will only 85 | be called if the \tt{pretty} parameter is \tt{true}. 86 | \endlist 87 | 88 | The \link{stream.TextWriter} class satisfies this interface, but you can use any object which does.)"), 89 | 90 | "writeJSON", 3 91 | }; 92 | 93 | word_t _writeJSON(CrocThread* t) 94 | { 95 | croc_ex_checkAnyParam(t, 2); 96 | auto pretty = croc_ex_optBoolParam(t, 3, false); 97 | 98 | auto output = [&](crocstr s) 99 | { 100 | croc_dup(t, 1); 101 | croc_pushNull(t); 102 | pushCrocstr(t, s); 103 | croc_methodCall(t, -3, "write", 0); 104 | }; 105 | 106 | auto newline = [&]() 107 | { 108 | croc_dup(t, 1); 109 | croc_pushNull(t); 110 | croc_methodCall(t, -2, "writeln", 0); 111 | }; 112 | 113 | toJSON(t, 2, pretty, output, newline); 114 | return 0; 115 | } 116 | 117 | const StdlibRegister _globalFuncs[] = 118 | { 119 | _DListItem(_fromJSON), 120 | _DListItem(_toJSON), 121 | _DListItem(_writeJSON), 122 | _DListEnd 123 | }; 124 | 125 | word loader(CrocThread* t) 126 | { 127 | registerGlobals(t, _globalFuncs); 128 | return 0; 129 | } 130 | } 131 | 132 | void initJSONLib(CrocThread* t) 133 | { 134 | registerModule(t, "json", &loader); 135 | croc_pushGlobal(t, "json"); 136 | #ifdef CROC_BUILTIN_DOCS 137 | CrocDoc doc; 138 | croc_ex_doc_init(t, &doc, __FILE__); 139 | croc_ex_doc_push(&doc, 140 | DModule("json") 141 | R"(\link[http://en.wikipedia.org/wiki/JSON]{JSON} is a standard for structured data interchange based on the 142 | JavaScript object notation. This library allows you to convert to and from JSON.)"); 143 | docFields(&doc, _globalFuncs); 144 | croc_ex_doc_pop(&doc, -1); 145 | croc_ex_doc_finish(&doc); 146 | #endif 147 | croc_popTop(t); 148 | } 149 | } -------------------------------------------------------------------------------- /src/croc/stdlib/modules.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/stack.hpp" 4 | #include "croc/stdlib/helpers/oscompat.hpp" 5 | #include "croc/stdlib/helpers/register.hpp" 6 | #include "croc/types/base.hpp" 7 | 8 | namespace croc 9 | { 10 | namespace 11 | { 12 | #include "croc/stdlib/modules.croc.hpp" 13 | } 14 | 15 | void initModulesLib(CrocThread* t) 16 | { 17 | croc_table_new(t, 1); 18 | croc_function_new(t, "_setfenv", 2, [](CrocThread* t) -> word_t 19 | { 20 | croc_dup(t, 2); 21 | croc_function_setEnv(t, 1); 22 | return 0; 23 | }, 0); 24 | croc_fielda(t, -2, "_setfenv"); 25 | 26 | croc_array_new(t, 0); 27 | for(auto addon = croc_vm_includedAddons(); *addon != nullptr; addon++) 28 | { 29 | croc_pushString(t, *addon); 30 | croc_cateq(t, -2, 1); 31 | } 32 | croc_fielda(t, -2, "IncludedAddons"); 33 | 34 | croc_function_new(t, "_existsTime", 1, [](CrocThread* t) -> word_t 35 | { 36 | croc_ex_checkParam(t, 1, CrocType_String); 37 | oscompat::FileInfo info; 38 | 39 | if(oscompat::getInfo(t, getCrocstr(t, 1), &info)) 40 | { 41 | croc_pushBool(t, true); 42 | croc_pushInt(t, cast(crocint_t)info.modified); 43 | return 2; 44 | } 45 | else 46 | { 47 | croc_pushBool(t, false); 48 | return 1; 49 | } 50 | }, 0); 51 | croc_fielda(t, -2, "_existsTime"); 52 | 53 | croc_function_new(t, "_getFileContents", 1, [](CrocThread* t) -> word_t 54 | { 55 | croc_ex_checkParam(t, 1, CrocType_String); 56 | auto name = getCrocstr(t, 1); 57 | auto f = oscompat::openFile(t, name, oscompat::FileAccess::Read, oscompat::FileCreate::OpenExisting); 58 | 59 | if(f == oscompat::InvalidHandle) 60 | { 61 | croc_pushFormat(t, "Error opening '%.*s' for reading: ", cast(int)name.length, name.ptr); 62 | croc_swapTop(t); 63 | croc_cat(t, 2); 64 | oscompat::throwIOEx(t); 65 | } 66 | 67 | auto size = oscompat::seek(t, f, 0, oscompat::Whence::End); 68 | 69 | if(size == cast(uint64_t)-1) 70 | oscompat::throwIOEx(t); 71 | 72 | if(oscompat::seek(t, f, 0, oscompat::Whence::Begin) == cast(uint64_t)-1) 73 | oscompat::throwIOEx(t); 74 | 75 | croc_memblock_new(t, size); 76 | auto ptr = cast(uint8_t*)croc_memblock_getData(t, -1); 77 | uword_t offset = 0; 78 | uword_t remaining = size; 79 | 80 | while(remaining > 0) 81 | { 82 | auto bytesRead = oscompat::read(t, f, DArray::n(ptr + offset, remaining)); 83 | 84 | if(bytesRead == 0) 85 | croc_eh_throwStd(t, "IOException", "Unexpected end of file"); 86 | 87 | offset += bytesRead; 88 | remaining -= bytesRead; 89 | } 90 | 91 | return 1; 92 | }, 0); 93 | croc_fielda(t, -2, "_getFileContents"); 94 | croc_newGlobal(t, "_modulestmp"); 95 | 96 | registerModuleFromString(t, "modules", modules_croc_text, "modules.croc"); 97 | 98 | croc_vm_pushGlobals(t); 99 | croc_pushString(t, "_modulestmp"); 100 | croc_removeKey(t, -2); 101 | croc_popTop(t); 102 | } 103 | } -------------------------------------------------------------------------------- /src/croc/stdlib/repl.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/api.h" 3 | #include "croc/internal/stack.hpp" 4 | #include "croc/stdlib/helpers/register.hpp" 5 | #include "croc/types/base.hpp" 6 | 7 | namespace croc 8 | { 9 | namespace 10 | { 11 | #include "croc/stdlib/repl.croc.hpp" 12 | } 13 | 14 | void initReplLib(CrocThread* t) 15 | { 16 | registerModuleFromString(t, "repl", repl_croc_text, "repl.croc"); 17 | } 18 | } -------------------------------------------------------------------------------- /src/croc/types/base.cpp: -------------------------------------------------------------------------------- 1 | #include "croc/base/hash.hpp" 2 | #include "croc/base/sanity.hpp" 3 | #include "croc/apitypes.h" 4 | #include "croc/types/base.hpp" 5 | 6 | namespace croc 7 | { 8 | const char* typeToString(CrocType t) 9 | { 10 | switch(t) 11 | { 12 | case CrocType_Null: return "null"; 13 | case CrocType_Bool: return "bool"; 14 | case CrocType_Int: return "int"; 15 | case CrocType_Float: return "float"; 16 | case CrocType_Nativeobj: return "nativeobj"; 17 | 18 | case CrocType_String: return "string"; 19 | case CrocType_Weakref: return "weakref"; 20 | 21 | case CrocType_Table: return "table"; 22 | case CrocType_Namespace: return "namespace"; 23 | case CrocType_Array: return "array"; 24 | case CrocType_Memblock: return "memblock"; 25 | case CrocType_Function: return "function"; 26 | case CrocType_Funcdef: return "funcdef"; 27 | case CrocType_Class: return "class"; 28 | case CrocType_Instance: return "instance"; 29 | case CrocType_Thread: return "thread"; 30 | 31 | case CrocType_Upval: return "upval"; 32 | 33 | default: assert(false); return nullptr; // dummy 34 | } 35 | } 36 | 37 | const Value Value::nullValue = {CrocType_Null, { cast(crocint)0 }}; 38 | 39 | hash_t Value::toHash() const 40 | { 41 | switch(this->type) 42 | { 43 | case CrocType_Null: return 0; 44 | case CrocType_Bool: return cast(hash_t)mBool; 45 | case CrocType_Int: return cast(hash_t)mInt; 46 | case CrocType_Float: return cast(hash_t)mFloat; 47 | case CrocType_Nativeobj: return cast(hash_t)cast(uword)mNativeobj; 48 | case CrocType_String: return mString->hash; 49 | default: return cast(hash_t)cast(uword)mGCObj; 50 | } 51 | } 52 | 53 | const char* ThreadStateStrings[5] = 54 | { 55 | "initial", 56 | "waiting", 57 | "running", 58 | "suspended", 59 | "dead" 60 | }; 61 | } -------------------------------------------------------------------------------- /src/croc/types/funcdef.cpp: -------------------------------------------------------------------------------- 1 | #include "croc/types/base.hpp" 2 | 3 | namespace croc 4 | { 5 | Funcdef* Funcdef::create(Memory& mem) 6 | { 7 | auto ret = ALLOC_OBJ(mem, Funcdef); 8 | ret->type = CrocType_Funcdef; 9 | return ret; 10 | } 11 | 12 | // Free a function definition. 13 | void Funcdef::free(Memory& mem, Funcdef* fd) 14 | { 15 | fd->paramMasks.free(mem); 16 | fd->returnMasks.free(mem); 17 | fd->upvals.free(mem); 18 | fd->innerFuncs.free(mem); 19 | fd->constants.free(mem); 20 | fd->code.free(mem); 21 | 22 | for(auto &st: fd->switchTables) 23 | st.offsets.clear(mem); 24 | 25 | fd->switchTables.free(mem); 26 | fd->lineInfo.free(mem); 27 | fd->upvalNames.free(mem); 28 | fd->locVarDescs.free(mem); 29 | FREE_OBJ(mem, Funcdef, fd); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/croc/types/function.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/base/writebarrier.hpp" 3 | #include "croc/types/base.hpp" 4 | 5 | #define SCRIPT_CLOSURE_EXTRA_SIZE(numUpvals) (sizeof(Upval*) * (numUpvals)) 6 | #define NATIVE_CLOSURE_EXTRA_SIZE(numUpvals) (sizeof(Value) * (numUpvals)) 7 | 8 | namespace croc 9 | { 10 | namespace 11 | { 12 | const uint32_t MaxParams = cast(uint32_t)-2; 13 | } 14 | 15 | // Create a script function. 16 | Function* Function::create(Memory& mem, Namespace* env, Funcdef* def) 17 | { 18 | if(def->environment && def->environment != env) 19 | return nullptr; 20 | 21 | if(def->cachedFunc) 22 | return def->cachedFunc; 23 | 24 | auto f = createPartial(mem, def->upvals.length); 25 | finishCreate(mem, f, env, def); 26 | return f; 27 | } 28 | 29 | // Partially construct a script closure. This is used by the serialization system. 30 | Function* Function::createPartial(Memory& mem, uword numUpvals) 31 | { 32 | auto f = ALLOC_OBJSZ(mem, Function, SCRIPT_CLOSURE_EXTRA_SIZE(numUpvals)); 33 | f->type = CrocType_Function; 34 | f->isNative = false; 35 | f->numUpvals = numUpvals; 36 | f->scriptUpvals().fill(nullptr); 37 | return f; 38 | } 39 | 40 | // Finish constructing a script closure. Also used by serialization. 41 | void Function::finishCreate(Memory& mem, Function* f, Namespace* env, Funcdef* def) 42 | { 43 | f->environment = env; 44 | f->name = def->name; 45 | f->numParams = def->numParams; 46 | 47 | if(def->isVararg) 48 | f->maxParams = MaxParams + 1; 49 | else 50 | f->maxParams = def->numParams; 51 | 52 | f->scriptFunc = def; 53 | 54 | if(def->environment == nullptr) 55 | { 56 | WRITE_BARRIER(mem, def); 57 | def->environment = env; 58 | } 59 | 60 | if(def->upvals.length == 0 && def->cachedFunc == nullptr) 61 | { 62 | WRITE_BARRIER(mem, def); 63 | def->cachedFunc = f; 64 | } 65 | } 66 | 67 | // Create a native function. 68 | Function* Function::create(Memory& mem, Namespace* env, String* name, uword numParams, CrocNativeFunc func, 69 | uword numUpvals) 70 | { 71 | auto f = ALLOC_OBJSZ(mem, Function, NATIVE_CLOSURE_EXTRA_SIZE(numUpvals)); 72 | f->type = CrocType_Function; 73 | f->nativeUpvals().fill(Value::nullValue); 74 | f->isNative = true; 75 | f->environment = env; 76 | f->name = name; 77 | f->numUpvals = numUpvals; 78 | f->numParams = numParams + 1; // +1 to include 'this' 79 | f->maxParams = f->numParams; 80 | f->nativeFunc = func; 81 | return f; 82 | } 83 | 84 | void Function::setNativeUpval(Memory& mem, uword idx, Value val) 85 | { 86 | auto slot = &this->nativeUpvals()[idx]; 87 | 88 | if(*slot != val) 89 | { 90 | if(slot->isGCObject() || val.isGCObject()) 91 | WRITE_BARRIER(mem, this); 92 | 93 | *slot = val; 94 | } 95 | } 96 | 97 | void Function::setEnvironment(Memory& mem, Namespace* ns) 98 | { 99 | if(this->environment != ns) 100 | { 101 | WRITE_BARRIER(mem, this); 102 | this->environment = ns; 103 | } 104 | } 105 | 106 | bool Function::isVararg() 107 | { 108 | if(this->isNative) 109 | return this->numParams == MaxParams + 1; 110 | else 111 | return this->scriptFunc->isVararg; 112 | } 113 | 114 | bool Function::isVarret() 115 | { 116 | if(this->isNative) 117 | return true; 118 | else 119 | return this->scriptFunc->isVarret; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/croc/types/instance.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/base/writebarrier.hpp" 3 | 4 | #define REMOVEKEYREF(mem, slot)\ 5 | do {\ 6 | if(!IS_KEY_MODIFIED(slot))\ 7 | (mem).decBuffer.add((mem), cast(GCObject*)(slot)->key);\ 8 | } while(false) 9 | 10 | #define REMOVEVALUEREF(mem, slot)\ 11 | do {\ 12 | if(!IS_VAL_MODIFIED(slot) && (slot)->value.isGCObject())\ 13 | (mem).decBuffer.add((mem), (slot)->value.toGCObject());\ 14 | } while(false) 15 | 16 | #define REMOVEFROZENVALUEREF(mem, slot)\ 17 | do {\ 18 | if(!(slot).modified && (slot).value.isGCObject())\ 19 | (mem).decBuffer.add((mem), (slot).value.toGCObject());\ 20 | } while(false) 21 | 22 | namespace croc 23 | { 24 | Instance* Instance::create(Memory& mem, Class* parent) 25 | { 26 | assert(parent->isFrozen); 27 | auto i = createPartial(mem, parent->numInstanceFields * sizeof(Array::Slot), parent->finalizer != nullptr); 28 | auto b = finishCreate(i, parent); 29 | assert(b); 30 | #ifdef NDEBUG 31 | (void)b; 32 | #endif 33 | return i; 34 | } 35 | 36 | Instance* Instance::createPartial(Memory& mem, uword extraSize, bool finalizable) 37 | { 38 | Instance* ret; 39 | if(finalizable) 40 | ret = ALLOC_OBJSZ_FINAL(mem, Instance, extraSize); 41 | else 42 | ret = ALLOC_OBJSZ(mem, Instance, extraSize); 43 | 44 | ret->type = CrocType_Instance; 45 | return ret; 46 | } 47 | 48 | bool Instance::finishCreate(Instance* i, Class* parent) 49 | { 50 | assert(parent->isFrozen); 51 | 52 | if(i->memSize != sizeof(Instance) + parent->numInstanceFields * sizeof(Array::Slot)) 53 | return false; 54 | 55 | i->parent = parent; 56 | i->fields = &parent->fields; 57 | 58 | void* hiddenFieldsLoc = cast(void*)(i + 1); 59 | 60 | if(parent->frozenFields.length > 0) 61 | { 62 | auto instFields = DArray::n(cast(Array::Slot*)(i + 1), parent->frozenFields.length); 63 | instFields.slicea(parent->frozenFields); 64 | 65 | for(auto &slot: instFields) 66 | slot.modified = slot.value.isGCObject(); 67 | 68 | hiddenFieldsLoc = cast(void*)(instFields.ptr + instFields.length); 69 | } 70 | 71 | if(parent->frozenHiddenFields.length > 0) 72 | { 73 | i->hiddenFieldsData = cast(Array::Slot*)hiddenFieldsLoc; 74 | 75 | auto instHiddenFields = DArray::n(cast(Array::Slot*)hiddenFieldsLoc, 76 | parent->frozenHiddenFields.length); 77 | instHiddenFields.slicea(parent->frozenHiddenFields); 78 | 79 | for(auto &slot: instHiddenFields) 80 | slot.modified = slot.value.isGCObject(); 81 | } 82 | 83 | return true; 84 | } 85 | 86 | bool Instance::setField(Memory& mem, String* name, Value value) 87 | { 88 | if(auto slot = this->fields->lookupNode(name)) 89 | { 90 | auto &fslot = (cast(Array::Slot*)(this + 1))[cast(uword)slot->value.mInt]; 91 | 92 | if(fslot.value != value) 93 | { 94 | REMOVEFROZENVALUEREF(mem, fslot); 95 | fslot.value = value; 96 | 97 | if(value.isGCObject()) 98 | { 99 | CONTAINER_WRITE_BARRIER(mem, this); 100 | fslot.modified = true; 101 | } 102 | else 103 | fslot.modified = false; 104 | } 105 | 106 | return true; 107 | } 108 | 109 | return false; 110 | } 111 | 112 | bool Instance::setHiddenField(Memory& mem, String* name, Value value) 113 | { 114 | if(this->hiddenFieldsData == nullptr) 115 | return false; 116 | 117 | if(auto slot = this->parent->hiddenFields.lookupNode(name)) 118 | { 119 | auto &fslot = this->hiddenFieldsData[cast(uword)slot->value.mInt]; 120 | 121 | if(fslot.value != value) 122 | { 123 | REMOVEFROZENVALUEREF(mem, fslot); 124 | fslot.value = value; 125 | 126 | if(value.isGCObject()) 127 | { 128 | CONTAINER_WRITE_BARRIER(mem, this); 129 | fslot.modified = true; 130 | } 131 | else 132 | fslot.modified = false; 133 | } 134 | 135 | return true; 136 | } 137 | 138 | return false; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/croc/types/memblock.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "croc/types/base.hpp" 4 | 5 | namespace croc 6 | { 7 | // Create a new memblock object of the given length. 8 | Memblock* Memblock::create(Memory& mem, uword itemLength) 9 | { 10 | auto ret = ALLOC_OBJ_ACYC(mem, Memblock); 11 | ret->type = CrocType_Memblock; 12 | ret->data = DArray::alloc(mem, itemLength); 13 | ret->ownData = true; 14 | return ret; 15 | } 16 | 17 | // Create a new memblock object that only views the given array, but does not own that data. 18 | Memblock* Memblock::createView(Memory& mem, DArray data) 19 | { 20 | auto ret = ALLOC_OBJ_ACYC(mem, Memblock); 21 | ret->type = CrocType_Memblock; 22 | ret->data = data; 23 | ret->ownData = false; 24 | return ret; 25 | } 26 | 27 | // Free a memblock object. 28 | void Memblock::free(Memory& mem, Memblock* m) 29 | { 30 | if(m->ownData) 31 | m->data.free(mem); 32 | 33 | FREE_OBJ(mem, Memblock, m); 34 | } 35 | 36 | // Change a memblock so it's a view into a given array (but does not own it). 37 | void Memblock::view(Memory& mem, DArray data) 38 | { 39 | if(this->ownData) 40 | this->data.free(mem); 41 | 42 | this->data = data; 43 | this->ownData = false; 44 | } 45 | 46 | // Resize a memblock object. 47 | void Memblock::resize(Memory& mem, uword newLength) 48 | { 49 | assert(this->ownData); 50 | this->data.resize(mem, newLength); 51 | } 52 | 53 | // Slice a memblock object to create a new memblock object with its own data. 54 | Memblock* Memblock::slice(Memory& mem, uword lo, uword hi) 55 | { 56 | auto n = ALLOC_OBJ_ACYC(mem, Memblock); 57 | n->type = CrocType_Memblock; 58 | n->data = this->data.slice(lo, hi).dup(mem); 59 | n->ownData = true; 60 | return n; 61 | } 62 | 63 | // Assign an entire other memblock into a slice of the destination memblock. Handles overlapping copies as well. 64 | void Memblock::sliceAssign(uword lo, uword hi, Memblock* other) 65 | { 66 | auto dest = this->data.slice(lo, hi); 67 | auto src = other->data; 68 | 69 | assert(dest.length == src.length); 70 | 71 | auto len = dest.length; 72 | 73 | if((dest.ptr + len) <= src.ptr || (src.ptr + len) <= dest.ptr) 74 | memcpy(dest.ptr, src.ptr, len); 75 | else 76 | memmove(dest.ptr, src.ptr, len); 77 | } 78 | 79 | // Returns a new memblock that is the concatenation of the two source memblocks. 80 | Memblock* Memblock::cat(Memory& mem, Memblock* other) 81 | { 82 | auto ret = create(mem, this->data.length + other->data.length); 83 | auto split = this->data.length; 84 | ret->data.slicea(0, split, this->data); 85 | ret->data.slicea(split, ret->data.length, other->data); 86 | return ret; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/croc/types/namespace.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/base/writebarrier.hpp" 3 | 4 | #define REMOVEKEYREF(mem, slot)\ 5 | do {\ 6 | if(!IS_KEY_MODIFIED(slot))\ 7 | (mem).decBuffer.add((mem), cast(GCObject*)(slot)->key);\ 8 | } while(false) 9 | 10 | #define REMOVEVALUEREF(mem, slot)\ 11 | do {\ 12 | if(!IS_VAL_MODIFIED(slot) && (slot)->value.isGCObject())\ 13 | (mem).decBuffer.add((mem), (slot)->value.toGCObject());\ 14 | } while(false) 15 | 16 | namespace croc 17 | { 18 | // Create a new namespace object. 19 | Namespace* Namespace::create(Memory& mem, String* name, Namespace* parent) 20 | { 21 | auto ret = createPartial(mem); 22 | finishCreate(ret, name, parent); 23 | return ret; 24 | } 25 | 26 | // Partially construct a namespace. This is used by the serialization system. 27 | Namespace* Namespace::createPartial(Memory& mem) 28 | { 29 | auto ret = ALLOC_OBJ(mem, Namespace); 30 | ret->type = CrocType_Namespace; 31 | return ret; 32 | } 33 | 34 | // Finish constructing a namespace. Also used by serialization. 35 | void Namespace::finishCreate(Namespace* ns, String* name, Namespace* parent) 36 | { 37 | assert(name != nullptr); 38 | ns->name = name; 39 | 40 | if(parent) 41 | { 42 | ns->parent = parent; 43 | auto root = parent; 44 | for( ; root->parent != nullptr; root = root->parent){} 45 | ns->root = root; 46 | } 47 | } 48 | 49 | // Free a namespace object. 50 | void Namespace::free(Memory& mem, Namespace* ns) 51 | { 52 | ns->data.clear(mem); 53 | FREE_OBJ(mem, Namespace, ns); 54 | } 55 | 56 | // Sets a key-value pair. 57 | void Namespace::set(Memory& mem, String* key, Value value) 58 | { 59 | if(this->setIfExists(mem, key, value)) 60 | return; 61 | 62 | CONTAINER_WRITE_BARRIER(mem, this); 63 | auto node = this->data.insertNode(mem, key); 64 | node->value = value; 65 | 66 | if(value.isGCObject()) 67 | SET_BOTH_MODIFIED(node); 68 | else 69 | SET_KEY_MODIFIED(node); 70 | } 71 | 72 | bool Namespace::setIfExists(Memory& mem, String* key, Value value) 73 | { 74 | auto node = this->data.lookupNode(key); 75 | 76 | if(node == nullptr) 77 | return false; 78 | 79 | if(node->value != value) 80 | { 81 | REMOVEVALUEREF(mem, node); 82 | node->value = value; 83 | 84 | if(value.isGCObject()) 85 | { 86 | CONTAINER_WRITE_BARRIER(mem, this); 87 | SET_VAL_MODIFIED(node); 88 | } 89 | else 90 | CLEAR_VAL_MODIFIED(node); 91 | } 92 | 93 | return true; 94 | } 95 | 96 | // Remove a key-value pair from the namespace. 97 | void Namespace::remove(Memory& mem, String* key) 98 | { 99 | if(auto node = this->data.lookupNode(key)) 100 | { 101 | REMOVEKEYREF(mem, node); 102 | REMOVEVALUEREF(mem, node); 103 | this->data.remove(key); 104 | } 105 | } 106 | 107 | // Clears all items from the namespace. 108 | void Namespace::clear(Memory& mem) 109 | { 110 | for(auto node: this->data) 111 | { 112 | REMOVEKEYREF(mem, node); 113 | REMOVEVALUEREF(mem, node); 114 | } 115 | 116 | this->data.clear(mem); 117 | } 118 | } -------------------------------------------------------------------------------- /src/croc/types/string.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "croc/api.h" 5 | #include "croc/types/base.hpp" 6 | #include "croc/util/misc.hpp" 7 | #include "croc/util/str.hpp" 8 | #include "croc/util/utf.hpp" 9 | 10 | #define STRING_EXTRA_SIZE(len) (1 + (sizeof(char) * (len))) 11 | 12 | namespace croc 13 | { 14 | namespace 15 | { 16 | String* createInternal(VM* vm, crocstr data, std::function getCPLen) 17 | { 18 | auto h = data.toHash(); 19 | 20 | if(auto s = vm->stringTab.lookup(data, h)) 21 | return *s; 22 | 23 | bool okay; 24 | auto cpLen = getCPLen(okay); 25 | 26 | if(!okay) 27 | return nullptr; 28 | 29 | auto ret = ALLOC_OBJSZ_ACYC(vm->mem, String, STRING_EXTRA_SIZE(data.length)); 30 | ret->type = CrocType_String; 31 | ret->hash = h; 32 | ret->length = data.length; 33 | ret->cpLength = cpLen; 34 | ret->setData(data); 35 | *vm->stringTab.insert(vm->mem, ret->toDArray()) = ret; 36 | return ret; 37 | } 38 | } 39 | 40 | // Create a new string object. String objects with the same data are reused. Thus, 41 | // if two string objects are identical, they are also equal. 42 | String* String::create(VM* vm, crocstr data) 43 | { 44 | return createInternal(vm, data, [&](bool& okay) 45 | { 46 | uword cpLen; 47 | 48 | if(verifyUtf8(data, cpLen) != UtfError_OK) 49 | croc_eh_throwStd(*vm->curThread, "UnicodeError", "Invalid UTF-8 sequence"); 50 | 51 | okay = true; 52 | return cpLen; 53 | }); 54 | } 55 | 56 | String* String::createUnverified(VM* vm, crocstr data, uword cpLen) 57 | { 58 | return createInternal(vm, data, [&](bool& okay) { okay = true; return cpLen; }); 59 | } 60 | 61 | String* String::tryCreate(VM* vm, crocstr data) 62 | { 63 | return createInternal(vm, data, [&](bool& okay) 64 | { 65 | uword cpLen; 66 | okay = verifyUtf8(data, cpLen) == UtfError_OK; 67 | return cpLen; 68 | }); 69 | } 70 | 71 | // Free a string object. 72 | void String::free(VM* vm, String* s) 73 | { 74 | bool b = vm->stringTab.remove(s->toDArray()); 75 | assert(b); 76 | #ifdef NDEBUG 77 | (void)b; 78 | #endif 79 | FREE_OBJ(vm->mem, String, s); 80 | } 81 | 82 | // Compare two string objects. 83 | crocint String::compare(String* other) 84 | { 85 | return this->toDArray().cmp(other->toDArray()); 86 | } 87 | 88 | // See if the string contains the given substring. 89 | bool String::contains(crocstr sub) 90 | { 91 | return strLocate(this->toDArray(), sub) != this->length; 92 | } 93 | 94 | // The slice indices are in codepoints, not byte indices. 95 | // And these indices better be good. 96 | String* String::slice(VM* vm, uword lo, uword hi) 97 | { 98 | return createUnverified(vm, utf8Slice(this->toDArray(), lo, hi), hi - lo); 99 | } 100 | } -------------------------------------------------------------------------------- /src/croc/types/table.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/base/writebarrier.hpp" 3 | 4 | #define REMOVEKEYREF(mem, slot)\ 5 | do {\ 6 | if(!IS_KEY_MODIFIED(slot) && (slot)->key.isGCObject())\ 7 | (mem).decBuffer.add((mem), (slot)->key.toGCObject());\ 8 | } while(false) 9 | 10 | #define REMOVEVALUEREF(mem, slot)\ 11 | do {\ 12 | if(!IS_VAL_MODIFIED(slot) && (slot)->value.isGCObject())\ 13 | (mem).decBuffer.add((mem), (slot)->value.toGCObject());\ 14 | } while(false) 15 | 16 | namespace croc 17 | { 18 | Table* Table::create(Memory& mem, uword size) 19 | { 20 | auto t = ALLOC_OBJ(mem, Table); 21 | t->type = CrocType_Table; 22 | t->data.prealloc(mem, size); 23 | return t; 24 | } 25 | 26 | // Free a table object. 27 | void Table::free(Memory& mem, Table* t) 28 | { 29 | t->data.clear(mem); 30 | FREE_OBJ(mem, Table, t); 31 | } 32 | 33 | // Duplicate an existing table efficiently. 34 | Table* Table::dup(Memory& mem) 35 | { 36 | auto newTab = ALLOC_OBJ(mem, Table); 37 | newTab->type = CrocType_Table; 38 | newTab->data.prealloc(mem, this->data.capacity()); 39 | 40 | assert(newTab->data.capacity() == this->data.capacity()); 41 | this->data.dupInto(newTab->data); 42 | 43 | // At this point we've basically done the equivalent of inserting every key-value pair from this into t, 44 | // so we have to do run through the new table and do the "insert" write barrier stuff. 45 | 46 | for(auto node: newTab->data) 47 | { 48 | if(node->key.isGCObject() || node->value.isGCObject()) 49 | { 50 | CONTAINER_WRITE_BARRIER(mem, newTab); 51 | 52 | if(node->key.isGCObject()) 53 | SET_KEY_MODIFIED(node); 54 | 55 | if(node->value.isGCObject()) 56 | SET_VAL_MODIFIED(node); 57 | } 58 | } 59 | 60 | return newTab; 61 | } 62 | 63 | void Table::idxa(Memory& mem, Value key, Value val) 64 | { 65 | auto node = this->data.lookupNode(key); 66 | 67 | if(node != nullptr) 68 | { 69 | if(val.type == CrocType_Null) 70 | { 71 | // Remove 72 | REMOVEKEYREF(mem, node); 73 | REMOVEVALUEREF(mem, node); 74 | this->data.remove(key); 75 | } 76 | else if(node->value != val) 77 | { 78 | // Update 79 | REMOVEVALUEREF(mem, node); 80 | node->value = val; 81 | 82 | if(val.isGCObject()) 83 | { 84 | CONTAINER_WRITE_BARRIER(mem, this); 85 | SET_VAL_MODIFIED(node); 86 | } 87 | else 88 | CLEAR_VAL_MODIFIED(node); 89 | } 90 | } 91 | else if(val.type != CrocType_Null) 92 | { 93 | // Insert 94 | node = this->data.insertNode(mem, key); 95 | node->value = val; 96 | 97 | if(key.isGCObject() || val.isGCObject()) 98 | { 99 | CONTAINER_WRITE_BARRIER(mem, this); 100 | 101 | if(key.isGCObject()) 102 | SET_KEY_MODIFIED(node); 103 | 104 | if(val.isGCObject()) 105 | SET_VAL_MODIFIED(node); 106 | } 107 | } 108 | 109 | // otherwise, do nothing (val is null and key doesn't exist) 110 | } 111 | 112 | // remove all key-value pairs from the table. 113 | void Table::clear(Memory& mem) 114 | { 115 | for(auto node: this->data) 116 | { 117 | REMOVEKEYREF(mem, node); 118 | REMOVEVALUEREF(mem, node); 119 | } 120 | 121 | this->data.clear(mem); 122 | } 123 | } -------------------------------------------------------------------------------- /src/croc/types/thread.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/base/writebarrier.hpp" 3 | 4 | namespace croc 5 | { 6 | // Create a new thread object. 7 | Thread* Thread::create(VM* vm) 8 | { 9 | auto t = createPartial(vm); 10 | auto &mem = vm->mem; 11 | 12 | t->ehFrames = DArray::alloc(mem, 10); 13 | t->actRecs = DArray::alloc(mem, 10); 14 | t->stack = DArray::alloc(mem, 20); 15 | t->results = DArray::alloc(mem, 8); 16 | t->stackIndex = cast(AbsStack)1; // So that there is a 'this' at top-level. 17 | t->hooksEnabled = true; 18 | return t; 19 | } 20 | 21 | // Partially create a new thread. Doesn't allocate any memory for its various stacks. Used for serialization. 22 | Thread* Thread::createPartial(VM* vm) 23 | { 24 | auto t = ALLOC_OBJ(vm->mem, Thread); 25 | t->type = CrocType_Thread; 26 | t->vm = vm; 27 | t->next = vm->allThreads; 28 | 29 | if(t->next) 30 | t->next->prev = t; 31 | 32 | vm->allThreads = t; 33 | return t; 34 | } 35 | 36 | // Create a new thread object with a function to be used as the thread body. 37 | Thread* Thread::create(VM* vm, Function* coroFunc) 38 | { 39 | auto t = create(vm); 40 | t->coroFunc = coroFunc; 41 | return t; 42 | } 43 | 44 | // Free a thread object. 45 | void Thread::free(Thread* t) 46 | { 47 | if(t->next) t->next->prev = t->prev; 48 | if(t->prev) t->prev->next = t->next; 49 | 50 | if(t->vm->allThreads == t) 51 | t->vm->allThreads = t->next; 52 | 53 | for(auto uv = t->upvalHead; uv != nullptr; uv = t->upvalHead) 54 | { 55 | t->upvalHead = uv->nextuv; 56 | uv->closedValue = *uv->value; 57 | uv->value = &uv->closedValue; 58 | } 59 | 60 | auto &mem = t->vm->mem; 61 | 62 | t->results.free(mem); 63 | t->stack.free(mem); 64 | t->actRecs.free(mem); 65 | t->ehFrames.free(mem); 66 | FREE_OBJ(mem, Thread, t); 67 | } 68 | 69 | void Thread::reset() 70 | { 71 | assert(this->upvalHead == nullptr); // should be..? 72 | this->currentEH = nullptr; 73 | this->ehIndex = 0; 74 | this->currentAR = nullptr; 75 | this->arIndex = 0; 76 | this->stackIndex = cast(AbsStack)1; 77 | this->stackBase = cast(AbsStack)0; 78 | this->resultIndex = 0; 79 | this->shouldHalt = false; 80 | this->state = CrocThreadState_Initial; 81 | } 82 | 83 | void Thread::setHookFunc(Memory& mem, Function* f) 84 | { 85 | if(this->hookFunc != f) 86 | { 87 | WRITE_BARRIER(mem, this); 88 | this->hookFunc = f; 89 | } 90 | } 91 | 92 | void Thread::setCoroFunc(Memory& mem, Function* f) 93 | { 94 | if(this->coroFunc != f) 95 | { 96 | WRITE_BARRIER(mem, this); 97 | this->coroFunc = f; 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/croc/types/weakref.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "croc/base/writebarrier.hpp" 3 | 4 | namespace croc 5 | { 6 | // Create a new weakref object. Weak reference objects that refer to the same object are reused. Thus, 7 | // if two weak references are identical, they refer to the same object. 8 | Weakref* Weakref::create(VM* vm, GCObject* obj) 9 | { 10 | if(auto r = vm->weakrefTab.lookup(obj)) 11 | return *r; 12 | 13 | auto ret = ALLOC_OBJ_ACYC(vm->mem, Weakref); 14 | ret->type = CrocType_Weakref; 15 | ret->obj = obj; 16 | *vm->weakrefTab.insert(vm->mem, obj) = ret; 17 | return ret; 18 | } 19 | 20 | Value Weakref::makeref(VM* vm, Value val) 21 | { 22 | if(val.isValType() || val.type == CrocType_Upval) 23 | return val; 24 | else 25 | return Value::from(create(vm, val.mGCObj)); 26 | } 27 | 28 | // Free a weak reference object. 29 | void Weakref::free(VM* vm, Weakref* r) 30 | { 31 | if(r->obj != nullptr) 32 | { 33 | auto b = vm->weakrefTab.remove(r->obj); 34 | assert(b); 35 | #ifdef NDEBUG 36 | (void)b; 37 | #endif 38 | } 39 | 40 | FREE_OBJ(vm->mem, Weakref, r); 41 | } 42 | } -------------------------------------------------------------------------------- /src/croc/util/misc.cpp: -------------------------------------------------------------------------------- 1 | #include "croc/util/misc.hpp" 2 | 3 | namespace croc 4 | { 5 | // Returns closest power of 2 that is >= n. Taken from the Stanford Bit Twiddling Hacks page. 6 | size_t largerPow2(size_t n) 7 | { 8 | if(n == 0) 9 | return 0; 10 | 11 | n--; 12 | n |= n >> 1; 13 | n |= n >> 2; 14 | n |= n >> 4; 15 | n |= n >> 8; 16 | n |= n >> 16; 17 | n |= n >> ((sizeof(size_t) > 4) * 32); 18 | n++; 19 | return n; 20 | } 21 | } -------------------------------------------------------------------------------- /src/croc/util/misc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_UTIL_MISC_HPP 2 | #define CROC_UTIL_MISC_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "croc/base/darray.hpp" 8 | 9 | namespace croc 10 | { 11 | size_t largerPow2(size_t n); 12 | 13 | template 14 | inline int Compare3(T a, T b) 15 | { 16 | return a < b ? -1 : a > b ? 1 : 0; 17 | } 18 | 19 | template 20 | inline T abs(T t) { if(t < 0) return -t; else return t; } 21 | 22 | template 23 | inline T min(T a, T b) { if(a < b) return a; else return b; } 24 | 25 | template 26 | inline T max(T a, T b) { if(a > b) return a; else return b; } 27 | 28 | inline DArray atoda(const char* str) 29 | { 30 | return DArray::n(cast(const unsigned char*)str, strlen(str)); 31 | } 32 | 33 | #define ATODA(lit) (DArray{cast(const unsigned char*)(lit), (sizeof(lit) / sizeof(char)) - 1}) 34 | } 35 | 36 | #endif -------------------------------------------------------------------------------- /src/croc/util/rng.cpp: -------------------------------------------------------------------------------- 1 | 2 | #ifdef _WIN32 3 | #include "windows.h" 4 | #else 5 | #include 6 | #endif 7 | 8 | #include "croc/util/rng.hpp" 9 | 10 | #define M_RAN_INVM32 2.32830643653869628906e-010 11 | #define M_RAN_INVM52 2.22044604925031308085e-016 12 | 13 | namespace croc 14 | { 15 | void RNG::seed() 16 | { 17 | uint64_t s; 18 | 19 | #ifdef _WIN32 20 | LARGE_INTEGER t; 21 | QueryPerformanceCounter(&t); 22 | s = t.QuadPart; 23 | #else 24 | timeval t; 25 | gettimeofday(&t, nullptr); 26 | s = t.tv_usec; 27 | #endif 28 | 29 | seed((uint32_t)s); 30 | } 31 | 32 | void RNG::seed(uint32_t seed) 33 | { 34 | initialSeed = seed; 35 | x = seed | 1; 36 | y = seed | 2; 37 | z = seed | 4; 38 | w = seed | 8; 39 | carry = 0; 40 | } 41 | 42 | uint32_t RNG::getSeed() 43 | { 44 | return initialSeed; 45 | } 46 | 47 | uint32_t RNG::next() 48 | { 49 | x = x * 69069 + 1; 50 | y ^= y << 13; 51 | y ^= y >> 17; 52 | y ^= y << 5; 53 | k = (z >> 2) + (w >> 3) + (carry >> 2); 54 | m = w + w + z + carry; 55 | z = w; 56 | w = m; 57 | carry = k >> 30; 58 | return x + y + w; 59 | } 60 | 61 | uint64_t RNG::next64() 62 | { 63 | return next() | (((uint64_t)next()) << 32); 64 | } 65 | 66 | double RNG::nextf32() 67 | { 68 | return ((int32_t)next()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM32 / 2); 69 | } 70 | 71 | double RNG::nextf52() 72 | { 73 | return 74 | ((int32_t)next()) * M_RAN_INVM32 + (0.5 + M_RAN_INVM52 / 2) + 75 | (((int32_t)next()) & 0x000FFFFF) * M_RAN_INVM52; 76 | } 77 | } -------------------------------------------------------------------------------- /src/croc/util/rng.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_UTIL_RNG_HPP 2 | #define CROC_UTIL_RNG_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace croc 8 | { 9 | struct RNG 10 | { 11 | private: 12 | uint32_t initialSeed; 13 | uint32_t k; 14 | uint32_t m; 15 | uint32_t x; 16 | uint32_t y; 17 | uint32_t z; 18 | uint32_t w; 19 | uint32_t carry; 20 | 21 | public: 22 | RNG(): 23 | initialSeed(0), 24 | k(0), 25 | m(0), 26 | x(1), 27 | y(2), 28 | z(4), 29 | w(8), 30 | carry(0) 31 | {} 32 | 33 | void seed(); 34 | void seed(uint32_t seed); 35 | uint32_t getSeed(); 36 | uint32_t next(); 37 | uint64_t next64(); 38 | double nextf32(); 39 | double nextf52(); 40 | }; 41 | } 42 | 43 | #endif -------------------------------------------------------------------------------- /src/croc/util/str.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_UTIL_STR_HPP 2 | #define CROC_UTIL_STR_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "croc/base/darray.hpp" 8 | #include "croc/util/utf.hpp" 9 | 10 | namespace croc 11 | { 12 | // ================================================================================================================= 13 | // UTF-8 14 | 15 | size_t findCharFast(custring str, uchar ch); 16 | // NOTE: the start indices on all these is in BYTES, not codepoints! 17 | size_t strLocate(custring source, custring match, size_t start = 0); 18 | size_t strLocateChar(custring source, uchar match, size_t start = 0); 19 | size_t strLocatePattern(custring source, custring match, size_t start = 0); 20 | size_t strRLocate(custring source, custring match); 21 | size_t strRLocate(custring source, custring match, size_t start); 22 | size_t strRLocateChar(custring source, uchar match); 23 | size_t strRLocateChar(custring source, uchar match, size_t start); 24 | size_t strRLocatePattern(custring source, custring match); 25 | size_t strRLocatePattern(custring source, custring match, size_t start); 26 | bool strEqFast(const uchar* s1, const uchar* s2, size_t length); 27 | size_t strMismatchFast(const uchar* s1, const uchar* s2, size_t length); 28 | 29 | custring strTrimWS(custring str); 30 | custring strTrimlWS(custring str); 31 | custring strTrimrWS(custring str); 32 | 33 | void delimiters(custring str, custring set, std::function dg); 34 | void delimitersBreak(custring str, custring set, std::function dg); 35 | 36 | void patterns(custring str, custring pat, std::function dg); 37 | void patternsBreak(custring str, custring pat, std::function dg); 38 | void patternsRep(custring str, custring pat, custring rep, std::function dg); 39 | void patternsRepBreak(custring str, custring pat, custring rep, std::function dg); 40 | 41 | void lines(custring str, std::function dg); 42 | void linesBreak(custring str, std::function dg); 43 | 44 | // ================================================================================================================= 45 | // UTF-32 46 | 47 | size_t findCharFast(cdstring str, dchar ch); 48 | size_t strLocate(cdstring source, cdstring match, size_t start); 49 | size_t strLocateChar(cdstring source, dchar match, size_t start); 50 | size_t strLocatePattern(cdstring source, cdstring match, size_t start); 51 | size_t strRLocate(cdstring source, cdstring match, size_t start); 52 | size_t strRLocateChar(cdstring source, dchar match, size_t start); 53 | size_t strRLocatePattern(cdstring source, cdstring match, size_t start); 54 | bool strEqFast(const dchar* s1, const dchar* s2, size_t length); 55 | size_t strMismatchFast(const dchar* s1, const dchar* s2, size_t length); 56 | cdstring strTrimWS(cdstring str); 57 | cdstring strTrimlWS(cdstring str); 58 | cdstring strTrimrWS(cdstring str); 59 | 60 | void delimiters(cdstring str, cdstring set, std::function dg); 61 | void delimitersBreak(cdstring str, cdstring set, std::function dg); 62 | 63 | void patterns(cdstring str, cdstring pat, std::function dg); 64 | void patternsBreak(cdstring str, cdstring pat, std::function dg); 65 | void patternsRep(cdstring str, cdstring pat, cdstring rep, std::function dg); 66 | void patternsRepBreak(cdstring str, cdstring pat, cdstring rep, std::function dg); 67 | 68 | void lines(cdstring str, std::function dg); 69 | void linesBreak(cdstring str, std::function dg); 70 | 71 | // ================================================================================================================= 72 | // conversion 73 | 74 | size_t intToString(ustring buf, uint64_t x, size_t radix, bool isUppercase); 75 | } 76 | 77 | #endif -------------------------------------------------------------------------------- /src/croc/util/utf.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CROC_UTIL_UTF_HPP 2 | #define CROC_UTIL_UTF_HPP 3 | 4 | namespace croc 5 | { 6 | typedef uint8_t uchar; 7 | typedef uint16_t wchar; 8 | typedef uint32_t dchar; 9 | 10 | typedef DArray ustring; 11 | typedef DArray wstring; 12 | typedef DArray dstring; 13 | 14 | typedef DArray custring; 15 | typedef DArray cwstring; 16 | typedef DArray cdstring; 17 | 18 | /** 19 | Enumeration of possible return values from certain UTF decoding functions. 20 | */ 21 | typedef enum UtfError 22 | { 23 | UtfError_OK = 0, /// Success. 24 | UtfError_BadEncoding = 1, /// The data is incorrectly encoded. It may or may not be possible to progress past this. 25 | UtfError_BadChar = 2, /// The data was encoded properly, but encodes an invalid character. 26 | UtfError_Truncated = 3, /// The end of the data comes before the character can be completely decoded. 27 | } UtfError; 28 | 29 | bool isValidChar(dchar c); 30 | size_t charUtf8Length(dchar c); 31 | size_t utf8SequenceLength(uchar firstByte); 32 | 33 | UtfError decodeUtf8Char(const uchar*& s, const uchar* end, dchar& out); 34 | template 35 | UtfError decodeUtf16Char(const wchar*& s, const wchar* end, dchar& out); 36 | #define decodeUtf16CharBS decodeUtf16Char 37 | template 38 | UtfError decodeUtf32Char(const dchar*& s, const dchar* end, dchar& out); 39 | #define decodeUtf32CharBS decodeUtf32Char 40 | void skipBadUtf8Char(const uchar*& s, const uchar* end); 41 | template 42 | void skipBadUtf16Char(const wchar*& s, const wchar* end); 43 | #define skipBadUtf16CharBS skipBadUtf16Char 44 | template 45 | void skipBadUtf32Char(const dchar*& s, const dchar* end); 46 | #define skipBadUtf32CharBS skipBadUtf32Char 47 | UtfError verifyUtf8(custring str, size_t& cpLen); 48 | UtfError Utf16ToUtf8(cwstring str, ustring buf, cwstring& remaining, ustring& output); 49 | UtfError Utf32ToUtf8(cdstring str, ustring buf, cdstring& remaining, ustring& output); 50 | UtfError Utf16ToUtf8BS(cwstring str, ustring buf, cwstring& remaining, ustring& output); 51 | UtfError Utf32ToUtf8BS(cdstring str, ustring buf, cdstring& remaining, ustring& output); 52 | UtfError encodeUtf8Char(ustring buf, dchar c, ustring& ret); 53 | 54 | // ================================================================================================================= 55 | // The functions from here on all assume the input string is well-formed -- which is the case with Croc's strings 56 | 57 | dchar fastDecodeUtf8Char(const uchar*& s); 58 | dchar fastReverseUtf8Char(const uchar*& s); 59 | void fastAlignUtf8(const uchar*& s); 60 | template 61 | wstring Utf8ToUtf16(custring str, wstring buf, custring& remaining); 62 | #define Utf8ToUtf16BS Utf8ToUtf16 63 | template 64 | dstring Utf8ToUtf32(custring str, dstring buf, custring& remaining); 65 | #define Utf8ToUtf32BS Utf8ToUtf32 66 | custring utf8Slice(custring str, size_t lo, size_t hi); 67 | dchar utf8CharAt(custring str, size_t idx); 68 | size_t utf8CPIdxToByte(custring str, size_t fake); 69 | size_t utf8ByteIdxToCP(custring str, size_t fake); 70 | size_t fastUtf8CPLength(custring str); 71 | size_t fastUtf16GetUtf8Size(cwstring str); 72 | size_t fastUtf32GetUtf8Size(cdstring str); 73 | size_t fastUtf8GetUtf16Size(custring str); 74 | 75 | struct DcharIterator 76 | { 77 | private: 78 | const uchar* mPtr; 79 | 80 | public: 81 | DcharIterator(const char* str) : mPtr(cast(const uchar*)str) {} 82 | DcharIterator(const uchar* str) : mPtr(str) {} 83 | DcharIterator(const DcharIterator& other) : mPtr(other.mPtr) {} 84 | DcharIterator& operator++() { mPtr += utf8SequenceLength(*mPtr); return *this; } 85 | DcharIterator operator++(int) { DcharIterator tmp(*this); operator++(); return tmp; } 86 | bool operator==(const DcharIterator& rhs) { return mPtr == rhs.mPtr; } 87 | bool operator!=(const DcharIterator& rhs) { return !(*this == rhs); } 88 | dchar operator*() { const uchar* tmp = mPtr; return fastDecodeUtf8Char(tmp); } 89 | }; 90 | 91 | struct dcharsOf 92 | { 93 | private: 94 | custring mStr; 95 | 96 | public: 97 | dcharsOf(custring str) : mStr(str) {} 98 | 99 | DcharIterator begin() 100 | { 101 | return DcharIterator(mStr.ptr); 102 | } 103 | 104 | DcharIterator end() 105 | { 106 | return DcharIterator(mStr.ptr + mStr.length); 107 | } 108 | }; 109 | } 110 | 111 | #endif -------------------------------------------------------------------------------- /tests/harness.croc: -------------------------------------------------------------------------------- 1 | module tests.harness 2 | 3 | import compiler: loadString 4 | 5 | namespace Test {} 6 | 7 | function xpassfn(func: function, result = null) 8 | { 9 | try 10 | ret = func() 11 | catch(e: Exception) 12 | throw Exception("Test expected to pass but failed", e) 13 | 14 | if(typeof(ret) != typeof(result) || ret != result) 15 | throw Exception("Test expected to give results '{}' but gave '{}' instead".format(result, ret)) 16 | } 17 | 18 | function xfailfn(func: function, result: class) 19 | { 20 | try 21 | func() 22 | catch(e: Exception) 23 | { 24 | if(e as result) 25 | return 26 | 27 | throw Exception("Test expected to fail and did, but threw exception type '{}' instead of '{}'".format(e.super, result), e) 28 | } 29 | 30 | throw Exception("Test expected to fail but passed") 31 | } 32 | 33 | 34 | function xpass(code: string, result = null) 35 | { 36 | local func, ret 37 | hash.clear(Test) 38 | 39 | try 40 | func = loadString(code, Test) 41 | catch(e: CompileException) 42 | throw Exception("Test '{}' failed to compile".format(code), e) 43 | 44 | try 45 | ret = func() 46 | catch(e: Exception) 47 | throw Exception("Test '{}' expected to pass but failed".format(code), e) 48 | 49 | if(typeof(ret) != typeof(result) || ret != result) 50 | throw Exception("Test '{}' expected to give results '{}' but gave '{}' instead".format(code, result, ret)) 51 | } 52 | 53 | function xfail(code: string, args: array, result: class) 54 | { 55 | local func 56 | hash.clear(Test) 57 | 58 | try 59 | func = loadString(code, Test) 60 | catch(e: CompileException) 61 | throw Exception("Test '{}' failed to compile".format(code), e) 62 | 63 | try 64 | func(args.expand()) 65 | catch(e: Exception) 66 | { 67 | if(e as result) 68 | return 69 | 70 | throw Exception("Test '{}' expected to fail and did, but threw exception type '{}' instead of '{}'".format(code, e.super, result), e) 71 | } 72 | 73 | throw Exception("Test '{}' expected to fail but passed".format(code)) 74 | } 75 | 76 | function xcomppass(code: string) 77 | { 78 | try 79 | loadString(code) 80 | catch(e: CompileException) 81 | throw Exception("Test '{}' expected to pass compilation but failed".format(code), e) 82 | catch(e: Exception) 83 | throw Exception("Test '{}' expected to pass compilation but caused an error".format(e)) 84 | } 85 | 86 | function xcompfail(code: string, somethingLike: string) 87 | { 88 | try 89 | loadString(code) 90 | catch(e: CompileException) 91 | { 92 | if(somethingLike in e.msg) 93 | return 94 | else 95 | throw Exception("Test '{}' failed compilation, but instead of an error like '{}', it gave '{}'".format(code, somethingLike, e.msg), e) 96 | } 97 | catch(e: Exception) 98 | throw Exception("Test '{}' expected to fail compilation but caused another error".format(e)) 99 | 100 | throw Exception("Test '{}' expected to fail compilation but passed".format(code)) 101 | } 102 | -------------------------------------------------------------------------------- /tools/wikiconv.croc: -------------------------------------------------------------------------------- 1 | module tools.wikiconv 2 | 3 | import pcre: Regex 4 | 5 | local rules = 6 | [ 7 | [@"svn.newt.jfbillingsley.com/trunk/docs", "jarrettbillingsley.github.com/Croc/docs"], 8 | [@"http://svn.newt.jfbillingsley.com/downloads", "https://raw.github.com/JarrettBillingsley/Croc/master/downloads"], 9 | 10 | [@`MDVM`, "Croc VM"], 11 | [@`StdLib2`, "StdLib"], 12 | [@`LanguageSpec2`, "LanguageSpec"], 13 | [@`\b(Newt|MiniD)\b`, "Croc"], 14 | [@`\b(newt|minid)\b`, "croc"], 15 | [@`\. `, ". "], 16 | ] 17 | 18 | foreach(rule; rules) 19 | rule[0] = Regex(rule[0], "m") 20 | 21 | function main() 22 | { 23 | local text = file.readFile("wiki.txt") 24 | 25 | foreach(rule; rules) 26 | text = rule[0].replace(text, rule[1]) 27 | 28 | file.writeFile("wikiOut.txt", text) 29 | } --------------------------------------------------------------------------------