mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-30 23:23:07 +00:00 
			
		
		
		
	Add json native instead of hello. Remove metabuild
code.
This commit is contained in:
		
							
								
								
									
										8
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								Makefile
									
									
									
									
									
								
							| @@ -130,11 +130,11 @@ valtest: $(DST_TARGET) | ||||
| ################### | ||||
|  | ||||
| natives: $(DST_TARGET) | ||||
| 	$(MAKE) -C natives/hello | ||||
| 	$(MAKE) -C natives/json | ||||
| 	$(MAKE) -j 8 -C natives/sqlite3 | ||||
|  | ||||
| clean-natives: | ||||
| 	$(MAKE) -C natives/hello clean | ||||
| 	$(MAKE) -C natives/json clean | ||||
| 	$(MAKE) -C natives/sqlite3 clean | ||||
|  | ||||
| ################# | ||||
| @@ -154,6 +154,10 @@ install: $(DST_TARGET) | ||||
| 	cp $(DST_LIBRARY) $(LIBDIR)/$(DST_LIBRARY) | ||||
| 	$(LDCONFIG) | ||||
|  | ||||
| install-libs: natives | ||||
| 	cp lib/* $(DST_PATH) | ||||
| 	cp natives/*/*.so $(DST_PATH) | ||||
|  | ||||
| uninstall: | ||||
| 	-rm $(BINDIR)/$(DST_TARGET) | ||||
| 	-rm $(LIBDIR)/$(DST_LIBRARY) | ||||
|   | ||||
| @@ -105,27 +105,3 @@ | ||||
|     (when x | ||||
|       (def thehead (get HEAD x)) | ||||
|       (if thehead (tuple thehead (take-while pred (get TAIL x))))))) | ||||
| 
 | ||||
| # Iterators are a concept that looks a lot like lazy seq | ||||
| # The following functions turn iterators to lazy seq and vice versa | ||||
| 
 | ||||
| (defn- iter-self | ||||
|   [next more] | ||||
|   (delay  | ||||
|     (if (more) (tuple (next) (iter-self next more))))) | ||||
| 
 | ||||
| (defn iter2lazy | ||||
|   "Create a lazy sequence from an iterator" | ||||
|   [iter] | ||||
|   (def {:more more :next next} iter) | ||||
|   (iter-self next more)) | ||||
| 
 | ||||
| (defn lazy2iter | ||||
|   "turn a lazy-seq to an iterator" | ||||
|   [lazy-seq] | ||||
|   (var node lazy-seq) | ||||
|   {:more (fn [] (node)) | ||||
|    :next (fn [] | ||||
|            (when-let [n (node)] | ||||
|                      (:= node (get n 1)) | ||||
|                      (get n 0)))}) | ||||
| @@ -1,103 +0,0 @@ | ||||
| # A script to help generate build files on different platforms | ||||
|  | ||||
| # The Makefile generator | ||||
|  | ||||
| (def- make-cflags @["-std=c99" "-Wall" "-Wextra" "-O2" "-shared" "-fpic"]) | ||||
| (def- make-ldflags @[]) | ||||
|  | ||||
| (if (= :macos (os.which)) | ||||
|   (array.push make-cflags "undefined -dynamic_lookup")) | ||||
|  | ||||
| (defn- $ | ||||
|   "Do and get the value of a subshell command" | ||||
|   [command] | ||||
|   (def f (file.popen command)) | ||||
|   (def ret (file.read f :all)) | ||||
|   (file.close f) | ||||
|   ret) | ||||
|  | ||||
| (defn- emit-rule | ||||
|   "Emit a rule in a makefile" | ||||
|   @[out target deps recipe] | ||||
|   (default recipe "@echo '-----'") | ||||
|   (file.write | ||||
|     out  | ||||
|     target  | ||||
|     ": "  | ||||
|     (if (indexed? deps) (string.join deps " ") deps)  | ||||
|     "\n\t" | ||||
|     (if (indexed? recipe) (string.join recipe "\n\t") recipe) | ||||
|     "\n\n") | ||||
|   out) | ||||
|  | ||||
| (defn- generate-make | ||||
|   "Generate a makefile" | ||||
|   [out-path sources target] | ||||
|   (def out (file.open out-path :w)) | ||||
|   (def csources (filter (fn [x] (= ".c" (string.slice x -2))) sources)) | ||||
|   (def hsources (filter (fn [x] (= ".h" (string.slice x -2))) sources)) | ||||
|   (file.write | ||||
|     out | ||||
|     "# Autogenerated Makefile, do not edit\n" | ||||
|     "# Generated at " ($ `date`) | ||||
|     "\nCFLAGS:=" (string.join make-cflags " ") | ||||
|     "\nLDFLAGS:=" (string.join make-ldflags " ") | ||||
|     "\nSOURCES:=" (string.join csources " ") | ||||
|     "\nHEADERS:=" (string.join hsources " ") | ||||
|     "\nOBJECTS:=$(patsubst %.c,%.o,${SOURCES})" | ||||
|     "\nTARGET:=" target ".so" | ||||
|     "\n\n") | ||||
|   (emit-rule out "all" "${TARGET}") | ||||
|   (emit-rule out "%.o" @["%.c" "${HEADERS}"] "${CC} ${CFLAGS} -o $@ $< ${LDFLAGS}") | ||||
|   (emit-rule out "${TARGET}" "${OBJECTS}" "${CC} ${CFLAGS} -o $@ $^ ${LDFLAGS}") | ||||
|   (emit-rule out "clean" "" "rm ${OBJECTS}") | ||||
|   (emit-rule out "clean" "" @["rm ${OBJECTS}" "rm @{TARGET}"]) | ||||
|   # Phony targets | ||||
|   (emit-rule out ".PHONY" @["all" "clean"]) | ||||
|   nil) | ||||
|  | ||||
| # The batch script generator (windows) | ||||
|  | ||||
| (defn- batch-crule | ||||
|   "Create a snippet to compile a single C file to an .obj file" | ||||
|   [file-path] | ||||
|   (string  | ||||
|     `cl /nologo /I..\..\src\include /c /O2 /W3 ` | ||||
|     file-path | ||||
|     "\n@if errorlevel 1 goto :BUILDFAIL")) | ||||
|  | ||||
| (defn- generate-batch | ||||
|   "Generate a batch file for windows" | ||||
|   [out-path sources target] | ||||
|   (def out (file.open out-path :w)) | ||||
|   (def csources (filter (fn [x] (= ".c" (string.slice x -2))) sources)) | ||||
|   (file.write | ||||
|     out | ||||
|     "@rem Generated batch script, run in 'Visual Studio Developer Prompt'\n" | ||||
|     "\n@rem \n\n" | ||||
|     "@echo off\n\n" | ||||
|     (string.join (map batch-crule csources) "\n\n") | ||||
|     "\n\n" | ||||
|     `link /nologo /dll ..\..\dst.lib /out:` target `.dll *.obj` | ||||
|     "\nif errorlevel 1 goto :BUILDFAIL" | ||||
|     "\n\n@echo .\n@echo ======\n@echo Build Succeeded.\n@echo =====\n" | ||||
|     "exit /b 0\n\n" | ||||
|     ":BUILDFAIL\n" | ||||
|     "@echo .\n" | ||||
|     "@echo =====\n" | ||||
|     "@echo BUILD FAILED. See Output For Details.\n" | ||||
|     "@echo =====\n" | ||||
|     "@echo .\n" | ||||
|     "exit /b 1\n") | ||||
|   (file.flush out) | ||||
|   (file.close out) | ||||
|   nil) | ||||
|  | ||||
| (defn generate | ||||
|   "Generate the build files for a given library." | ||||
|   [out-path sources target] | ||||
|   ((if (= :windows (os.which)) generate-batch generate-make) | ||||
|     out-path | ||||
|     sources | ||||
|     target)) | ||||
|  | ||||
							
								
								
									
										76
									
								
								natives/hello/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								natives/hello/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,76 +0,0 @@ | ||||
|  | ||||
| # Created by https://www.gitignore.io/api/c | ||||
|  | ||||
| ### C ### | ||||
| # Prerequisites | ||||
| *.d | ||||
|  | ||||
| # Object files | ||||
| *.o | ||||
| *.ko | ||||
| *.obj | ||||
| *.elf | ||||
|  | ||||
| # Linker output | ||||
| *.ilk | ||||
| *.map | ||||
| *.exp | ||||
|  | ||||
| # Precompiled Headers | ||||
| *.gch | ||||
| *.pch | ||||
|  | ||||
| # Libraries | ||||
| *.lib | ||||
| *.a | ||||
| *.la | ||||
| *.lo | ||||
|  | ||||
| # Shared objects (inc. Windows DLLs) | ||||
| *.dll | ||||
| *.so | ||||
| *.so.* | ||||
| *.dylib | ||||
|  | ||||
| # Executables | ||||
| *.exe | ||||
| *.out | ||||
| *.app | ||||
| *.i*86 | ||||
| *.x86_64 | ||||
| *.hex | ||||
|  | ||||
| # Debug files | ||||
| *.dSYM/ | ||||
| *.su | ||||
| *.idb | ||||
| *.pdb | ||||
|  | ||||
| # Kernel Module Compile Results | ||||
| *.mod* | ||||
| *.cmd | ||||
| .tmp_versions/ | ||||
| modules.order | ||||
| Module.symvers | ||||
| Mkfile.old | ||||
| dkms.conf | ||||
|  | ||||
|  | ||||
| # End of https://www.gitignore.io/api/c | ||||
|  | ||||
| # Created by https://www.gitignore.io/api/cmake | ||||
|  | ||||
| ### CMake ### | ||||
| CMakeCache.txt | ||||
| CMakeFiles | ||||
| CMakeScripts | ||||
| Testing | ||||
| Makefile | ||||
| cmake_install.cmake | ||||
| install_manifest.txt | ||||
| compile_commands.json | ||||
| CTestTestfile.cmake | ||||
| build | ||||
|  | ||||
|  | ||||
| # End of https://www.gitignore.io/api/cmake | ||||
| @@ -1,24 +0,0 @@ | ||||
| # Autogenerated Makefile, do not edit | ||||
| # Generated at Fri Aug  3 22:55:50 EDT 2018 | ||||
|  | ||||
| CFLAGS:=-std=c99 -Wall -Wextra -O2 -shared -fpic | ||||
| LDFLAGS:= | ||||
| SOURCES:=main.c | ||||
| HEADERS:= | ||||
| OBJECTS:=$(patsubst %.c,%.o,${SOURCES}) | ||||
| TARGET:=hello.so | ||||
|  | ||||
| all: ${TARGET} | ||||
|  | ||||
| %.o: %.c ${HEADERS} | ||||
| 	${CC} ${CFLAGS} -o $@ -c $< ${LDFLAGS} | ||||
|  | ||||
| ${TARGET}: ${OBJECTS} | ||||
| 	${CC} ${CFLAGS} -o $@ $^ ${LDFLAGS} | ||||
|  | ||||
| clean:  | ||||
| 	rm ${OBJECTS} | ||||
| 	rm ${TARGET} | ||||
|  | ||||
| .PHONY: all clean | ||||
|  | ||||
| @@ -1,42 +0,0 @@ | ||||
| /* | ||||
| * Copyright (c) 2017 Calvin Rose | ||||
| * | ||||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| * of this software and associated documentation files (the "Software"), to | ||||
| * deal in the Software without restriction, including without limitation the | ||||
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| * sell copies of the Software, and to permit persons to whom the Software is | ||||
| * furnished to do so, subject to the following conditions: | ||||
| * | ||||
| * The above copyright notice and this permission notice shall be included in | ||||
| * all copies or substantial portions of the Software. | ||||
| * | ||||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| * IN THE SOFTWARE. | ||||
| */ | ||||
|  | ||||
| #include <dst/dst.h> | ||||
|  | ||||
| static int hello(DstArgs args) { | ||||
|     (void) args; | ||||
|     printf("Hello, world!\n"); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /*****************************************************************************/ | ||||
|  | ||||
| static const DstReg cfuns[] = { | ||||
|     {"hello", hello}, | ||||
|     {NULL, NULL} | ||||
| }; | ||||
|  | ||||
| DST_MODULE_ENTRY (DstArgs args) { | ||||
|     DstTable *env = dst_env_arg(args); | ||||
|     dst_env_cfuns(env, cfuns); | ||||
|     return 0; | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| #!/usr/bin/env dst | ||||
|  | ||||
| (import metabuild :as mb) | ||||
|  | ||||
| (mb.generate "hello" @["main.c"]) | ||||
							
								
								
									
										44
									
								
								natives/json/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								natives/json/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| # Copyright (c) 2018 Calvin Rose | ||||
| #  | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| #  | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| #  | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| CFLAGS:=-std=c99 -Wall -Wextra -O2 -shared -fpic | ||||
| CFLAGS=-std=c99 -Wall -Wextra -I../../src/include -O2 -shared -fpic | ||||
| OBJECTS:=json.o | ||||
| TARGET:=json.so | ||||
|  | ||||
| # MacOS specifics | ||||
| UNAME:=$(shell uname -s) | ||||
| ifeq ($(UNAME), Darwin) | ||||
| 	CFLAGS:=$(CFLAGS) -undefined dynamic_lookup | ||||
| endif | ||||
|  | ||||
| all: $(TARGET) | ||||
|  | ||||
| %.o: %.c $(HEADERS) | ||||
| 	$(CC) $(CFLAGS) -c $< | ||||
|  | ||||
| $(TARGET): $(OBJECTS) | ||||
| 	$(CC) $(CFLAGS) -o $@ $^ | ||||
|  | ||||
| clean:  | ||||
| 	rm $(OBJECTS) | ||||
| 	rm $(TARGET) | ||||
|  | ||||
| .PHONY: all clean | ||||
| @@ -4,10 +4,10 @@ | ||||
| 
 | ||||
| @echo off | ||||
| 
 | ||||
| cl /nologo /I..\..\src\include /c /O2 /W3 hello.c | ||||
| cl /nologo /I..\..\src\include /c /O2 /W3 json.c | ||||
| @if errorlevel 1 goto :BUILDFAIL | ||||
| 
 | ||||
| link /nologo /dll ..\..\dst.lib /out:hello.dll *.obj | ||||
| link /nologo /dll ..\..\dst.lib /out:json.dll *.obj | ||||
| if errorlevel 1 goto :BUILDFAIL | ||||
| 
 | ||||
| @echo . | ||||
							
								
								
									
										596
									
								
								natives/json/json.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										596
									
								
								natives/json/json.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,596 @@ | ||||
| /* | ||||
| * Copyright (c) 2018 Calvin Rose | ||||
| * | ||||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| * of this software and associated documentation files (the "Software"), to | ||||
| * deal in the Software without restriction, including without limitation the | ||||
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| * sell copies of the Software, and to permit persons to whom the Software is | ||||
| * furnished to do so, subject to the following conditions: | ||||
| * | ||||
| * The above copyright notice and this permission notice shall be included in | ||||
| * all copies or substantial portions of the Software. | ||||
| * | ||||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| * IN THE SOFTWARE. | ||||
| */ | ||||
|  | ||||
| #include <dst/dst.h> | ||||
| #include <stdlib.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| /*****************/ | ||||
| /* JSON Decoding */ | ||||
| /*****************/ | ||||
|  | ||||
| /* Check if a character is whitespace */ | ||||
| static int white(uint8_t c) { | ||||
|     return c == '\t' || c == '\n' || c == ' ' || c == '\r'; | ||||
| } | ||||
|  | ||||
| /* Skip whitespace */ | ||||
| static void skipwhite(const char **p) { | ||||
|     const char *cp = *p; | ||||
|     for (;;) { | ||||
|         if (white(*cp)) | ||||
|             cp++; | ||||
|         else | ||||
|             break; | ||||
|     } | ||||
|     *p = cp; | ||||
| } | ||||
|  | ||||
| /* Get a hex digit value */ | ||||
| static int hexdig(char dig) { | ||||
|     if (dig >= '0' && dig <= '9') | ||||
|         return dig - '0'; | ||||
|     if (dig >= 'a' && dig <= 'f') | ||||
|         return 10 + dig - 'a'; | ||||
|     if (dig >= 'A' && dig <= 'F') | ||||
|         return 10 + dig - 'A'; | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* Read the hex value for a unicode escape */ | ||||
| static char *decode_utf16_escape(const char *p, uint32_t *outpoint) { | ||||
|     if (!p[0] || !p[1] || !p[2] || !p[3]) | ||||
|         return "unexpected end of source"; | ||||
|     int d1 = hexdig(p[0]); | ||||
|     int d2 = hexdig(p[1]); | ||||
|     int d3 = hexdig(p[2]); | ||||
|     int d4 = hexdig(p[3]); | ||||
|     if (d1 < 0 || d2 < 0 || d3 < 0 || d4 < 0) | ||||
|         return "invalid hex digit"; | ||||
|     *outpoint = d4 | (d3 << 4) | (d2 << 8) | (d1 << 12); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* Parse a string */ | ||||
| const char *decode_string(const char **p, Dst *out) { | ||||
|     DstBuffer *buffer = dst_buffer(0); | ||||
|     const char *cp = *p; | ||||
|     while (*cp != '"') { | ||||
|         uint8_t b = (uint8_t) *cp; | ||||
|         if (b < 32) return "invalid character in string"; | ||||
|         if (b == '\\') { | ||||
|             cp++; | ||||
|             switch(*cp) { | ||||
|                 default: | ||||
|                     return "unknown string escape"; | ||||
|                 case 'b': | ||||
|                     b = '\b'; | ||||
|                     break; | ||||
|                 case 'f': | ||||
|                     b = '\f'; | ||||
|                     break; | ||||
|                 case 'n': | ||||
|                     b = '\n'; | ||||
|                     break; | ||||
|                 case 'r': | ||||
|                     b = '\r'; | ||||
|                     break; | ||||
|                 case 't': | ||||
|                     b = '\t'; | ||||
|                     break; | ||||
|                 case '"': | ||||
|                     b = '"'; | ||||
|                     break; | ||||
|                 case '\\': | ||||
|                     b = '\\'; | ||||
|                     break; | ||||
|                 case 'u': | ||||
|                     { | ||||
|                         /* Get codepoint and check for surrogate pair */ | ||||
|                         uint32_t codepoint; | ||||
|                         const char *err = decode_utf16_escape(cp + 1, &codepoint); | ||||
|                         if (err) return err; | ||||
|                         if (codepoint >= 0xDC00 && codepoint <= 0xDFFF) { | ||||
|                             return "unexpected utf-16 low surrogate"; | ||||
|                         } else if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { | ||||
|                             if (cp[5] != '\\') return "expected utf-16 low surrogate pair"; | ||||
|                             if (cp[6] != 'u') return "expected utf-16 low surrogate pair"; | ||||
|                             uint32_t lowsur; | ||||
|                             const char *err = decode_utf16_escape(cp + 7, &lowsur); | ||||
|                             if (err) return err; | ||||
|                             if (lowsur < 0xDC00 || lowsur > 0xDFFF) | ||||
|                                 return "expected utf-16 low surrogate pair"; | ||||
|                             codepoint = ((codepoint - 0xD800) << 10) + | ||||
|                                 (lowsur - 0xDC00) + 0x10000; | ||||
|                             cp += 11; | ||||
|                         } else { | ||||
|                             cp += 5; | ||||
|                         } | ||||
|                         /* Write codepoint */ | ||||
|                         if (codepoint <= 0x7F) { | ||||
|                             dst_buffer_push_u8(buffer, codepoint); | ||||
|                         } else if (codepoint <= 0x7FF) { | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >>  6) & 0x1F) | 0xC0); | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >>  0) & 0x3F) | 0x80); | ||||
|                         } else if (codepoint <= 0xFFFF) { | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >> 12) & 0x0F) | 0xE0); | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >>  6) & 0x3F) | 0x80); | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >>  0) & 0x3F) | 0x80); | ||||
|                         } else { | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >> 18) & 0x07) | 0xF0); | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >> 12) & 0x3F) | 0x80); | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >>  6) & 0x3F) | 0x80); | ||||
|                             dst_buffer_push_u8(buffer, ((codepoint >>  0) & 0x3F) | 0x80); | ||||
|                         } | ||||
|                     } | ||||
|                     continue; | ||||
|             } | ||||
|         } | ||||
|         dst_buffer_push_u8(buffer, b); | ||||
|         cp++; | ||||
|     } | ||||
|     *out = dst_stringv(buffer->data, buffer->count); | ||||
|     *p = cp + 1; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static const char *decode_one(const char **p, Dst *out, int depth) { | ||||
|  | ||||
|     /* Prevent stack overflow */ | ||||
|     if (depth > DST_RECURSION_GUARD) goto recurdepth; | ||||
|  | ||||
|     /* Skip leading whitepspace */ | ||||
|     skipwhite(p); | ||||
|  | ||||
|     /* Main switch */ | ||||
|     switch (**p) { | ||||
|         default: | ||||
|             goto badchar; | ||||
|         case '\0': | ||||
|             goto eos; | ||||
|         /* Numbers */ | ||||
|         case '-': case '0': case '1' : case '2': case '3' : case '4': | ||||
|         case '5': case '6': case '7' : case '8': case '9': | ||||
|             { | ||||
|                 errno = 0; | ||||
|                 char *end = NULL; | ||||
|                 double x = strtod(*p, &end); | ||||
|                 if (end == *p) goto badnum; | ||||
|                 *p = end; | ||||
|                 *out = dst_wrap_real(x); | ||||
|                 break; | ||||
|             } | ||||
|         /* false, null, true */ | ||||
|         case 'f': | ||||
|             { | ||||
|                 const char *cp = *p; | ||||
|                 if (cp[1] != 'a' || cp[2] != 'l' || cp[3] != 's' || cp[4] != 'e') | ||||
|                     goto badident; | ||||
|                 *out = dst_wrap_false(); | ||||
|                 *p = cp + 5; | ||||
|                 break; | ||||
|             } | ||||
|         case 'n': | ||||
|             { | ||||
|                 const char *cp = *p; | ||||
|  | ||||
|                 if (cp[1] != 'u' || cp[2] != 'l' || cp[3] != 'l') | ||||
|                     goto badident; | ||||
|                 *out = dst_wrap_nil(); | ||||
|                 *p = cp + 4; | ||||
|                 break; | ||||
|             } | ||||
|         case 't': | ||||
|             { | ||||
|                 const char *cp = *p; | ||||
|                 if (cp[1] != 'r' || cp[2] != 'u' || cp[3] != 'e') | ||||
|                     goto badident; | ||||
|                 *out = dst_wrap_true(); | ||||
|                 *p = cp + 4; | ||||
|                 break; | ||||
|             } | ||||
|         /* String */ | ||||
|         case '"': | ||||
|             { | ||||
|                 const char *cp = *p + 1; | ||||
|                 const char *start = cp; | ||||
|                 while (*cp >= 32 && *cp != '"' && *cp != '\\') | ||||
|                     cp++; | ||||
|                 /* Only use a buffer for strings with escapes, else just copy | ||||
|                  * memory from source */ | ||||
|                 if (*cp == '\\') { | ||||
|                     *p = *p + 1; | ||||
|                     const char *err = decode_string(p, out); | ||||
|                     if (err) return err; | ||||
|                     break; | ||||
|                 } | ||||
|                 if (*cp != '"') goto badchar; | ||||
|                 *p = cp + 1; | ||||
|                 *out = dst_stringv((const uint8_t *)start, cp - start); | ||||
|                 break; | ||||
|             } | ||||
|         /* Array */ | ||||
|         case '[': | ||||
|             { | ||||
|                 *p = *p + 1; | ||||
|                 DstArray *array = dst_array(0); | ||||
|                 const char *err; | ||||
|                 Dst subval; | ||||
|                 skipwhite(p); | ||||
|                 while (**p != ']') { | ||||
|                     err = decode_one(p, &subval, depth + 1); | ||||
|                     if (err) return err; | ||||
|                     dst_array_push(array, subval); | ||||
|                     skipwhite(p); | ||||
|                     if (**p == ']') break; | ||||
|                     if (**p != ',') goto wantcomma; | ||||
|                     *p = *p + 1; | ||||
|                 } | ||||
|                 *p = *p + 1; | ||||
|                 *out = dst_wrap_array(array); | ||||
|             } | ||||
|             break; | ||||
|         /* Object */ | ||||
|         case '{': | ||||
|             { | ||||
|                 *p = *p + 1; | ||||
|                 DstTable *table = dst_table(0); | ||||
|                 const char *err; | ||||
|                 Dst subkey, subval; | ||||
|                 skipwhite(p); | ||||
|                 while (**p != '}') { | ||||
|                     skipwhite(p); | ||||
|                     if (**p != '"') goto wantstring; | ||||
|                     err = decode_one(p, &subkey, depth + 1); | ||||
|                     if (err) return err; | ||||
|                     skipwhite(p); | ||||
|                     if (**p != ':') goto wantcolon; | ||||
|                     *p = *p + 1; | ||||
|                     err = decode_one(p, &subval, depth + 1); | ||||
|                     if (err) return err; | ||||
|                     dst_table_put(table, subkey, subval); | ||||
|                     skipwhite(p); | ||||
|                     if (**p == '}') break; | ||||
|                     if (**p != ',') goto wantcomma; | ||||
|                     *p = *p + 1; | ||||
|                 } | ||||
|                 *p = *p + 1; | ||||
|                 *out = dst_wrap_table(table); | ||||
|                 break; | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     /* Good return */ | ||||
|     return NULL; | ||||
|  | ||||
|     /* Errors */ | ||||
| recurdepth: | ||||
|     return "recured too deeply"; | ||||
| eos: | ||||
|     return "unexpected end of source"; | ||||
| badident: | ||||
|     return "bad identifier"; | ||||
| badnum: | ||||
|     return "bad number"; | ||||
| wantcomma: | ||||
|     return "expected comma"; | ||||
| wantcolon: | ||||
|     return "expected colon"; | ||||
| badchar: | ||||
|     return "unexpected character"; | ||||
| wantstring: | ||||
|     return "expected json string"; | ||||
| } | ||||
|  | ||||
| static int json_decode(DstArgs args) { | ||||
|     Dst ret; | ||||
|     DST_FIXARITY(args, 1); | ||||
|     const char *err; | ||||
|     const char *start; | ||||
|     const char *p; | ||||
|     if (dst_checktype(args.v[0], DST_BUFFER)) { | ||||
|         DstBuffer *buffer = dst_unwrap_buffer(args.v[0]); | ||||
|         /* Ensure 0 padded */ | ||||
|         dst_buffer_push_u8(buffer, 0); | ||||
|         start = p = (const char *)buffer->data; | ||||
|         err = decode_one(&p, &ret, 0); | ||||
|         buffer->count--; | ||||
|     } else { | ||||
|         const uint8_t *bytes; | ||||
|         int32_t len; | ||||
|         DST_ARG_BYTES(bytes, len, args, 0); | ||||
|         start = p = (const char *)bytes; | ||||
|         err = decode_one(&p, &ret, 0); | ||||
|     } | ||||
|     /* Check trailing values */ | ||||
|     if (!err) { | ||||
|         skipwhite(&p); | ||||
|         if (*p) | ||||
|             err = "unexpected extra token"; | ||||
|     } | ||||
|     if (err) { | ||||
|         DST_THROWV(args, dst_wrap_string(dst_formatc( | ||||
|                     "decode error at postion %d: %s", | ||||
|                     p - start, | ||||
|                     err))); | ||||
|     } | ||||
|     DST_RETURN(args, ret); | ||||
| } | ||||
|  | ||||
| /*****************/ | ||||
| /* JSON Encoding */ | ||||
| /*****************/ | ||||
|  | ||||
| typedef struct { | ||||
|     DstBuffer *buffer; | ||||
|     int32_t indent; | ||||
|     const uint8_t *tab; | ||||
|     const uint8_t *newline; | ||||
|     int32_t tablen; | ||||
|     int32_t newlinelen; | ||||
| } Encoder; | ||||
|  | ||||
| static const char *encode_newline(Encoder *e) { | ||||
|     if (dst_buffer_push_bytes(e->buffer, e->newline, e->newlinelen)) | ||||
|         return "buffer overflow"; | ||||
|     for (int32_t i = 0; i < e->indent; i++) | ||||
|         if (dst_buffer_push_bytes(e->buffer, e->tab, e->tablen)) | ||||
|             return "buffer overflow"; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static const char *encode_one(Encoder *e, Dst x, int depth) { | ||||
|     switch(dst_type(x)) { | ||||
|         default: | ||||
|             goto badtype; | ||||
|         case DST_NIL: | ||||
|             { | ||||
|                 if (dst_buffer_push_cstring(e->buffer, "null")) | ||||
|                     goto overflow; | ||||
|             } | ||||
|             break; | ||||
|         case DST_FALSE: | ||||
|             { | ||||
|                 if (dst_buffer_push_cstring(e->buffer, "false")) | ||||
|                     goto overflow; | ||||
|             } | ||||
|             break; | ||||
|         case DST_TRUE: | ||||
|             { | ||||
|                 if (dst_buffer_push_cstring(e->buffer, "true")) | ||||
|                     goto overflow; | ||||
|             } | ||||
|             break; | ||||
|         case DST_INTEGER: | ||||
|             { | ||||
|                 char cbuf[20]; | ||||
|                 sprintf(cbuf, "%d", dst_unwrap_integer(x)); | ||||
|                 if (dst_buffer_push_cstring(e->buffer, cbuf)) | ||||
|                     goto overflow; | ||||
|             } | ||||
|             break; | ||||
|         case DST_REAL: | ||||
|             { | ||||
|                 char cbuf[25]; | ||||
|                 sprintf(cbuf, "%.17g", dst_unwrap_real(x)); | ||||
|                 if (dst_buffer_push_cstring(e->buffer, cbuf)) | ||||
|                     goto overflow; | ||||
|             } | ||||
|             break; | ||||
|         case DST_STRING: | ||||
|         case DST_SYMBOL: | ||||
|         case DST_BUFFER: | ||||
|             { | ||||
|                 const uint8_t *bytes; | ||||
|                 const uint8_t *c; | ||||
|                 const uint8_t *end; | ||||
|                 int32_t len; | ||||
|                 dst_bytes_view(x, &bytes, &len); | ||||
|                 if (dst_buffer_push_u8(e->buffer, '"')) goto overflow; | ||||
|                 c = bytes; | ||||
|                 end = bytes + len; | ||||
|                 while (c < end) { | ||||
|  | ||||
|                     /* get codepoint */ | ||||
|                     uint32_t codepoint; | ||||
|                     if (*c < 0x80) { | ||||
|                         /* one byte */ | ||||
|                         codepoint = *c++; | ||||
|                     } else if (*c < 0xE0) { | ||||
|                         /* two bytes */ | ||||
|                         if (c + 2 > end) goto overflow; | ||||
|                         codepoint = ((c[0] & 0x1F) << 6) | | ||||
|                             (c[1] & 0x3F); | ||||
|                         c += 2; | ||||
|                     } else if (*c < 0xF0) { | ||||
|                         /* three bytes */ | ||||
|                         if (c + 3 > end) goto overflow; | ||||
|                         codepoint = ((c[0] & 0x0F) << 12) | | ||||
|                             ((c[1] & 0x3F) << 6) | | ||||
|                             (c[2] & 0x3F); | ||||
|                         c += 3; | ||||
|                     } else if (*c < 0xF8) { | ||||
|                         /* four bytes */ | ||||
|                         if (c + 4 > end) goto overflow; | ||||
|                         codepoint = ((c[0] & 0x07) << 18) | | ||||
|                             ((c[1] & 0x3F) << 12) | | ||||
|                             ((c[3] & 0x3F) << 6) | | ||||
|                             (c[3] & 0x3F); | ||||
|                         c += 4; | ||||
|                     } else { | ||||
|                         /* invalid */ | ||||
|                         goto invalidutf8; | ||||
|                     } | ||||
|  | ||||
|                     /* write codepoint */ | ||||
|                     if (codepoint > 0x1F && codepoint < 0x80) { | ||||
|                         /* Normal, no escape */ | ||||
|                         if (codepoint == '\\' || codepoint == '"') | ||||
|                             if (dst_buffer_push_u8(e->buffer, '\\')) | ||||
|                                 goto overflow; | ||||
|                         if (dst_buffer_push_u8(e->buffer, (uint8_t) codepoint)) | ||||
|                             goto overflow; | ||||
|                     } else if (codepoint < 0x10000) { | ||||
|                         /* One unicode escape */ | ||||
|                         uint8_t buf[6]; | ||||
|                         buf[0] = '\\'; | ||||
|                         buf[1] = 'u'; | ||||
|                         buf[2] = (codepoint >> 12) & 0xF; | ||||
|                         buf[3] = (codepoint >> 8) & 0xF; | ||||
|                         buf[4] = (codepoint >> 4) & 0xF; | ||||
|                         buf[5] = codepoint & 0xF; | ||||
|                         if (dst_buffer_push_bytes(e->buffer, buf, sizeof(buf))) | ||||
|                             goto overflow; | ||||
|                     } else { | ||||
|                         /* Two unicode escapes (surrogate pair) */ | ||||
|                         uint32_t hi, lo; | ||||
|                         uint8_t buf[12]; | ||||
|                         hi = ((codepoint - 0x10000) >> 10) + 0xD800; | ||||
|                         lo = ((codepoint - 0x10000) & 0x3FF) + 0xDC00; | ||||
|                         buf[0] = '\\'; | ||||
|                         buf[1] = 'u'; | ||||
|                         buf[2] = (hi >> 12) & 0xF; | ||||
|                         buf[3] = (hi >> 8) & 0xF; | ||||
|                         buf[4] = (hi >> 4) & 0xF; | ||||
|                         buf[5] = hi & 0xF; | ||||
|                         buf[6] = '\\'; | ||||
|                         buf[7] = 'u'; | ||||
|                         buf[8] = (lo >> 12) & 0xF; | ||||
|                         buf[9] = (lo >> 8) & 0xF; | ||||
|                         buf[10] = (lo >> 4) & 0xF; | ||||
|                         buf[11] = lo & 0xF; | ||||
|                         if (dst_buffer_push_bytes(e->buffer, buf, sizeof(buf))) | ||||
|                             goto overflow; | ||||
|                     } | ||||
|                 } | ||||
|                 if (dst_buffer_push_u8(e->buffer, '"')) goto overflow; | ||||
|             } | ||||
|             break; | ||||
|         case DST_TUPLE: | ||||
|         case DST_ARRAY: | ||||
|             { | ||||
|                 const char *err; | ||||
|                 const Dst *items; | ||||
|                 int32_t len; | ||||
|                 dst_indexed_view(x, &items, &len); | ||||
|                 if (dst_buffer_push_u8(e->buffer, '[')) goto overflow; | ||||
|                 e->indent++; | ||||
|                 if ((err = encode_newline(e))) return err; | ||||
|                 for (int32_t i = 0; i < len; i++) { | ||||
|                     if ((err = encode_newline(e))) return err; | ||||
|                     if ((err = encode_one(e, items[i], depth + 1))) | ||||
|                         return err; | ||||
|                     if (dst_buffer_push_u8(e->buffer, ',')) | ||||
|                         goto overflow; | ||||
|                 } | ||||
|                 e->indent--; | ||||
|                 if (e->buffer->data[e->buffer->count - 1] == ',') { | ||||
|                     e->buffer->count--; | ||||
|                     if ((err = encode_newline(e))) return err; | ||||
|                 } | ||||
|                 if (dst_buffer_push_u8(e->buffer, ']')) goto overflow; | ||||
|             } | ||||
|             break; | ||||
|         case DST_TABLE: | ||||
|         case DST_STRUCT: | ||||
|             { | ||||
|                 const char *err; | ||||
|                 const DstKV *kvs; | ||||
|                 int32_t count, capacity; | ||||
|                 dst_dictionary_view(x, &kvs, &count, &capacity); | ||||
|                 if (dst_buffer_push_u8(e->buffer, '{')) goto overflow; | ||||
|                 e->indent++; | ||||
|                 for (int32_t i = 0; i < capacity; i++) { | ||||
|                     if (dst_checktype(kvs[i].key, DST_NIL)) | ||||
|                         continue; | ||||
|                     if (!dst_checktype(kvs[i].key, DST_STRING)) | ||||
|                         return "only strings keys are allowed in objects"; | ||||
|                     if ((err = encode_newline(e))) return err; | ||||
|                     if ((err = encode_one(e, kvs[i].key, depth + 1))) | ||||
|                         return err; | ||||
|                     const char *sep = e->tablen ? ": " : ":"; | ||||
|                     if (dst_buffer_push_cstring(e->buffer, sep)) | ||||
|                         goto overflow; | ||||
|                     if ((err = encode_one(e, kvs[i].value, depth + 1))) | ||||
|                         return err; | ||||
|                     if (dst_buffer_push_u8(e->buffer, ',')) | ||||
|                         goto overflow; | ||||
|                 } | ||||
|                 e->indent--; | ||||
|                 if (e->buffer->data[e->buffer->count - 1] == ',') { | ||||
|                     e->buffer->count--; | ||||
|                     if ((err = encode_newline(e))) return err; | ||||
|                 } | ||||
|                 if (dst_buffer_push_u8(e->buffer, '}')) goto overflow; | ||||
|             } | ||||
|             break; | ||||
|     } | ||||
|     return NULL; | ||||
|  | ||||
|     /* Errors */ | ||||
| overflow: | ||||
|     return "buffer overflow"; | ||||
| badtype: | ||||
|     return "type not supported"; | ||||
| invalidutf8: | ||||
|     return "string contains invalid utf-8"; | ||||
| } | ||||
|  | ||||
| static int json_encode(DstArgs args) { | ||||
|     DST_MINARITY(args, 1); | ||||
|     DST_MAXARITY(args, 3); | ||||
|     Encoder e; | ||||
|     e.indent = 0; | ||||
|     e.buffer = dst_buffer(10); | ||||
|     e.tab = NULL; | ||||
|     e.newline = NULL; | ||||
|     e.tablen = 0; | ||||
|     e.newlinelen = 0; | ||||
|     if (args.n >= 2) { | ||||
|         DST_ARG_BYTES(e.tab, e.tablen, args, 1); | ||||
|         if (args.n >= 3) { | ||||
|             DST_ARG_BYTES(e.newline, e.newlinelen, args, 2); | ||||
|         } else { | ||||
|             e.newline = (const uint8_t *)"\n"; | ||||
|             e.newlinelen = 1; | ||||
|         } | ||||
|     } | ||||
|     const char *err = encode_one(&e, args.v[0], 0); | ||||
|     if (err) DST_THROW(args, err); | ||||
|     DST_RETURN_BUFFER(args, e.buffer); | ||||
| } | ||||
|  | ||||
| /****************/ | ||||
| /* Module Entry */ | ||||
| /****************/ | ||||
|  | ||||
| static const DstReg cfuns[] = { | ||||
|     {"encode", json_encode}, | ||||
|     {"decode", json_decode}, | ||||
|     {NULL, NULL} | ||||
| }; | ||||
|  | ||||
| DST_MODULE_ENTRY(DstArgs args) { | ||||
|     DstTable *env = dst_env_arg(args); | ||||
|     dst_env_cfuns(env, cfuns); | ||||
|     return 0; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose